了解如何使用PyTorch构建图神经网络

人工智能

635人已加入

描述

  学习有关图神经网络的所有知识,包括 GNN 是什么,不同类型的图神经网络,以及它们的用途。此外,了解如何使用 PyTorch 构建图神经网络。

  为适合中文阅读习惯,阅读更有代入感,原文翻译后有删改。转载请注明原文出处,并说明由我得学城翻译整理。

        Abid Ali Awan| 作者 

1. 什么是图?

图是一种包含节点和边的数据结构。一个节点可以是一个人、地方或物体,边定义了节点之间的关系。边可以是有向的,也可以是无向的,基于方向性依赖关系。

在下面的示例中,蓝色圆圈是节点,箭头是边。边的方向定义了两个节点之间的依赖关系。

编码器

让我们了解一下复杂的图数据集:爵士音乐家网络。它包含198个节点和2742条边。

爵士音乐家网络https://datarepository.wolframcloud.com/resources/Jazz-Musicians-Network

在下面的社区图中,不同颜色的节点代表爵士音乐家的各种社区,边连接着它们。存在一种协作网络,其中单个音乐家在社区内外都有关系。

编码器

爵士音乐家网络的社区图

图在处理具有关系和相互作用的复杂问题方面非常出色。它们在模式识别、社交网络分析、推荐系统和语义分析中得到应用。创建基于图的解决方案是一个全新的领域,为复杂且相互关联的数据集提供了丰富的见解。

2. 使用 NetworkX 创建图

在本节中,我们将学习使用NetworkX创建图。

下面的代码受到 Daniel Holmberg 在 Python 中的图神经网络博客的影响。

创建 networkx 的DiGraph对象“H”

添加包含不同标签、颜色和大小的节点

添加边以创建两个节点之间的关系。例如,“(0,1)”表示 0 对 1 有方向性依赖。我们将通过添加“(1,0)”来创建双向关系

以列表形式提取颜色和大小

使用 networkx 的draw函数绘制图

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()函数将数据结构从有向图转换为无向图。

# 转换为无向图
G = H.to_undirected()
nx.draw(G, with_labels=True, node_color=colors, node_size=sizes)

3. 为什么分析图很难?

基于图的数据结构存在一些缺点,数据科学家在开发基于图的解决方案之前必须了解这些缺点。

图存在于非欧几里得空间中。它不在 2D 或 3D 空间中存在,这使得解释数据变得更加困难。为了在 2D 空间中可视化结构,您必须使用各种降维工具。

图是动态的;它们没有固定的形式。可以存在两个在视觉上不同的图,但它们可能具有相似的邻接矩阵表示。这使得使用传统的统计工具来分析数据变得困难。

对于人类解读来说,图的规模和维度会增加图的复杂性。具有多个节点和数千条边的密集结构更难理解和提取洞察。

4. 什么是图神经网络(GNN)?

图神经网络是一种特殊类型的神经网络,能够处理图数据结构。它们受到卷积神经网络(CNNs)和图嵌入的很大影响。GNNs 用于预测节点、边和基于图的任务。

CNNs 用于图像分类。类似地,GNNs 应用于图结构(像素网格)以预测一个类。

循环神经网络用于文本分类。类似地,GNNs 应用于图结构,其中每个单词是句子中的一个节点。

GNNs 是在卷积神经网络由于图的任意大小和复杂结构而无法取得最佳结果时引入的。

编码器

图像由 Purvanshi Mehta 提供

输入图经过一系列神经网络。输入图结构被转换成图嵌入,允许我们保留关于节点、边和全局上下文的信息。

然后,节点 A 和 C 的特征向量通过神经网络层。它聚合这些特征并将它们传递到下一层。

4.1 图神经网络的类型

有几种类型的神经网络,它们大多数都有一些卷积神经网络的变体。在本节中,我们将学习最流行的 GNNs。

图卷积网络(GCNs, Graph Convolutional Networks)类似于传统的 CNNs。它通过检查相邻节点来学习特征。GNNs 聚合节点向量,将结果传递给稠密层,并使用激活函数应用非线性。简而言之,它包括图卷积、线性层和非学习激活函数。有两种主要类型的 GCNs:空间卷积网络和频谱卷积网络。

图自编码器网络(Graph Auto-Encoder Networks)使用编码器学习图表示,并尝试使用解码器重建输入图。编码器和解码器通过瓶颈层连接。它们通常用于链路预测,因为自编码器擅长处理类平衡问题。

循环图神经网络(RGNNs, Recurrent Graph Neural Networks)学习最佳扩散模式,它们可以处理单个节点具有多个关系的多关系图。这种类型的图神经网络使用正则化器来增强平滑性并消除过度参数化。RGNNs 使用更少的计算能力产生更好的结果。它们用于生成文本、机器翻译、语音识别、生成图像描述、视频标记和文本摘要。

门控图神经网络(GGNNs, Gated Graph Neural Networks)在执行具有长期依赖性的任务方面优于 RGNNs。门控图神经网络通过在长期依赖性上添加节点、边和时间门来改进循环图神经网络。类似于门控循环单元(GRUs),门用于在不同状态下记住和忘记信息。

4.2 图神经网络任务类型

下面,我们列举了一些图神经网络任务类型,并提供了示例:

图分类(Graph Classification:):用于将图分类为不同的类别。其应用包括社交网络分析和文本分类。

节点分类(Node Classification:):这个任务使用相邻节点的标签来预测图中缺失的节点标签。

链路预测(Link Prediction):预测图中具有不完整邻接矩阵的一对节点之间的链接。这通常用于社交网络。

社区检测(Community Detection):基于边的结构将节点划分为不同的群集。它类似地从边的权重、距离和图对象中学习。

图嵌入(Graph Embedding):将图映射到向量,保留有关节点、边和结构的相关信息。

图生成(Graph Generation:):从样本图分布中学习,以生成一个新的但相似的图结构。

编码器

图神经网络类型

4.3 图神经网络的缺点

使用 GNNs 存在一些缺点。了解这些缺点将帮助我们确定何时使用 GNN 以及如何优化我们的机器学习模型的性能。

大多数神经网络可以深度学习以获得更好的性能,而 GNNs 大多是浅层网络,主要有三层。这限制了我们在大型数据集上获得最先进性能的能力。

图结构不断变化,这使得在其上训练模型变得更加困难。

将模型部署到生产环境面临可扩展性问题,因为这些网络在计算上很昂贵。如果您有一个庞大且复杂的图结构,将难以在生产环境中扩展 GNNs。

5. 什么是图卷积网络(GCN)?

大多数 GNNs 都是图卷积网络,了解它们在进入节点分类教程之前很重要。

GCN 中的卷积与卷积神经网络中的卷积相同。它将神经元与权重(滤波器)相乘,以从数据特征中学习。

它在整个图像上充当滑动窗口,以从相邻单元中学习特征。该滤波器使用权重共享在图像识别系统中学习各种面部特征。

现在将相同的功能转移到图卷积网络中,其中模型从相邻节点中学习特征。GCN 和 CNN 之间的主要区别在于,GCN 被设计为在非欧几里得数据结构上工作,其中节点和边的顺序可能变化。

编码器

CNN vs GCN

有两种类型的 GCNs:

空间图卷积网络(Spatial Graph Convolutional Networks)使用空间特征从位于空间空间的图中学习。

频谱图卷积网络(Spectral Graph Convolutional Networks)使用图拉普拉斯矩阵的特征值分解进行节点间的信息传播。这些网络灵感来自信号与系统中的波动传播。

6. 图神经网络如何工作?使用 PyTorch 构建图神经网络

我们将构建和训练用于节点分类模型的谱图卷积。代码源可在文末获取,让您体验并运行您的第一个基于图的机器学习模型。

6.1 准备

我们将安装 Pytorch 软件包,因为 pytorch_geometric 是在其基础上构建的。

 

 

!pip install -q torch

 

 

然后,我们将使用 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

 

 

6.2 Planetoid Cora 数据集

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])

 

 

6.3 使用 GNN 进行节点分类

我们将创建一个包含两个 GCNConv 层、relu 激活和 0.5 的丢弃率的 GCN 模型结构。该模型包含 16 个隐藏通道。

GCN 层:

编码器

上述方程中的 W(ℓ+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)
  )

 

 

6.4 可视化未经训练的 GCN 网络

让我们使用 sklearn.manifold.TSNE 和 matplotlib.pyplot 来可视化未经训练的 GCN 网络的节点嵌入。它将绘制一个包含 7 个维度节点嵌入的 2D 散点图。

 

 

%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)

 

 

编码器

6.5 训练 GNN

我们将使用 Adam 优化器和交叉熵损失函数(Cross-Entropy Loss)对模型进行 100 轮训练。

在训练函数中,我们有:

清除梯度

执行一次前向传播

使用训练节点计算损失

计算梯度,并更新参数

在测试函数中,我们有:

预测节点类别

提取具有最高概率的类别标签

检查有多少个值被正确预测

创建准确率比,使用正确预测的总和除以节点的总数。

 

 

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

 

 

6.6 模型评估

我们将使用测试函数在未见过的数据集上评估模型,如您所见,我们在准确率上取得了相当不错的结果,为 81.5%。

 

 

test_acc = test()
print(f'Test Accuracy: {test_acc:.4f}')

 

 

输出:

 

 

>>> 测试准确率:0.8150

 

 

现在,我们将可视化经过训练的模型的输出嵌入以验证结果。

 

 

model.eval()
out = model(data.x, data.edge_index)
visualize(out, color=data.y)

 

 

编码器

正如我们所看到的,经过训练的模型为相同类别的节点产生了更好的聚类。

6.7 训练 GATConv 模型

在第二个例子中,我们将使用 GATConv 层替换 GCNConv。图注意力网络使用掩码的自注意力层来解决 GCNConv 的缺点并取得最先进的结果。

您还可以尝试其他 GNN 层,并尝试不同的优化、丢失率和隐藏通道数量,以获得更好的性能。

在下面的代码中,我们只是用具有 8 个注意力头的 GATConv 替换了 GCNConv,其中第一层有 8 个头,第二层有 1 个头。

我们还将设置:

dropout为 0.6

隐藏通道为 8

学习率为 0.005

我们修改了测试函数以找到特定掩码(验证、测试)的准确率。这将帮助我们在模型训练期间打印出验证和测试分数。我们还将验证和测试结果存储到后面的绘图线图中。

 

 

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 表现得更好。它需要进行超参数优化或更多轮次的训练才能取得最先进的结果。

6.8 模型评估

在评估部分,我们使用 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 层来提高模型性能。

GNN 常见问题

图神经网络(GNN)用于什么?

图神经网络直接应用于图数据集,您可以训练它们以预测节点、边缘和与图相关的任务。它用于图和节点分类、链路预测、图聚类和生成,以及图像和文本分类。

在图神经网络中,什么是图?

在图神经网络中,图是一种包含节点和节点之间连接(称为边)的数据结构。边可以是有向的或无向的。它具有动态形状和多维结构。例如,在社交媒体中,节点可以是您朋友群中的人,而边则是您与每个人之间的关系。

图神经网络有多强大?

在图像和节点分类方面,图神经网络优于典型的卷积神经网络(CNN)。许多图神经网络的变体在节点和图分类任务中取得了最先进的结果 - openreview.net。

神经网络是否使用图论?

是的,神经网络与设计用于处理非欧几里得数据的图论密切相关。其中一些神经网络本身就是图,或者输出图。

什么是图卷积网络?

图卷积网络类似于用于图数据集的卷积神经网络。它包括图卷积、线性层和非线性激活。GNN 通过图上的滤波器,检查可用于对数据中的节点进行分类的节点和边。

在深度学习中,什么是图?

图深度学习也被称为几何深度学习。它使用多个神经网络层以实现更好的性能。这是一个活跃的研究领域,科学家们正试图在不影响性能的情况下增加层数。

审核编辑:黄飞

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分