背景 : 弄懂Graph Convolution Network的pytorch代码如何加载数据并且如何定义网络结构的。
代码地址:https://github.com/tkipf/pygcn
论文地址:https://arxiv.org/abs/1609.02907 Semi-Supervised Classification with Graph Convolutional Networks,ICLR 2017
目录
一、模型结构定义
1.1 调用位置
1.2 输入参数
1.3 GCN定义
1.4 网络结构(核心)
二、图卷积层Graph convolution
2.1 层初始化定义
2.2 初始化权重
2.3 前馈运算
三、加载数据
3.1 数据格式
content file
cites file
3.2 content file的读取
3.3 cites的读取
3.4 运算symmetric adjacency matrix
3.5 数据集分割
train.py之中,调用模型
# Model and optimizer
model = GCN(nfeat=features.shape[1],
nhid=args.hidden,
nclass=labels.max().item() + 1,
dropout=args.dropout)
optimizer = optim.Adam(model.parameters(),
lr=args.lr, weight_decay=args.weight_decay)
代码models.py之中,
class GCN(nn.Module):
def __init__(self, nfeat, nhid, nclass, dropout):
super(GCN, self).__init__()
self.gc1 = GraphConvolution(nfeat, nhid)
self.gc2 = GraphConvolution(nhid, nclass)
self.dropout = dropout
此部分,需要结合论文与代码一同理解其结构,此部分也是论文和代码关于网络结构的核心。需要详细查阅pytorch的函数才能弄懂,后续再来查。
结合论文,此公式为:
代码之中就是这样:
def forward(self, x, adj):
x = F.relu(self.gc1(x, adj))
x = F.dropout(x, self.dropout, training=self.training)
x = self.gc2(x, adj)
return F.log_softmax(x, dim=1)
输入feature,输出feature,权重,偏移。
依然存在一个问题,不太熟悉pytorch的架构及代码,后续一个一个查阅。
def __init__(self, in_features, out_features, bias=True):
super(GraphConvolution, self).__init__()
self.in_features = in_features
self.out_features = out_features
self.weight = Parameter(torch.FloatTensor(in_features, out_features))
if bias:
self.bias = Parameter(torch.FloatTensor(out_features))
else:
self.register_parameter('bias', None)
self.reset_parameters()
初始化为均匀分布。
def reset_parameters(self):
stdv = 1. / math.sqrt(self.weight.size(1))
self.weight.data.uniform_(-stdv, stdv)
if self.bias is not None:
self.bias.data.uniform_(-stdv, stdv)
input X与权重W相乘,然后adj矩阵与 他们的积稀疏乘。
def forward(self, input, adj):
support = torch.mm(input, self.weight)
output = torch.spmm(adj, support)
if self.bias is not None:
return output + self.bias
else:
return output
这是我们第一次面对图结构的数据,因此需要弄懂图结构的数据如何读出,如何进入网络之中的。关于数据集的数据的格式,我们前面有看过数据集的格式。
https://github.com/tkipf/pygcn/tree/master/data/cora
Graph Convolution Network图卷积网络PyTorch代码(一)概览与训练运行
每行包含下面几项:
+
第一列为paper ID,后面几列为每个单词出现与否,用0与1表示,最后一列为类别标签。
表示论文之间的引用关系(由此看来这个图是有向图)
前面为被引论文的ID,后面为引用前面的论文的ID
887 6215
887 64519
887 87363
887 976334
906 1103979
906 1105344
906 1114352
906 1136397
content file的每一行的格式为 :
feature为第二列到倒数第二列,labels为最后一列。
idx_features_labels = np.genfromtxt("{}{}.content".format(path, dataset),
dtype=np.dtype(str))
features = sp.csr_matrix(idx_features_labels[:, 1:-1], dtype=np.float32)
labels = encode_onehot(idx_features_labels[:, -1])
cites file的每一行格式为:
根据前面的contents与这里的cites创建图,算出edges矩阵与adj 矩阵。
# build graph
idx = np.array(idx_features_labels[:, 0], dtype=np.int32)
idx_map = {j: i for i, j in enumerate(idx)}
edges_unordered = np.genfromtxt("{}{}.cites".format(path, dataset),
dtype=np.int32)
edges = np.array(list(map(idx_map.get, edges_unordered.flatten())),
dtype=np.int32).reshape(edges_unordered.shape)
adj = sp.coo_matrix((np.ones(edges.shape[0]), (edges[:, 0], edges[:, 1])),
shape=(labels.shape[0], labels.shape[0]),
dtype=np.float32)
矩阵的定义如下:
运算为 symmetric adjacency matrix的格式,具体需要参阅原论文与代码搞懂此矩阵到底如何得来的。
论文之中对symmetric adjacency matrix的描述为:
# build symmetric adjacency matrix
adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj)
features = normalize(features)
adj = normalize(adj + sp.eye(adj.shape[0]))
分割为train,val,test三个集
最终数据加载为torch的格式并且分成三个数据集
idx_train = range(140)
idx_val = range(200, 500)
idx_test = range(500, 1500)
features = torch.FloatTensor(np.array(features.todense()))
labels = torch.LongTensor(np.where(labels)[1])
adj = sparse_mx_to_torch_sparse_tensor(adj)
idx_train = torch.LongTensor(idx_train)
idx_val = torch.LongTensor(idx_val)
idx_test = torch.LongTensor(idx_test)