MPNN 消息传递框架详解:三步走核心逻辑
消息传递神经网络(MPNN)是一个通用框架。它把图上的学习任务拆成了三个清晰步骤:消息传递、消息聚合、节点更新。说白了,就是每个节点先给邻居发信息,然后邻居们把收到的信息汇总,最后更新自己的状态。
我刚接触 MPNN 时觉得这名字挺唬人。后来在项目里写了几版代码才明白——本质上就是个“收发室”模型。你想想看,一个节点就是一个人,他需要知道周围人在聊什么,然后决定自己接下来怎么表态。
第一步:消息传递(Message Function)
消息传递定义了一个节点如何构造发给邻居的信息。最简单的做法就是把两个节点的特征拼起来,再做个线性变换。但实际项目中我踩过坑——如果直接拼特征,边的属性就没法用了。
更通用的形式:
m_{uv} = M(h_u, h_v, e_{uv})
其中 M 是消息函数,h_u 是邻居特征,h_v 是自身特征,e_{uv} 是边特征。我建议新手先从简单的 MLP 开始:
def message_function(h_u, h_v, e_uv):
# 拼接特征
concat = torch.cat([h_u, h_v, e_uv], dim=-1)
return W @ concat + b
第二步:消息聚合(Aggregation Function)
收到所有邻居的消息后,需要把它们合并成一个固定维度的向量。常见的聚合器有:
- Sum:求和,保留消息的尺度信息
- Mean:平均,对度大的节点更稳定
- Max:最大值,只保留最强信号
- Attention:加权求和,权重由注意力机制算出
| 聚合方式 | 特点 | 适用场景 |
|---|---|---|
| Sum | 能区分不同度数节点 | 分子图、社交网络 |
| Mean | 对度数不敏感 | 节点分类、特征稳定时 |
| Max | 抗噪声,只取最强信号 | 点云、3D几何 |
| Attention | 自适应权重 | 异构图、关系推理 |
我曾经就吃过 sum 聚合的亏——在一个社交推荐项目里,大V节点度数上万,求和后特征爆炸,训练直接 NaN。后来换成 mean 加 layer norm 才稳住。嗯,这个坑一定要记住。
第三步:节点更新(Update Function)
把聚合后的消息和当前节点特征结合起来,生成新的节点表示。最简单的更新就是 GRU 或者一个残差连接:
h_v^{new} = U(h_v, agg_messages)
一个经典实现:
def update_function(h_v, agg):
# 残差连接 + 激活
return relu(W_self @ h_v + W_neighbor @ agg + b)
完整的消息传递步骤
把三步串起来,单层 MPNN 的流程就是这样:
- 对每条边
(u, v),计算消息m_{uv} = M(h_u, h_v, e_{uv}) - 对每个节点
v,聚合所有邻居消息agg_v = A({m_{uv} for u in N(v)}) - 用聚合结果更新节点
h_v^{new} = U(h_v, agg_v)
scatter_ 操作高效聚合。我常用 scatter_add 或 scatter_mean,比循环快10倍以上。
MPNN 框架图
多层 MPNN 与过平滑
堆叠多层 MPNN 可以捕获更大范围的图结构。但层数多了有个大问题——过平滑。所有节点特征会收敛到同一个值,导致模型退化。
我个人的经验是:3到4层是个安全区间。对于大部分节点分类任务,3层足够捕获三阶邻居信息。如果必须堆很深,可以用跳连(skip connection)或者门控机制缓解。
消息函数的设计灵活性
消息函数是 MPNN 最灵活的部分。你可以根据任务需要自由定制:
- 距离加权:如果图有空间坐标,用距离的倒数作为消息权重
- 角度嵌入:做分子构象时,把键角、二面角编码进消息
- 时间衰减:动态图里,用时间戳给历史消息打折
举个例子,我在做交通流量预测时,把路段的限速、车道数都当作边特征塞进消息函数,预测精度直接提升了8%。
聚合函数的组合使用
单个聚合器有时不够灵活。我推荐的做法是:组合多个聚合器再拼接。比如:
agg = torch.cat([sum_msgs, mean_msgs, max_msgs], dim=-1)
h_v_new = U(h_v, agg)
这相当于让模型自己决定哪种聚合方式更重要。在多个公开数据集上,这种多聚合拼接比单一聚合高出2-3个点。
更新函数的常见变体
除了残差连接,还有几种常用更新方式:
| 变体 | 公式 | 特点 |
|---|---|---|
| 残差更新 | h' = ReLU(h + W·agg) | 简单稳定,缓解过平滑 |
| 门控更新 | g = σ(W·[h, agg]) ; h' = g⊙h + (1-g)⊙tanh(U·agg) | 可学习新旧混合比例 |
| GRU 更新 | 类似循环神经网络 | 可堆叠更深,但有参数量大 |
| LayerNorm + FFN | h' = FFN(LayerNorm(h + agg)) | Transformer风格,训练稳定 |
我比较推荐从残差更新入手。它简单,效果好,容易调试。等模型跑通了再尝试门控或 GRU 版本,感受一下差异。
MPNN 框架的强大之处就在于这三个步骤可以自由组合。你设计新模型时,只需要盯着“消息怎么算”“聚合怎么取”“更新怎么做”这三个问题,其他都是细节。弄懂了这三步,图神经网络的大门就算真正推开了。