如果你正在处理社交网络推荐、药物分子预测、交通流量分析,或是任何涉及“关系”的数据,传统的深度学习模型(如CNN、RNN)可能会让你感到力不从心。因为这些模型是为网格化(如图像)或序列化(如文本)数据设计的,它们天生不擅长处理图(Graph)这种非欧几里得结构的数据。这正是图神经网络(Graph Neural Networks, GNNs)在过去几年迅速崛起,并成为AI领域最热门方向之一的核心原因。
很多人以为GNN只是一个“给社交网络做推荐的工具”,或者“一个复杂难懂的学术概念”。这其实是一个巨大的误解。GNN的本质,是为我们提供了一套全新的“计算范式”,让AI能够像理解像素和单词一样,去理解和推理实体之间的“连接”与“相互作用”。从推荐系统中挖掘“你朋友喜欢的你也可能喜欢”,到生物信息学中预测蛋白质的3D结构与功能,再到金融风控中识别欺诈团伙,其底层逻辑都是对“图”的建模与学习。
本文不会停留在概念科普。我们将从一个开发者最关心的视角出发:当你拿到一个图结构的数据集时,如何一步步将其转化为GNN模型可以处理的格式,并完成一个完整的、可运行的分类或预测任务。我们将拆解GNN的核心思想(消息传递),并用PyTorch Geometric(PyG)这个目前最流行的图神经网络库,带你跑通一个完整的实战流程。你会看到,GNN的实现并没有想象中那么复杂,关键在于理解其“图视角”的思维方式。
读完本文,你将能清晰地回答以下问题:
- GNN解决了传统神经网络解决不了的什么问题?(为什么重要)
- GNN的核心计算单元“消息传递”是如何工作的?(核心原理)
- 如何用PyTorch Geometric快速搭建一个图卷积网络(GCN)?(实操步骤)
- 在节点分类、链接预测等经典任务上,完整的代码流程是怎样的?(代码实现)
- 训练GNN模型有哪些独特的技巧和常见坑点?(最佳实践与排错)
1. 从“网格”到“关系”:GNN到底解决了什么根本问题?
要理解GNN的价值,必须从数据的本质说起。我们熟悉的数据形态主要有两种:
- 欧几里得数据:具有规则的空间结构。例如图像是规则的像素网格(2D网格),文本是词序列(1D网格)。CNN和RNN正是利用了这种规则的局部性(平移不变性、序列依赖性)才如此有效。
- 非欧几里得数据:没有固定的网格结构,每个节点的邻居数量可能不同。例如社交网络中,每个人的朋友数量不等;分子图中,每个原子连接的化学键数量也不同。
传统神经网络(如MLP)处理图数据时,通常会将每个节点视为独立的样本,完全忽略它们之间的连接关系。这就像试图通过分析孤立的单词来理解一篇文章的语义,丢失了最重要的上下文信息。
GNN的核心突破在于,它定义了一种在图上进行“信息传播”的机制。每个节点通过聚合其邻居节点的信息来更新自身的特征表示。经过几轮这样的聚合(或称“消息传递”),一个节点的特征就包含了其多跳邻居的信息,从而编码了该节点在图中的局部结构信息。
这带来了几个关键优势:
- 对不规则结构的天然适配:无论一个节点有多少邻居,GNN的聚合操作(如求和、均值、最大值)都能处理。
- 关系推理能力:模型能够显式地利用连接关系进行预测,例如在推荐中,模型不仅知道用户A和物品B,还知道用户A的朋友C也喜欢B,这会显著增强推荐的置信度。
- 归纳与泛化:训练好的GNN模型可以应用到从未见过的、结构不同的新图上(归纳学习),这对于新用户的冷启动、新分子的性质预测至关重要。
简单来说,GNN让AI学会了“看关系”,而不仅仅是“看个体”。这是处理社交、生物、化学、知识图谱、交通等复杂系统数据的范式转变。
2. 核心原理拆解:消息传递是GNN的“发动机”
几乎所有现代GNN模型(GCN, GAT, GraphSAGE等)都可以统一到“消息传递”框架下。理解了这个框架,就理解了GNN的七寸。
消息传递包含三个核心步骤,在每一层(每一轮)中重复进行:
- 消息生成(Message):对于图中的每条边
(u, v),从源节点u生成一个发送给目标节点v的消息。通常,这个消息是源节点特征h_u的变换。 - 消息聚合(Aggregate):对于每个目标节点
v,聚合所有来自其邻居节点u ∈ N(v)的消息。常见的聚合函数有求和(sum)、均值(mean)、最大值(max)。 - 节点更新(Update):结合目标节点
v自身上一层的特征h_v和聚合后的邻居消息,生成节点v新的特征表示h_v'。
用公式可以简洁地表示为:h_v^{(l+1)} = UPDATE^{(l)}(h_v^{(l)}, AGGREGATE^{(l)}({MESSAGE^{(l)}(h_u^{(l)}, h_v^{(l)}, e_{uv}) for u in N(v)}))其中l表示第l层。
一个生动的类比:想象在一个社交网络中,每个人(节点)最初只知道自己的兴趣(节点特征)。每一轮交流(一层GNN)中,每个人都会听取所有朋友(邻居)的兴趣(消息),然后综合朋友的意见更新自己对世界的认知(更新节点特征)。经过几轮交流后,每个人的认知都融合了其社交圈的多层信息。
不同的GNN模型主要在这三个步骤的函数设计上有所不同:
- GCN (Graph Convolutional Network):可以看作一种特殊的、归一化的均值聚合。它对邻居特征进行加权平均,权重由图的度(邻居数)决定。
- GAT (Graph Attention Network):在聚合时,不是平等对待所有邻居,而是通过一个注意力机制为每个邻居分配不同的权重,实现“选择性倾听”。
- GraphSAGE:强调归纳学习,其聚合函数(如LSTM, Pooling)是可学习的,并且支持对邻居进行采样以处理大规模图。
3. 环境准备:PyTorch Geometric (PyG) 安装指南
理论之后,我们进入实战。PyTorch Geometric (PyG) 是基于PyTorch的图神经网络库,封装了绝大多数常见的GNN层、数据集和数据处理管道,极大降低了入门门槛。
前置条件:
- Python: 3.8 或以上版本(推荐3.9+)。
- PyTorch: 请根据你的CUDA版本(或CPU)从 官网 获取安装命令。例如,对于CUDA 11.8:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
安装PyTorch Geometric: PyG的安装稍微复杂,因为它需要与PyTorch和CUDA版本精确匹配。最可靠的方式是使用预编译的wheel。
检查你的PyTorch和CUDA版本:
python -c "import torch; print(torch.__version__); print(torch.version.cuda)"假设输出为
2.1.0和11.8。根据 官方安装页面 查找对应wheel。对于
torch==2.1.0和cuda==11.8,安装命令可能如下:pip install torch-scatter torch-sparse torch-cluster torch-spline-conv -f https://data.pyg.org/whl/torch-2.1.0+cu118.html pip install torch-geometric注意:
torch-scatter等库的版本必须匹配。如果上述链接失效,请务必访问官方文档获取最新链接。验证安装:
import torch import torch_geometric print(torch_geometric.__version__) # 应能正常输出版本号
可选但推荐的库:
pip install numpy matplotlib networkx scikit-learnnetworkx: 用于图的可视化。scikit-learn: 用于评估指标。
4. PyG数据基础:如何表示一个图?
在PyG中,一个图由torch_geometric.data.Data对象表示。它封装了图的所有关键组件。
import torch from torch_geometric.data import Data # 1. 节点特征矩阵 [num_nodes, num_node_features] # 假设我们有4个节点,每个节点有2个特征 x = torch.tensor([[1, 2], [3, 4], [5, 6], [7, 8]], dtype=torch.float) # 2. 边索引(图连接关系)[2, num_edges] # 定义边:(0->1), (1->2), (2->3), (3->0), (1->3) # 这是有向边的表示,对于无向图,需要添加反向边 (1->0), (2->1)... edge_index = torch.tensor([[0, 1, 2, 3, 1], [1, 2, 3, 0, 3]], dtype=torch.long) # edge_index 的 shape 是 [2, 5],表示有5条边。 # 第一行是源节点索引,第二行是目标节点索引。 # 3. (可选)边特征 [num_edges, num_edge_features] edge_attr = torch.tensor([[0.1], [0.2], [0.3], [0.4], [0.5]], dtype=torch.float) # 4. (可选)节点标签 [num_nodes] # 假设我们做节点分类,4个节点分别属于3个类别 y = torch.tensor([0, 1, 2, 0], dtype=torch.long) # 5. 创建Data对象 data = Data(x=x, edge_index=edge_index, edge_attr=edge_attr, y=y) print(data) # 输出: Data(x=[4, 2], edge_index=[2, 5], edge_attr=[5, 1], y=[4]) print(f'Number of nodes: {data.num_nodes}') # 4 print(f'Number of edges: {data.num_edges}') # 5 print(f'Has isolated nodes: {data.has_isolated_nodes()}') # False print(f'Has self-loops: {data.has_self_loops()}') # False关键理解:
edge_index是GNN高效计算的关键。这种稀疏的邻接表表示,使得在消息传递时能快速定位每个节点的邻居。- PyG内置了许多标准数据集(如Cora, Citeseer, PubMed),可以方便地加载和实验。
5. 实战演练:用GCN实现Cora数据集节点分类
Cora是一个经典的引文网络数据集,包含2708篇机器学习论文(节点),每篇论文由一个1433维的词袋特征向量表示。边表示论文间的引用关系。任务是将每篇论文分类到7个类别之一。
5.1 数据加载与探索
from torch_geometric.datasets import Planetoid import torch_geometric.transforms as T # 加载Cora数据集 dataset = Planetoid(root='./data/Cora', name='Cora', transform=T.NormalizeFeatures()) # transform=T.NormalizeFeatures() 对节点特征进行归一化,通常能提升性能 data = dataset[0] # Cora只有一个图 print(f'Dataset: {dataset}') print(f'Number of graphs: {len(dataset)}') print(f'Number of features: {dataset.num_features}') print(f'Number of classes: {dataset.num_classes}') print(f'\nData object:') print('=============') print(data) print(f'\n图结构信息:') print(f'Number of nodes: {data.num_nodes}') print(f'Number of edges: {data.num_edges}') print(f'Average node degree: {data.num_edges / data.num_nodes:.2f}') print(f'Has isolated nodes: {data.has_isolated_nodes()}') print(f'Has self-loops: {data.has_self_loops()}') print(f'Is undirected: {data.is_undirected()}') # Cora是无向图 # 查看数据集内置的训练、验证、测试掩码 print(f'\nTrain mask: {data.train_mask.sum().item()} nodes') print(f'Val mask: {data.val_mask.sum().item()} nodes') print(f'Test mask: {data.test_mask.sum().item()} nodes')5.2 构建GCN模型
我们将实现一个两层的GCN。第一层将1433维特征映射到16维隐藏层,第二层将16维隐藏层映射到7维(类别数)。
import torch.nn as nn import torch.nn.functional as F from torch_geometric.nn import GCNConv class GCN(nn.Module): def __init__(self, in_channels, hidden_channels, out_channels): super().__init__() # 第一层图卷积 self.conv1 = GCNConv(in_channels, hidden_channels) # 第二层图卷积 self.conv2 = GCNConv(hidden_channels, out_channels) # 可选的Dropout层,防止过拟合 self.dropout = nn.Dropout(p=0.5) def forward(self, data): x, edge_index = data.x, data.edge_index # 第一层:特征变换 + ReLU激活 + Dropout x = self.conv1(x, edge_index) x = F.relu(x) x = self.dropout(x) # 第二层:输出层(通常不加激活函数,因为后面接CrossEntropyLoss) x = self.conv2(x, edge_index) return F.log_softmax(x, dim=1) # 输出对数概率,更数值稳定 # 初始化模型 model = GCN(in_channels=dataset.num_features, # 1433 hidden_channels=16, out_channels=dataset.num_classes) # 7 print(model)5.3 训练与验证循环
import torch.optim as optim from sklearn.metrics import accuracy_score device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = model.to(device) data = data.to(device) # 将整个图数据(包括特征、边、掩码)移到设备上 optimizer = optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4) criterion = nn.CrossEntropyLoss() # 内部会做log_softmax,但我们的forward已经做了log_softmax # 因此,在计算loss时,需要使用负对数似然损失(NLLLoss),或者将模型forward中的log_softmax去掉。 # 这里我们选择修改模型,使用更标准的写法:forward返回最后一层卷积结果,在计算loss时使用CrossEntropyLoss。 # 修改模型forward方法,移除最后的log_softmax class GCN(nn.Module): def __init__(self, in_channels, hidden_channels, out_channels): super().__init__() self.conv1 = GCNConv(in_channels, hidden_channels) self.conv2 = GCNConv(hidden_channels, out_channels) self.dropout = nn.Dropout(p=0.5) def forward(self, data): x, edge_index = data.x, data.edge_index x = self.conv1(x, edge_index) x = F.relu(x) x = self.dropout(x) x = self.conv2(x, edge_index) # 直接输出,不做softmax return x model = GCN(dataset.num_features, 16, dataset.num_classes).to(device) optimizer = optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4) criterion = nn.CrossEntropyLoss() def train(): model.train() optimizer.zero_grad() out = model(data) # 前向传播,得到所有节点的输出 [num_nodes, num_classes] # 只计算训练集节点的损失 loss = criterion(out[data.train_mask], data.y[data.train_mask]) loss.backward() optimizer.step() return loss.item() @torch.no_grad() def test(): model.eval() out = model(data) # 分别计算训练、验证、测试集上的准确率 pred = out.argmax(dim=1) accs = [] for mask in [data.train_mask, data.val_mask, data.test_mask]: acc = accuracy_score(data.y[mask].cpu().numpy(), pred[mask].cpu().numpy()) accs.append(acc) return accs # 训练循环 for epoch in range(1, 201): loss = train() if epoch % 50 == 0: train_acc, val_acc, test_acc = test() print(f'Epoch: {epoch:03d}, Loss: {loss:.4f}, ' f'Train Acc: {train_acc:.4f}, Val Acc: {val_acc:.4f}, Test Acc: {test_acc:.4f}')5.4 运行结果分析
运行上述代码,你可能会得到类似以下的输出(具体数值会有波动):
Epoch: 050, Loss: 0.3012, Train Acc: 0.9571, Val Acc: 0.7520, Test Acc: 0.7580 Epoch: 100, Loss: 0.1854, Train Acc: 0.9857, Val Acc: 0.7880, Test Acc: 0.7920 Epoch: 150, Loss: 0.1321, Train Acc: 0.9929, Val Acc: 0.8000, Test Acc: 0.8050 Epoch: 200, Loss: 0.0987, Train Acc: 1.0000, Val Acc: 0.8100, Test Acc: 0.8150结果解读:
- 训练损失持续下降,训练准确率接近100%:说明模型有能力拟合训练数据。
- 验证集和测试集准确率在81%左右:这是一个在Cora数据集上GCN模型的典型性能。表明模型学到了泛化能力,没有严重过拟合。
- 与MLP对比:如果你用一个简单的多层感知机(MLP,忽略图结构)在相同数据上训练,测试准确率通常只有55%-60%。这超过20个百分点的提升,直观地证明了利用图结构信息的巨大价值。
6. 进阶任务:链接预测实战
节点分类是GNN的经典任务。另一个同样重要的任务是链接预测,即预测图中哪些节点之间可能存在边。这在社交网络好友推荐、知识图谱补全中应用广泛。
我们以Cora数据集为例,将其转换为链接预测任务:目标是判断两个节点间是否存在引用关系。
from torch_geometric.nn import GCNConv from torch_geometric.utils import negative_sampling # 用于生成负样本 # 1. 数据准备:我们需要正样本(存在的边)和负样本(不存在的边) data = dataset[0] edge_index = data.edge_index # 为链接预测任务划分训练/测试边 # 这里我们采用简单划分:随机隐藏一部分边作为正测试样本,并生成对应的负测试样本 from torch_geometric.transforms import RandomLinkSplit transform = RandomLinkSplit(is_undirected=True, split_labels=True, add_negative_train_samples=False) train_data, val_data, test_data = transform(data) print(f'Training edges: {train_data.edge_index.shape[1]}') print(f'Validation edges: {val_data.pos_edge_label_index.shape[1]}') print(f'Test edges: {test_data.pos_edge_label_index.shape[1]}') # 2. 构建编码器模型(使用GCN) class GCNEncoder(torch.nn.Module): def __init__(self, in_channels, hidden_channels, out_channels): super().__init__() self.conv1 = GCNConv(in_channels, hidden_channels) self.conv2 = GCNConv(hidden_channels, out_channels) def forward(self, x, edge_index): x = self.conv1(x, edge_index).relu() x = self.conv2(x, edge_index) return x # 3. 构建链接预测模型(编码器 + 解码器) # 解码器通常采用点积(Dot Product)计算两个节点向量的相似度 class LinkPredictionModel(torch.nn.Module): def __init__(self, encoder): super().__init__() self.encoder = encoder def decode(self, z, edge_index): # 计算节点对之间的分数(这里用点积) src, dst = edge_index return (z[src] * z[dst]).sum(dim=-1) def forward(self, x, edge_index): z = self.encoder(x, edge_index) # 编码得到所有节点的嵌入向量 return z # 4. 初始化模型、优化器 encoder = GCNEncoder(dataset.num_features, 128, 64) # 输出64维嵌入 model = LinkPredictionModel(encoder).to(device) optimizer = torch.optim.Adam(model.parameters(), lr=0.01) # 5. 训练函数(使用负采样) def train_link_predictor(model, train_data, optimizer): model.train() optimizer.zero_grad() # 编码节点 z = model(train_data.x, train_data.edge_index) # 正样本分数 pos_score = model.decode(z, train_data.edge_index) # 生成负样本(不存在的边) neg_edge_index = negative_sampling( edge_index=train_data.edge_index, num_nodes=train_data.num_nodes, num_neg_samples=train_data.edge_index.size(1) # 负样本数量与正样本相同 ) neg_score = model.decode(z, neg_edge_index) # 损失函数:让正样本分数尽可能高,负样本分数尽可能低 # 使用二元交叉熵损失,正样本标签为1,负样本标签为0 pos_loss = -torch.log(torch.sigmoid(pos_score) + 1e-15).mean() neg_loss = -torch.log(1 - torch.sigmoid(neg_score) + 1e-15).mean() loss = pos_loss + neg_loss loss.backward() optimizer.step() return loss.item() # 6. 评估函数(计算AUC) from sklearn.metrics import roc_auc_score @torch.no_grad() def eval_link_predictor(model, data): model.eval() z = model(data.x, data.edge_index) # 正样本分数 pos_score = torch.sigmoid(model.decode(z, data.pos_edge_label_index)) # 负样本分数 neg_score = torch.sigmoid(model.decode(z, data.neg_edge_label_index)) # 拼接分数和标签 scores = torch.cat([pos_score, neg_score]).cpu().numpy() labels = torch.cat([torch.ones_like(pos_score), torch.zeros_like(neg_score)]).cpu().numpy() return roc_auc_score(labels, scores) # 7. 训练循环 for epoch in range(1, 101): loss = train_link_predictor(model, train_data, optimizer) if epoch % 20 == 0: val_auc = eval_link_predictor(model, val_data) test_auc = eval_link_predictor(model, test_data) print(f'Epoch: {epoch:03d}, Loss: {loss:.4f}, Val AUC: {val_auc:.4f}, Test AUC: {test_auc:.4f}')这个例子展示了如何将GNN应用于链接预测任务。核心思想是:先通过GNN编码器获得节点的低维向量表示(嵌入),然后通过一个简单的解码器(如点积)计算节点对之间存在边的可能性。
7. 常见问题、调试技巧与性能优化
7.1 常见问题排查
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
RuntimeError: Expected all tensors to be on the same device | 模型和数据不在同一个设备(CPU/GPU)上。 | 检查model.device和data.device。 | 确保在训练前执行model = model.to(device)和data = data.to(device)。 |
| 梯度爆炸或损失为NaN | 学习率过高;网络层数太深;没有进行归一化。 | 监控每层梯度范数;检查输入特征范围。 | 降低学习率;使用梯度裁剪 (torch.nn.utils.clip_grad_norm_);对节点特征进行归一化 (T.NormalizeFeatures)。 |
| 过拟合(训练精度高,测试精度低) | 模型复杂度过高;训练数据太少;没有使用正则化。 | 观察训练/验证损失曲线。 | 增加Dropout;增加L2权重衰减 (weight_decay);使用更浅的网络;尝试早停(Early Stopping)。 |
| 欠拟合(训练精度也很低) | 模型能力不足;特征信息不够;训练轮次不足。 | 检查模型是否足够深/宽;检查输入特征。 | 增加隐藏层维度或层数;检查特征工程;延长训练时间。 |
| 内存不足(OOM) | 图太大,无法全图加载到GPU内存。 | 监控GPU内存使用。 | 使用邻居采样(Neighbor Sampling),PyG提供了NeighborLoader;尝试CPU训练;使用更小的批次或子图。 |
| 预测结果全是同一类 | 类别极度不平衡;损失函数或输出层有问题。 | 查看模型输出的分布;检查数据集标签分布。 | 为损失函数添加类别权重 (weight参数);检查模型最后一层是否误加了激活函数(如Softmax,应与CrossEntropyLoss二选一)。 |
7.2 模型设计与调优建议
- 层数不要过深:GNN通常存在“过度平滑”问题,即随着层数增加,所有节点的特征会变得相似。对于同质图(如社交网络),2-3层往往足够;对于异质图或需要长距离依赖的任务,可考虑跳跃连接(Jumping Knowledge)或更深但带残差的架构。
- 聚合函数的选择:
mean聚合对噪声更鲁棒,sum聚合能保留邻居数量信息,max聚合能捕捉显著特征。GraphSAGE的LSTM聚合器更强大但计算成本高。可以从mean开始尝试。 - 激活函数与归一化:
ReLU是默认选择。可以尝试LeakyReLU。对于深层GNN,考虑在卷积层后加入BatchNorm或LayerNorm。 - 学习率与优化器:
Adam优化器是安全的选择。学习率从0.01或0.001开始。使用学习率调度器(如ReduceLROnPlateau)在验证集性能停滞时降低学习率。 - 特征工程:节点初始特征的质量至关重要。对于没有特征的数据(如只有拓扑结构的图),可以使用节点度、PageRank等图统计量作为初始特征,或使用DeepWalk、Node2Vec等无监督方法生成嵌入。
7.3 处理大规模图:邻居采样
当图太大无法一次性加载时,必须使用采样。PyG的NeighborLoader是核心工具。
from torch_geometric.loader import NeighborLoader # 假设 `data` 是一个大图 loader = NeighborLoader( data, num_neighbors=[10, 5], # 采样两层,第一层采样10个邻居,第二层采样5个邻居 batch_size=32, # 每个批次的种子节点数 input_nodes=data.train_mask, # 只在训练节点上采样 shuffle=True ) # 训练循环变为批次循环 for epoch in range(epochs): for batch in loader: batch = batch.to(device) optimizer.zero_grad() out = model(batch.x, batch.edge_index) loss = criterion(out[batch.train_mask], batch.y[batch.train_mask]) loss.backward() optimizer.step()采样会引入随机性,但它是训练大规模GNN的唯一可行方法。
8. 总结与拓展方向
通过本文,我们完成了从GNN核心思想(消息传递)到PyTorch Geometric实战(节点分类、链接预测)的完整闭环。你应该已经掌握了:
- 理解GNN的动机:它专为处理关系数据而生,通过聚合邻居信息来学习节点表示。
- 掌握PyG基础:会用
Data对象构建图,理解edge_index的格式。 - 搭建GCN模型:能够使用
GCNConv等层构建网络,并完成训练、验证、测试流程。 - 应对常见任务:掌握了节点分类和链接预测两种经典范式的代码实现。
- 具备调试能力:能根据常见问题现象进行排查和调优。
如果你想继续深入,以下方向值得探索:
- 更先进的模型架构:尝试GAT (Graph Attention Network),学习如何让模型关注重要的邻居;了解GraphSAGE的归纳学习能力及其采样策略。
- 异质图神经网络 (HGNN):现实中的图往往包含多种节点和边类型(如学术图中有作者、论文、会议)。可以学习
torch_geometric.nn.HeteroConv。 - 动态图神经网络:处理随时间变化的图(如社交网络演变)。关注
torch_geometric.temporal模块。 - 图自监督学习:如何在缺乏标签的情况下学习好的图表示?对比学习(如GraphCL)是一个热门方向。
- 图生成模型:如何生成合理的分子图或社交网络?可以了解GraphVAE, GraphRNN等模型。
GNN正在快速渗透到各个领域。从今天开始,当你面对任何带有“关系”的数据时,不妨多思考一下:这能不能建模成一个图?如果能,GNN或许就是你打开新世界大门的钥匙。建议将本文的代码作为模板收藏,在遇到具体任务时,调整模型结构和数据处理流程,相信你能够快速上手并解决实际问题。