图神经网络入门:用 MLP 作为 Cora 数据集的基线模型
以下是该 MLP 模型在 Cora 数据集上完整训练与测试流程的纯文本形式流程图。
开始
│
├─ 1. 加载数据
│ └─ 使用 Planetoid 加载 Cora 数据集(root="D:\\py机器学习\\data")
│ → 获取 data 对象(包含 x, y, train_mask, val_mask, test_mask)
│
├─ 2. 数据准备(可选)
│ └─ 将 data.x 和 data.y 转为 pandas DataFrame(仅用于查看)
│
├─ 3. 定义辅助函数
│ └─ accuracy(y_pred, y_true):计算预测准确率
│
├─ 4. 构建 MLP 模型
│ └─ 输入维度:1433(Cora 节点特征数)
│ └─ 隐藏层维度:16
│ └─ 输出维度:7(Cora 类别数)
│ └─ 结构:Linear → ReLU → Linear → log_softmax
│
├─ 5. 模型训练(fit 方法)
│ ├─ 初始化优化器:Adam(lr=0.01, weight_decay=5e-4)
│ ├─ 初始化损失函数:NLLLoss
│ └─ 循环 Epoch = 0 到 100:
│ ├─ 前向传播:out = MLP(data.x)
│ ├─ 计算训练损失:loss = NLLLoss(out[train_mask], y[train_mask])
│ ├─ 计算训练准确率:acc = accuracy(pred[train_mask], y[train_mask])
│ ├─ 反向传播:loss.backward()
│ ├─ 更新参数:optimizer.step()
│ └─ 每 20 轮:
│ ├─ 计算验证损失:val_loss = NLLLoss(out[val_mask], y[val_mask])
│ └─ 计算验证准确率:val_acc = accuracy(pred[val_mask], y[val_mask])
│ → 打印日志(Epoch | Train Loss | Train Acc | Val Loss | Val Acc)
│
├─ 6. 模型测试(test 方法)
│ ├─ 切换至评估模式:model.eval()
│ ├─ 关闭梯度计算:@torch.no_grad()
│ ├─ 前向传播:out = MLP(data.x)
│ ├─ 获取预测类别:pred = out.argmax(dim=1)
│ └─ 计算测试准确率:acc = accuracy(pred[test_mask], y[test_mask])
│
└─ 7. 输出结果└─ 打印:MLP test accuracy: XX.XX%
🔍 补充说明
- 整个流程不涉及图结构(如邻接矩阵或邻居聚合),仅使用节点自身特征。
- 训练/验证/测试划分由数据集内置的布尔掩码(mask)决定。
- 这是一个标准的监督分类流程,适用于任何带标签的特征数据,不限于图数据。
第一部分:导入必要的库
import torch
import pandas as pd
from torch_geometric.datasets import Planetoid
import torch
from torch.nn import Linear
import torch.nn.functional as F
torch:PyTorch 核心库,用于张量计算和自动微分。pandas:用于数据处理,这里用来将节点特征转为 DataFrame。Planetoid:PyTorch Geometric 提供的经典图数据集类(如 Cora、CiteSeer、PubMed)。Linear:全连接层(线性变换)。F(torch.nn.functional):包含激活函数(如relu)、损失函数(如log_softmax)等。
注:重复导入
torch是多余的,但不影响运行。
第二部分:加载数据集(Cora)
dataset = Planetoid(root="D:\\py机器学习\\data", name="Cora")
data = dataset[0]
- 使用
Planetoid加载 Cora 引文网络数据集:- 节点 = 论文
- 边 = 引用关系
- 节点特征 = 词袋(bag-of-words)向量(1433 维)
- 标签 = 论文所属的 7 个类别之一
root指定数据保存路径。dataset[0]获取图数据对象(Cora 只有一个图,所以索引为 0)。data包含:x: 节点特征矩阵([2708, 1433])y: 节点标签([2708])edge_index: 边的稀疏表示([2, 10556])train_mask,val_mask,test_mask: 布尔掩码,划分训练/验证/测试集
第三部分:将特征和标签转为 pandas DataFrame(可选调试)
df_x = pd.DataFrame(data.x.numpy())
df_x['label'] = pd.DataFrame(data.y)
print(df_x)
- 将节点特征
data.x转为 NumPy 再转为 pandas DataFrame。 - 把标签
data.y作为新列'label'加入。 - 主要用于查看或调试数据结构,在训练中并不需要。
第四部分:定义准确率函数
def accuracy(y_pred, y_true):"""Calculate accuracy."""return torch.sum(y_pred == y_true) / len(y_true)
- 输入:预测标签
y_pred(如[0, 2, 1, ...])和真实标签y_true。 - 计算预测正确的比例(即准确率)。
- 注意:这里假设
y_pred是类别索引(不是概率),所以直接比较。
第五部分:定义 MLP 模型类
class MLP(torch.nn.Module):def __init__(self, dim_in, dim_h, dim_out):super().__init__()self.linear1 = Linear(dim_in, dim_h)self.linear2 = Linear(dim_h, dim_out)
- 继承
torch.nn.Module,构建一个两层的多层感知机(MLP)。 dim_in: 输入维度(Cora 是 1433)dim_h: 隐藏层维度(设为 16)dim_out: 输出类别数(Cora 是 7)
第六部分:前向传播(forward)
def forward(self, x):x = self.linear1(x)x = torch.relu(x)x = self.linear2(x)return F.log_softmax(x, dim=1)
- 输入特征
x(形状[N, dim_in]) - 经过第一层线性变换 + ReLU 激活
- 再经过第二层线性变换
- 输出使用 log_softmax(因为后面用
NLLLoss,需要 log-probabilities)
⚠️ 注意:这是一个纯 MLP,没有使用图结构(即忽略
edge_index),仅基于节点特征做分类。
第七部分:训练方法(fit)
def fit(self, data, epochs):criterion = torch.nn.NLLLoss()optimizer = torch.optim.Adam(self.parameters(), lr=0.01, weight_decay=5e-4)self.train()for epoch in range(epochs + 1):optimizer.zero_grad()out = self(data.x)loss = criterion(out[data.train_mask], data.y[data.train_mask])acc = accuracy(out[data.train_mask].argmax(dim=1), data.y[data.train_mask])loss.backward()optimizer.step()if (epoch % 20 == 0):val_loss = criterion(out[data.val_mask], data.y[data.val_mask])val_acc = accuracy(out[data.val_mask].argmax(dim=1), data.y[data.val_mask])print(f'Epoch{epoch:>3}|Train Loss:{loss:.3f}|Train Acc:{acc * 100:>5.2f}%|Val Loss:{val_loss:.2f}|Val Acc:{val_acc * 100:.2f}%')
- 损失函数:
NLLLoss(负对数似然),配合log_softmax使用。 - 优化器:Adam,带 L2 正则(
weight_decay=5e-4)。 - 训练流程:
- 清零梯度
- 前向传播得到输出
out - 仅在
train_mask对应的节点上计算 loss 和 acc - 反向传播 + 更新参数
- 每 20 轮打印一次训练和验证指标(使用
val_mask)
📌 关键点:MLP 不利用图结构,只用节点特征,所以性能通常不如 GCN/GAT 等图神经网络。
第八部分:测试方法(test)
@torch.no_grad()def test(self, data):self.eval()out = self(data.x)acc = accuracy(out.argmax(dim=1)[data.test_mask], data.y[data.test_mask])return acc
@torch.no_grad():关闭梯度计算,节省内存,加速推理。self.eval():设置为评估模式(对 Dropout/BatchNorm 有影响,但本模型没有)。- 在
test_mask上计算准确率。
第九部分:实例化模型并训练
mlp = MLP(dataset.num_features, 16, dataset.num_classes)
print(mlp)mlp.fit(data, epochs=100)acc = mlp.test(data)
print(f'\nMLP test accuracy: {acc*100:.2f}%')
- 创建 MLP:输入 1433 维,隐藏层 16,输出 7 类。
- 训练 100 轮。
- 最后在测试集上评估准确率。
输出:
0 1 2 3 4 5 ... 1428 1429 1430 1431 1432 label
0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 3
1 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 4
2 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 4
3 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0
4 0.0 0.0 0.0 1.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 3
... ... ... ... ... ... ... ... ... ... ... ... ... ...
2703 0.0 0.0 0.0 0.0 0.0 1.0 ... 0.0 0.0 0.0 0.0 0.0 3
2704 0.0 0.0 1.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 3
2705 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 3
2706 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 3
2707 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 3[2708 rows x 1434 columns]
MLP((linear1): Linear(in_features=1433, out_features=16, bias=True)(linear2): Linear(in_features=16, out_features=7, bias=True)
)
Epoch 0|Train Loss:1.956|Train Acc:14.29%|Val Loss:1.95|Val Acc:12.20%
Epoch 20|Train Loss:0.120|Train Acc:100.00%|Val Loss:1.42|Val Acc:50.40%
Epoch 40|Train Loss:0.013|Train Acc:100.00%|Val Loss:1.48|Val Acc:51.00%
Epoch 60|Train Loss:0.007|Train Acc:100.00%|Val Loss:1.43|Val Acc:54.00%
Epoch 80|Train Loss:0.008|Train Acc:100.00%|Val Loss:1.36|Val Acc:55.00%
Epoch100|Train Loss:0.008|Train Acc:100.00%|Val Loss:1.33|Val Acc:56.40%MLP test accuracy: 56.20%总结
| 模块 | 说明 |
|---|---|
| 数据 | 使用 Cora 图数据集,但 MLP 忽略图结构 |
| 模型 | 两层全连接神经网络(MLP) |
| 训练 | 仅用带标签的训练节点,标准监督学习 |
| 评估 | 在验证集监控,在测试集报告最终性能 |
| 局限 | 未利用图信息(邻居、边),性能通常低于 GNN |
💡 如果你想利用图结构,可以改用 GCN、GAT 等模型,它们会聚合邻居信息。

