图神经网络(GNN)是一组在图领域工作的深度学习方法。 这些网络最近已应用于多个领域,包括: 组合优化、推荐系统、计算机视觉—仅举几例。 这些网络还可用于对大型系统进行建模,例如社交网络、蛋白质-蛋白质相互作用网络、知识图以及其他研究领域。 与图像等其他数据不同,图形数据在非欧几里得空间中工作。 因此,图分析的目标是节点分类、链接预测和聚类。
在本文中,我们将进一步探讨图神经网络(GNN)。
图(Graph)是包含节点和顶点的数据结构。 各个节点之间的关系由顶点定义。 如果在节点中指定了方向,则该图被称为有向图,否则,该图是无向图。
使用图表的一个很好的例子是对社交网络中不同人之间的联系进行建模。
图神经网络(GNN:Graph Neural Network)是一类特殊的神经网络,能够处理以图形形式表示的数据。 这些网络很大程度上受到卷积神经网络(CNN)和图嵌入的推动。 CNN 无法处理图数据,因为图中的节点不以任何顺序表示,而且两个节点之间的依赖信息由边表示。
让我们花一点时间看看如何使用 NetworkX处理图数据。 NetworkX 是一个可用于创建图的 Python 包。 以下是如何使用该包创建没有节点的空图:
import networkx as nx
G = nx.Graph()
然后,你可以使用?add_nodes
函数向图中添加一些节点:
G.add_nodes_from([2, 3])
接下来,使用?add_edges_from
函数向图中添加一些边:
edges = [(2,1),(2,2),(3,2),(4,3),(6,4),(7,5),(14,5)]
G.add_edges_from(edges)
可以使用 Matplotlib 可视化这个图。 这是通过调用?draw
函数并使用 Matpotlib 显示图形来完成的。
nx.draw(G, with_labels=True, font_weight='bold')
import matplotlib.pyplot as plt
plt.show()
图神经网络(GNN)的概念由 Franco Scarselli Bruna 等人于 2009 年首次提出。在他们名为“图神经网络模型”的论文中,他们提出了现有神经网络的扩展,用于处理以图结构表示的数据。 该模型可以处理非循环图、循环图、有向图和无向图。 GNN 的目标是学习封装每个节点邻域信息的状态嵌入。 该嵌入用于产生输出。 例如,输出可以是节点标签。
最初的 GNN 提案有一些限制:
也可以使用传统方法来分析图表。 这些方法通常是算法,包括:
这些方法的挑战是需要先验知识,因此它们不能用于图分类。
图神经网络有多种类型。 让我们看一下其中的几个。
图卷积网络 (GCN) 使用与普通卷积神经网络相同的卷积运算。 GCN 通过检查相邻节点来学习特征。 它们通常由图卷积、线性层和非线性激活组成。 GNN 的工作原理是聚合邻域中的向量,将结果传递到密集的神经网络层,最后应用非线性。
GNN 与 CNN 的不同之处在于,它们是为处理非欧几里德结构化数据而构建的。 GCN 主要有两种类型:
图自动编码器网络(GAE:Graph Auto-Encoder Networks)由编码器和解码器组成。 这两个网络通过瓶颈层连接起来。 编码通过使图特征通过卷积滤波器来获取图像特征。 解码器尝试重建输入。 众所周知,自动编码器模型可以处理链接预测问题中常见的极端类别不平衡问题。 因此,图自动编码器网络尝试学习图表示,然后使用解码器重新构建图。
图循环神经网络 (GRNN:Graph Recurrent Neural Network) 利用多关系图并使用基于图的正则化器来提高平滑度并减轻过度参数化。 由于邻域的确切大小并不总是已知,因此使用循环 GNN 层使网络更加灵活。 GRNN 可以学习适合数据的最佳扩散模式。 它还能够处理节点涉及多个关系的情况。 该网络的计算成本也很低,因为操作数量根据图边的数量线性缩放。
在具有长期依赖性的问题上,门控图神经网络(GGNN:Gated Graph Neural Network)比循环图神经网络表现更好。 长期依赖关系由节点和边缘门编码。 长期时间依赖性由时间门编码。 因此,门控图神经网络通过添加门控机制来改进循环图神经网络。 这些门负责不同状态下的信息记忆和遗忘。
现在让我们花点时间看看 GNN 可以做什么:
图神经网络是强大的网络。 然而,有一些与它们相关的已知问题:
PyTorch 可以与 DGL 结合构建用于节点预测的图神经网络。 深度图库 (DGL) 是一个 Python 包,可用于通过 PyTorch 和 TensorFlow 实现 GNN。 官方文档提供了有关如何入门的示例。
让我们看一个 PyTorch 示例,展示如何在?Cora 数据集上为半监督节点分类模型构建 GNN。第一步是导入包并加载数据:
import dgl
import torch
import torch.nn as nn
import torch.nn.functional as F
import dgl.data
dataset = dgl.data.CoraGraphDataset()
g = dataset[0]
下一步是定义图卷积网络,该网络将使用邻域信息计算节点表示。 这是使用?dgl.nn.GraphConv
完成的:
from dgl.nn import GraphConv
class GCN(nn.Module):
def __init__(self, in_feats, h_feats, num_classes):
super(GCN, self).__init__()
self.conv1 = GraphConv(in_feats, h_feats)
self.conv2 = GraphConv(h_feats, num_classes)
def forward(self, g, in_feat):
h = self.conv1(g, in_feat)
h = F.relu(h)
h = self.conv2(g, h)
return h
# Create the model with given dimensions
model = GCN(g.ndata['feat'].shape[1], 16, dataset.num_classes)
下一步是训练神经网络。 训练的方式与在 PyTorch 或 TensorFlow 中进行训练的方式类似:
def train(g, model):
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
best_val_acc = 0
best_test_acc = 0
features = g.ndata['feat']
labels = g.ndata['label']
train_mask = g.ndata['train_mask']
val_mask = g.ndata['val_mask']
test_mask = g.ndata['test_mask']
for e in range(100):
# Forward
logits = model(g, features)
# Compute prediction
pred = logits.argmax(1)
# Compute loss
# Note that you should only compute the losses of the nodes in the training set.
loss = F.cross_entropy(logits[train_mask], labels[train_mask])
# Compute accuracy on training/validation/test
train_acc = (pred[train_mask] == labels[train_mask]).float().mean()
val_acc = (pred[val_mask] == labels[val_mask]).float().mean()
test_acc = (pred[test_mask] == labels[test_mask]).float().mean()
# Save the best validation accuracy and the corresponding test accuracy.
if best_val_acc < val_acc:
best_val_acc = val_acc
best_test_acc = test_acc
# Backward
optimizer.zero_grad()
loss.backward()
optimizer.step()
if e % 5 == 0:
print('In epoch {}, loss: {:.3f}, val acc: {:.3f} (best {:.3f}), test acc: {:.3f} (best {:.3f})'.format(
e, loss, val_acc, best_val_acc, test_acc, best_test_acc))
model = GCN(g.ndata['feat'].shape[1], 16, dataset.num_classes)
train(g, model)
输出如下:
你也可以将DPL与 TensorFlow 结合使用。 这将要求你在环境中设置环境变量?DGLBACKEND
。 以下是如何在 Google Colab 上完成此操作:
!export DGLBACKEND tensorflow
这篇论文提出了基于TensorFlow和Keras的Keras图卷积神经网络Python包(kgcnn)。 它为图神经网络提供 Keras 层。 官方页面提供了大量如何使用该包的示例。 示例之一是如何使用 Cora 数据集使用 kgcnn 进行节点分类。 让我们看一下这个插图的一个片段。
第一步通常是加载所需的包:
from kgcnn.data.cora.cora import cora_graph
from kgcnn.literature.GCN import make_gcn
from kgcnn.utils.adj import precompute_adjacency_scaled, convert_scaled_adjacency_to_list, make_adjacency_undirected_logical_or
from kgcnn.utils.data import ragged_tensor_from_nested_numpy
from kgcnn.utils.learning import lr_lin_reduction
下一步是加载数据并将其转换为密集矩阵:
# Download and load Dataset
A_data, X_data, y_data = cora_graph()
# Make node features dense
nodes = X_data.todense()
下一步是预先计算缩放后的无向邻接矩阵,并将邻接矩阵映射到索引列表加上边权重。 之后,使用 NumPy 转换数组的形状:
# Precompute scaled and undirected (symmetric) adjacency matrix
A_scaled = precompute_adjacency_scaled(make_adjacency_undirected_logical_or(A_data))
# Use edge_indices and weights instead of adj_matrix
edge_index, edge_weight = convert_scaled_adjacency_to_list(A_scaled)
edge_weight = np.expand_dims(edge_weight, axis=-1)
接下来,one-hot 对标签进行编码:
# Change labels to one-hot-encoding
labels = np.expand_dims(y_data, axis=-1)
labels = np.array(labels == np.arange(70), dtype=np.float)
可以使用 make_gcn 函数定义模型。 该函数需要输入节点的形状、输入边的形状、深度等:
model = make_gcn(
input_node_shape=[None, 8710],
input_edge_shape=[None, 1],
# Output
output_embedd={"output_mode": 'node'},
output_mlp={"use_bias": [True, True, False], "units": [140, 70, 70], "activation": ['relu', 'relu', 'softmax']},
# model specs
depth=3,
gcn_args={"units": 140, "use_bias": True, "activation": "relu", "has_unconnected": True}
)
以下是该模型的摘要:
下一步是训练这个模型。 训练在 Google Colab 上进行 300 个 epoch 后结束:
# Training loop
trainlossall = []
testlossall = []
start = time.process_time()
for iepoch in range(0, epo, epostep):
hist = model.fit(xtrain, ytrain,
epochs=iepoch + epostep,
initial_epoch=iepoch,
batch_size=1,
callbacks=[cbks],
verbose=1,
sample_weight=train_mask # Important!!!
)
trainlossall.append(hist.history)
testlossall.append(model.evaluate(xtrain, ytrain, sample_weight=val_mask))
stop = time.process_time()
print("Print Time for taining: ", stop - start)
然后,可以通过使用 Matplotlib 绘制训练和测试损失来检查训练和测试损失:
plt.figure(figsize=(12,8))
plt.plot(np.arange(1, len(trainlossall) + 1), trainlossall, label='Training Loss', c='blue')
plt.plot(np.arange(epostep, epo + epostep, epostep), testlossall[:, 1], label='Test Loss', c='red')
plt.xlabel('Epochs')
plt.ylabel('Accurarcy')
plt.title('GCN')
plt.legend(loc='lower right', fontsize='x-large')
plt.savefig('gcn_loss.png')
plt.show()