
使用这些基本 REST API 最佳实践构建出色的 API
本文介绍了有关图神经网络的所有内容,包括 GNN 是什么、不同类型的图神经网络以及它们的用途。此外,还展示了如何使用 Pytorch 构建图神经网络。
图形是包含节点和边的数据结构类型。节点可以是人、地点或事物,边定义节点之间的关系。边可以基于方向依赖关系进行定向和无向。
在下面的示例中,蓝色圆圈是节点,箭头是边。边的方向定义了两个节点之间的依赖关系。
让我们来了解一下复杂的 Graph 数据集:Jazz Musicians Network。它包含 198 个节点和 2742 条边。在下面的社区图中,不同颜色的节点代表爵士音乐家的各种社区以及连接它们的边缘。有一个合作网络,一个音乐家在社区内外都有关系。
Jazz Musicians Network 的社区图
图形在处理关系和交互的复杂问题方面非常出色。它们用于模式识别、社交网络分析、推荐系统和语义分析。创建基于图形的解决方案是一个全新的领域,它提供了对复杂和相互关联的数据集的丰富见解。
在本节中,我们将学习如何使用 NetworkX 创建图形。
下面的代码受到 Daniel Holmberg 关于 Python 中的图神经网络的博客的影响。
import networkx as nx
H = nx.DiGraph()
#adding nodes
H.add_nodes_from([
(0, {"color": "blue", "size": 250}),
(1, {"color": "yellow", "size": 400}),
(2, {"color": "orange", "size": 150}),
(3, {"color": "red", "size": 600})
])
#adding edges
H.add_edges_from([
(0, 1),
(1, 2),
(1, 0),
(1, 3),
(2, 3),
(3,0)
])
node_colors = nx.get_node_attributes(H, "color").values()
colors = list(node_colors)
node_sizes = nx.get_node_attributes(H, "size").values()
sizes = list(node_sizes)
#Plotting Graph
nx.draw(H, with_labels=True, node_color=colors, node_size=sizes)
在下一步中,我们将使用 to_undirected() 函数将数据结构从定向图转换为无向图。
#converting to undirected graph
G = H.to_undirected()
nx.draw(G, with_labels=True, node_color=colors, node_size=sizes)
基于图的数据结构有缺点,数据科学家在开发基于图的解决方案之前必须了解这些缺点。
图神经网络是能够处理图数据结构的特殊类型的神经网络。它们受到卷积神经网络 (CNN) 和图嵌入的高度影响。GNN 用于预测节点、边和基于图的任务。
当卷积神经网络由于图的任意大小和复杂的结构而未能达到最佳结果时,引入了GNN。
图片由 Purvanshi Mehta 提供
输入图通过一系列神经网络传递。输入图结构被转换为图嵌入,使我们能够维护有关节点、边缘和全局上下文的信息。
然后,节点 A 和 C 的特征向量通过神经网络层。它聚合这些特征并将它们传递到下一层 – neptune.ai。
阅读我们的深度学习教程或参加我们的深度学习入门课程,了解有关深度学习算法和应用的更多信息。
有几种类型的神经网络,其中大多数都有一些卷积神经网络的变体。在本节中,我们将了解最流行的 GNN。
如果您有兴趣了解有关递归神经网络 (RNN) 的更多信息,请查看 DataCamp 的课程。它将向您介绍各种 RNN 模型架构、Keras 框架和 RNN 应用程序。
下面,我们通过示例概述了一些类型的 GNN 任务:
图片由作者提供
了解它们将有助于我们确定何时使用 GNNa 以及如何优化机器学习模型的性能。
大多数 GNN 都是图卷积网络,在进入节点分类教程之前了解它们很重要。
GCN 中的卷积与卷积神经网络中的卷积相同。它用权重(过滤器)将神经元相乘,以从数据特征中学习。
它充当整个图像的滑动窗口,以从相邻单元格中学习特征。该过滤器使用权重共享来学习图像识别系统中的各种面部特征 – 迈向数据科学。
现在将相同的功能转移到图卷积网络,其中模型从相邻节点学习特征。GCN 和 CNN 之间的主要区别在于,它是为处理节点和边的顺序可能变化的非欧几里得数据结构而开发的。
CNN 与 GCN |图片来源
通过阅读卷积神经网络 (CNN) 和 TensorFlow 教程,了解有关基本 CNN 的更多信息。
有两种类型的 GCN:
我们将为节点分类模型构建和训练 Spectral Graph Convolution。Workspace 上提供了代码源,供你体验和运行第一个基于图形的机器学习模型。
编码示例受 Pytorch 几何文档的影响。
开始
我们将安装 Pytorch 包,因为 pytorch_geometric 是建立在它之上的。
!pip install -q torch
然后我们将安装 torch-scatter 和 torch-sparse。之后,我们将从 GitHub 安装 pytorch_geometric 的最新版本。
%%capture
import os
import torch
os.environ['TORCH'] = torch.__version__
os.environ['PYTHONWARNINGS'] = "ignore"
!pip install torch-scatter -f https://data.pyg.org/whl/torch-${TORCH}.html
!pip install torch-sparse -f https://data.pyg.org/whl/torch-${TORCH}.html
!pip install git+https://github.com/pyg-team/pytorch_geometric.git
Planetoid 是来自 Cora、CiteSeer 和 PubMed 的引文网络数据集。节点是具有 1433 维词袋特征向量的文档,边缘是研究论文之间的引文链接。有 7 个类,我们将训练模型来预测缺失的标签。
我们将引入 Planetoid Cora 数据集,并对词袋输入要素进行行规范化。之后,我们将分析数据集和第一个图形对象。
from torch_geometric.datasets import Planetoid
from torch_geometric.transforms import NormalizeFeatures
dataset = Planetoid(root='data/Planetoid', name='Cora', transform=NormalizeFeatures())
print(f'Dataset: {dataset}:')
print('======================')
print(f'Number of graphs: {len(dataset)}')
print(f'Number of features: {dataset.num_features}')
print(f'Number of classes: {dataset.num_classes}')
data = dataset[0] # Get the first graph object.
print(data)
Cora 数据集有 2708 个节点、10,556 条边、1433 个要素和 7 个类。第一个对象有 2708 个训练掩码、验证掩码和测试掩码。我们将使用这些掩码来训练和评估模型。
Dataset: Cora():
======================
Number of graphs: 1
Number of features: 1433
Number of classes: 7
Data(x=[2708, 1433], edge_index=[2, 10556], y=[2708], train_mask=[2708], val_mask=[2708], test_mask=[2708])
我们将创建一个 GCN 模型结构,其中包含两个 GCNConv 层、relu 激活和 0.5 的辍学率。该模型由 16 个隐藏通道组成。
GCN层:
W(l+1) 是上式中的可移动权重矩阵,Cw,v 对每条边都具有固定的归一化系数。
from torch_geometric.nn import GCNConv
import torch.nn.functional as F
class GCN(torch.nn.Module):
def __init__(self, hidden_channels):
super().__init__()
torch.manual_seed(1234567)
self.conv1 = GCNConv(dataset.num_features, hidden_channels)
self.conv2 = GCNConv(hidden_channels, dataset.num_classes)
def forward(self, x, edge_index):
x = self.conv1(x, edge_index)
x = x.relu()
x = F.dropout(x, p=0.5, training=self.training)
x = self.conv2(x, edge_index)
return x
model = GCN(hidden_channels=16)
print(model)
>>> GCN(
(conv1): GCNConv(1433, 16)
(conv2): GCNConv(16, 7)
)
让我们使用 sklearn.manifold.TSNE 和 matplotlib.pyplot 可视化未经训练的 GCN 网络的节点嵌入。它将绘制一个嵌入 2D 散点图的 7 维节点。
%matplotlib inline
import matplotlib.pyplot as plt
from sklearn.manifold import TSNE
def visualize(h, color):
z = TSNE(n_components=2).fit_transform(h.detach().cpu().numpy())
plt.figure(figsize=(10,10))
plt.xticks([])
plt.yticks([])
plt.scatter(z[:, 0], z[:, 1], s=70, c=color, cmap="Set2")
plt.show()
我们将评估模型,然后将训练数据添加到未训练的模型中,以可视化各种节点和类别。
model.eval()
out = model(data.x, data.edge_index)
visualize(out, color=data.y)
我们将使用 Adam 优化和交叉熵损失函数在 100 个 Epoch 上训练我们的模型。
在 train 函数中,我们有:
在测试函数中,我们有:
model = GCN(hidden_channels=16)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)
criterion = torch.nn.CrossEntropyLoss()
def train():
model.train()
optimizer.zero_grad()
out = model(data.x, data.edge_index)
loss = criterion(out[data.train_mask], data.y[data.train_mask])
loss.backward()
optimizer.step()
return loss
def test():
model.eval()
out = model(data.x, data.edge_index)
pred = out.argmax(dim=1)
test_correct = pred[data.test_mask] == data.y[data.test_mask]
test_acc = int(test_correct.sum()) / int(data.test_mask.sum())
return test_acc
for epoch in range(1, 101):
loss = train()
print(f'Epoch: {epoch:03d}, Loss: {loss:.4f}')
GAT(
(conv1): GATConv(1433, 8, heads=8)
(conv2): GATConv(64, 7, heads=8)
)
.. .. .. ..
.. .. .. ..
Epoch: 098, Loss: 0.5989
Epoch: 099, Loss: 0.6021
Epoch: 100, Loss: 0.5799
现在,我们将使用测试函数在看不见的数据集上评估模型,正如您所看到的,我们以 81.5% 的准确率获得了相当不错的结果。
test_acc = test()
print(f'Test Accuracy: {test_acc:.4f}')
>>> Test Accuracy: 0.8150
现在,我们将可视化经过训练的模型的输出嵌入以验证结果。
model.eval()
out = model(data.x, data.edge_index)
visualize(out, color=data.y)
正如我们所看到的,经过训练的模型为同一类别的节点生成了更好的聚类。
在第二种情况下,我们将用 GATConv 层替换 GCNConv。图注意力网络使用屏蔽的自我注意力层来解决 GCNConv 的缺点并实现最先进的结果。
您还可以尝试其他 GNN 层,并尝试优化、丢弃和一些隐藏通道,以实现更好的性能。
在下面的代码中,我们刚刚将 GCNConv 替换为 GATConv,第一层有 8 个注意力头,第二层有 1 个注意力头。
我们还将设置:
我们修改了测试函数,以查找特定掩码的准确性(有效,测试)。它将帮助我们在模型训练期间打印出验证和测试分数。稍后,我们还会将验证和测试结果存储到绘图折线图中。
from torch_geometric.nn import GATConv
class GAT(torch.nn.Module):
def __init__(self, hidden_channels, heads):
super().__init__()
torch.manual_seed(1234567)
self.conv1 = GATConv(dataset.num_features, hidden_channels,heads)
self.conv2 = GATConv(heads*hidden_channels, dataset.num_classes,heads)
def forward(self, x, edge_index):
x = F.dropout(x, p=0.6, training=self.training)
x = self.conv1(x, edge_index)
x = F.elu(x)
x = F.dropout(x, p=0.6, training=self.training)
x = self.conv2(x, edge_index)
return x
model = GAT(hidden_channels=8, heads=8)
print(model)
optimizer = torch.optim.Adam(model.parameters(), lr=0.005, weight_decay=5e-4)
criterion = torch.nn.CrossEntropyLoss()
def train():
model.train()
optimizer.zero_grad()
out = model(data.x, data.edge_index)
loss = criterion(out[data.train_mask], data.y[data.train_mask])
loss.backward()
optimizer.step()
return loss
def test(mask):
model.eval()
out = model(data.x, data.edge_index)
pred = out.argmax(dim=1)
correct = pred[mask] == data.y[mask]
acc = int(correct.sum()) / int(mask.sum())
return acc
val_acc_all = []
test_acc_all = []
for epoch in range(1, 101):
loss = train()
val_acc = test(data.val_mask)
test_acc = test(data.test_mask)
val_acc_all.append(val_acc)
test_acc_all.append(test_acc)
print(f'Epoch: {epoch:03d}, Loss: {loss:.4f}, Val: {val_acc:.4f}, Test: {test_acc:.4f}')
.. .. .. ..
.. .. .. ..
Epoch: 098, Loss: 1.1283, Val: 0.7960, Test: 0.8030
Epoch: 099, Loss: 1.1352, Val: 0.7940, Test: 0.8050
Epoch: 100, Loss: 1.1053, Val: 0.7960, Test: 0.8040
正如我们所观察到的,我们的模型并不比 GCNConv 表现得更好。它需要超参数优化或更多的 Epoch 才能获得最先进的结果。
在评估部分,我们使用 matplotlib.pyplot 的折线图可视化验证和测试分数。
import numpy as np
plt.figure(figsize=(12,8))
plt.plot(np.arange(1, len(val_acc_all) + 1), val_acc_all, label='Validation accuracy', c='blue')
plt.plot(np.arange(1, len(test_acc_all) + 1), test_acc_all, label='Testing accuracy', c='red')
plt.xlabel('Epochs')
plt.ylabel('Accurarcy')
plt.title('GATConv')
plt.legend(loc='lower right', fontsize='x-large')
plt.savefig('gat_loss.png')
plt.show()
在60个周期后,验证和测试精度达到了0.8+/-0.02的稳定值。
同样,让我们可视化 GATConv 模型的节点聚类。
model.eval()
out = model(data.x, data.edge_index)
visualize(out, color=data.y)
正如我们所看到的,GATConv 层在同一类别的节点上聚类时产生了相同的结果。
我们可以通过添加验证数据集来减少过拟合,并通过试验pytoch_geometric中的各种 GCN 层来提高模型性能。
本文章转载微信公众号@Python人工智能前沿