跳至内容

图神经网络(GNN)全面入门

全面了解图神经网络:GNN 是什么、常见类型及其用途。另附使用 Pytorch 构建图神经网络的指南。
已更新 2026年10月6日  · 15分钟 阅读

使用 AI 探索

ChatGPTClaudePerplexity

GNN

什么是图(Graph)?

图是一种包含节点(nodes)和边(edges)的数据结构。节点可以表示人、地点或事物,边用于定义节点之间的关系。根据方向依赖关系,边可以是有向或无向的。

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

dependencies between two nodes

图片来源:作者

我们来了解一个复杂的图数据集:Jazz Musicians Network。它包含 198 个节点和 2742 条边。在下方的社区图中,节点的不同颜色代表不同的爵士乐手社区及其连接的边。这里存在一个协作网络,单个乐手在社区内部和外部都有关系。

Community Graph

社区图 | 数据来源:Jazz Musicians Network

图在处理包含关系和交互的复杂问题时表现出色,常用于模式识别、社交网络分析、推荐系统和语义分析。基于图的解决方案是一片独立领域,可对复杂且相互关联的数据集提供丰富洞见。

使用 NetworkX 构建图

本节将学习如何使用 NetworkX 创建图。

下面的代码参考了 Daniel Holmberg 关于 Python 图神经网络的博文。

  1. 创建 networkx 的 DiGraph 对象 “H”
  2. 添加带有不同标签、颜色和大小的节点
  3. 添加边以创建两个节点之间的关系。例如,“(0,1)” 表示 0 对 1 有方向依赖。通过添加 “(1,0)” 创建双向关系
  4. 以列表形式提取颜色和大小
  5. 使用 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)

undirectional graph

下一步,我们将使用 to_undirected() 函数把该数据结构从有向图转换为无向图。

#converting to undirected graph
G = H.to_undirected()
nx.draw(G, with_labels=True, node_color=colors, node_size=sizes)

为什么图难以分析?

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

  1. 图存在于非欧几里得空间中,并非天然处于 2D 或 3D 空间,这让数据更难解读。要在二维空间中可视化其结构,您需要使用各种降维工具。
  2. 图是动态的,没有固定形态。可能存在两个视觉上不同的图,但它们的邻接矩阵表示却相似。这使得我们难以用传统统计工具来分析数据。
  3. 规模和维度过大会增加图的复杂度,影响人的理解。包含大量节点和上千条边的稠密结构更难理解并从中提取洞见。

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

图神经网络是一类能够处理图数据结构的特殊神经网络,深受卷积神经网络(CNN)和图嵌入方法的影响。GNN 可用于节点预测、边预测以及各种基于图的任务。

  • CNN 用于图像分类;类似地,GNN 作用于图结构(像素网格),用于预测类别。
  • 循环神经网络用于文本分类;同理,GNN 可用于句子级的图结构,其中每个词都是一个节点。

当卷积神经网络因图的任意大小和复杂结构而难以取得最佳效果时,GNN 应运而生。

Neural network

图片来源:Purvanshi Mehta

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

随后,节点 A 和 C 的特征向量会传入神经网络层。该层对这些特征进行聚合并将其传递到下一层——neptune.ai。

阅读我们的深度学习教程,或参加Python 深度学习入门课程,进一步了解深度学习算法与应用。

图神经网络的类型

图神经网络有多种类型,其中大多数都与卷积神经网络有所变体。这里我们介绍几种最常见的 GNN。

  • 图卷积网络(GCN)与传统 CNN 类似。它通过查看相邻节点来学习特征。GNN 对节点向量进行聚合,将结果传入全连接层,并通过激活函数引入非线性。简而言之,它由图卷积、线性层和非线性激活函数构成。GCN 主要有两种类型:空间卷积网络和谱卷积网络。
  • 图自编码器网络使用编码器学习图的表示,并尝试用解码器重构输入图。编码器与解码器之间通过瓶颈层连接。由于自编码器擅长处理类别不平衡,它们常用于链接预测。
  • 循环图神经网络(RGNN)可以学习最佳扩散模式,能够处理单个节点具有多重关系的多关系图。这类网络使用正则项提升平滑性并消除过度参数化,运算开销更小且效果更好。应用包括文本生成、机器翻译、语音识别、图像描述生成、视频标签和文本摘要。
  • 门控图神经网络(GGNN)在处理长期依赖任务上优于 RGNN。它通过在长期依赖上引入节点、边和时间的门控机制来改进 RGNN。类似门控循环单元(GRU),这些门用于在不同状态下记忆或遗忘信息。

如果您对循环神经网络(RNN)感兴趣,欢迎查看我们的课程。课程将介绍多种 RNN 模型架构、Keras 框架及其应用。

图神经网络任务类型

下面列出一些常见的 GNN 任务类型及示例:

  • 图分类:将图划分到不同类别。应用包括社交网络分析和文本分类。
  • 节点分类:利用相邻节点的标签预测图中缺失的节点标签。
  • 链接预测:在邻接矩阵不完整的图中,预测一对节点之间的链接。常用于社交网络。
  • 社区发现:根据边的结构将节点划分为不同簇。它同样会从边权重、距离和图对象中学习。
  • 图嵌入:将图映射为向量,同时保留关于节点、边和结构的关键信息。
  • 图生成:从样本图的分布中学习,生成新的但相似的图结构。

Types of Graph Neural Networks

图片来源:作者

图神经网络的劣势

使用 GNN 也存在一些不足。理解这些问题有助于我们判断何时使用 GNN,以及如何优化模型性能。

  1. 多数神经网络可以加深以获得更好表现,而GNN 通常较浅,多为三层左右。这限制了其在大型数据集上达到最前沿性能。
  2. 图结构不断变化,使得模型更难训练。
  3. 在生产环境部署时会面临可扩展性问题,因为这些网络计算代价高。如果图结构庞大且复杂,要在生产中扩展 GNN 将十分困难。

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

大多数 GNN 都属于图卷积网络。在进入节点分类实操之前,先了解 GCN 十分重要。

GCN 中的卷积与卷积神经网络中的卷积相同,即用带权重(滤波器)的神经元与输入相乘,从数据特征中学习。

它相当于在整幅图像上滑动的窗口,从相邻单元中学习特征。滤波器通过权值共享来学习图像识别系统中的各种面部特征——参见 Towards Data Science。

将同样的思想迁移至图卷积网络,模型从相邻节点中学习特征。GCN 与 CNN 的主要区别在于:GCN 旨在处理非欧几里得数据结构,节点和边的顺序可以变化。

CNN vs GCN

CNN vs GCN | 图片来源

想进一步了解基础 CNN,请参阅我们的TensorFlow 卷积神经网络(CNN)教程。

GCN 分为两类:

  • 空间图卷积网络使用空间特征,从位于空间中的图中学习。
  • 谱图卷积网络利用图拉普拉斯矩阵的特征分解在节点间传播信息。这类网络的灵感来自信号与系统中的波动传播。

GNN 如何工作?用 Pytorch 构建图神经网络

我们将构建并训练谱图卷积用于节点分类模型。代码源在此 DataLab 工作簿中,您可以亲手运行第一个基于图的机器学习模型。

示例代码参考了 Pytorch Geometric 文档。

开始上手

我们先安装 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

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 个节点、10556 条边、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])

使用 GNN 进行节点分类

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

GCN 层:

GCN layer

上式中的 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)
  )

可视化未训练的 GCN 网络

我们将使用 sklearn.manifold.TSNE 和 matplotlib.pyplot 可视化未训练 GCN 的节点嵌入。它会将 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)

untrained model

训练 GNN

我们将使用 Adam 优化器和交叉熵损失函数训练 100 个 Epoch。

train 函数中:

  1. 清空梯度
  2. 执行一次前向传播
  3. 使用训练节点计算损失
  4. 计算梯度并更新参数

test 函数中:

  1. 预测节点类别
  2. 提取概率最高的类别标签
  3. 统计预测正确的数量
  4. 用正确预测数除以节点总数得到准确率
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

模型评估

现在我们用 test 函数在未见过的数据上评估模型。如您所见,我们获得了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)

可以看到,训练后的模型在同一类别的节点上形成了更好的聚类。

clustering of nodes

训练 GATConv 模型

第二部分我们将把 GCNConv 替换为 GATConv 层。图注意力网络使用带掩码的自注意力层,弥补 GCNConv 的不足并取得最前沿的结果。

您也可以尝试其他 GNN 层,并在优化器、dropout、隐藏通道数等方面进行实验,以获得更好性能。

在下面的代码中,我们仅将第一层的 GCNConv 替换为具有8 个注意力头的 GATConv,第二层为 1 个注意力头。

另外设置:

  • dropout 率为 0.6
  • 隐藏通道数为 8
  • 学习率为 0.005

我们还修改了 test 函数,以计算特定掩码(验证、测试)的准确率,便于在训练过程中打印验证和测试分数。我们也会保存验证与测试结果,以便后续绘制折线图。

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。要达到最前沿效果,还需要进行超参数优化或增加训练轮数。

模型评估

在评估部分,我们使用 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 个 Epoch 后,验证与测试准确率稳定在 0.8 左右,波动约 +/-0.02。

GATConv model

我们再来可视化 GATConv 模型的节点聚类。

model.eval()

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

可以看到,GATConv 层在同类别节点的聚类上也产生了类似结果。

clustering

我们可以通过添加第二个验证数据集来降低过拟合,并尝试 pytoch_geometric 中的多种 GCN 层来提升模型表现。

通过参加Python 深度学习技能路径课程,把深度学习技能写进您的简历。课程将介绍深度学习算法、Keras、Pytorch 和 TensorFlow 框架。

FAQs

图神经网络的用途是什么?

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

图神经网络中的图是什么?

图是一种由节点构成的数据结构,节点之间的连接称为边。边可以是有向或无向的。图具有动态形状和多维结构。例如,在社交媒体中,节点是您好友圈中的人,边则是您与他人之间的关系。

图神经网络有多强大?

在图像与节点分类任务中,图神经网络往往优于典型的卷积神经网络(CNN)。许多 GNN 变体在节点和图分类任务上都取得了最前沿的结果——openreview.net。

神经网络会用到图论吗?

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

什么是图卷积网络?

图卷积网络类似于卷积神经网络,但面向图数据集。它由图卷积、线性层和非线性激活组成。GNN 通过在图上滑动“滤波器”,查看节点和边,从而对数据中的节点进行分类。

深度学习中的图是什么?

图深度学习也称为几何深度学习。它通过叠加多层神经网络以获得更好表现。这是一个活跃研究领域,研究者们正尝试在不牺牲性能的前提下增加层数。

主题
Python

Python 课程

课程

Python 中级

4 小时
1.4M
通过使用 Matplotlib 创建可视化并用 pandas 操作 DataFrame,提升你的数据科学技能。
查看详情Right Arrow
开始课程
查看更多Right Arrow