4. GCN模型原理:图卷积操作、消息传递机制、聚合函数
我们做药物知识图谱的表示学习,最常碰到的不是欧氏空间的数据,而是图结构。分子图、蛋白质互作网络、药物-靶点关系图,这些东西没法直接塞进CNN。GCN就是来解决这个问题的——它让你能在图上做卷积,说白了就是让每个节点去“听”邻居节点在说什么,然后更新自己的表征。
我记得刚开始接触GCN时,第一反应是:“不就是个邻居平均吗?”后来真动手做才意识到,里面的设计细节直接决定了模型在链接预测、节点分类任务上的表现。今天我们就把它拆开揉碎了讲。
4.1 从谱域到空间:图卷积的两种思路
图卷积最早是从图的拉普拉斯矩阵特征分解来的——也就是谱域方法。代表性的工作是ChebNet和它的一阶近似简化版GCN。我建议初学者直接理解空间域的消息传递视角,因为物理含义更清晰,你调参也能直觉感知。
空间域的核心思想:每个节点通过聚合邻居的特征来更新自己。这个过程通常包含三步:
- 消息生成——每个节点给邻居发消息(通常是自己的特征乘上可学习变换)
- 消息聚合——把收到的邻居消息合并成一个向量(求和、均值、最大池化等)
- 节点更新——把聚合结果和自身的旧特征融合,通过激活函数输出新特征
你想想看,这和卷积神经网络在图像上的滑动窗口概念是不是很像?只不过图像上的邻居是空间上固定的像素网格,而图上的邻居是不规则的、数量可变的。
4.2 消息传递机制的数学形式
原始GCN的消息传递公式可以写成标准形式:
H^{(l+1)} = σ( Â · H^{(l)} · W^{(l)} )
其中 Â = D̃^{-1/2} Ã D̃^{-1/2}
à = A + I
D̃_ii = ∑_j Ã_ij
我来解释一下这几个符号在干什么:
- A 是邻接矩阵,表示图的结构。加上单位阵I(自环),让节点自己能听到自己的信息。
- D̃ 是度矩阵,对角线上存着每个节点的邻居数(包含自己)。用 D̃^{-1/2} 做对称归一化,相当于把邻居的消息做平均,同时确保大度节点不会主导特征尺度。
- W^{(l)} 是可训练权重矩阵,作用是对特征做线性变换。
- σ 是非线性激活函数,比如ReLU。
我在实际项目里踩过的坑:如果不加自环(即用原始A而不是Ã),中心节点会丢失自己的特征信息,在深层网络里节点表征容易趋同——这就是常说的 oversmoothing 问题的早期表现之一。我曾经在一批蛋白质功能预测数据上,不加自环的结果比加上自环低了近6个点的F1,从那以后我每次写GCN都会先检查自环。
4.3 聚合函数的设计选择
聚合函数决定了如何从邻居信息生成单一向量。它在GCN中属于最关键的设计维度之一,因为不同聚合方式擅长捕捉不同类型的图结构模式。
| 聚合函数 | 公式形式 | 适合场景 | 我在项目中的体会 |
|---|---|---|---|
| 均值聚合 | (1 / deg(i)) · Σ h_j | 节点度差异小的图(如分子图) | 稳定基准,但会稀释稀有结构信号 |
| 求和聚合 | Σ h_j | 需要区分不同度数节点(如社交网络) | 对关键子结构敏感,偶尔梯度爆炸 |
| 最大池化 | max( h_j ) 沿特征维度 | 突出最显著邻居特征(如关键基团检测) | 在我的药物靶点预测任务中效果最好 |
| 注意力加权 | Σ α_ij · h_j | 邻居重要性各异的场景(如异构图) | 参数多但可解释性强,后面GAT会细讲 |
4.4 GCN在药物知识图谱中的典型工作流
我一般会在药物分子图上先做几层GCN,然后拿每个原子的表征做图池化(sum/mean)得到分子整体向量,再用这个向量去预测理化性质或与靶点蛋白的结合亲和度。整个流程可以用下面这张图来概括:
嗯,这里要注意:图中的“自环消息”不是必须的,但加上后可以让中心节点直接感受自己的旧特征,相当于给残差连接打了底。我在做大规模药物筛选时发现,2层GCN加自环几乎是最稳的基线配置。
4.5 为什么GCN对药物知识图谱特别有效?
药物分子本质上是有标签的图——原子类型、化学键类型、立体化学信息都可以编码成节点/边特征。GCN的消息传递天然能捕获分子中的局部结构模式,比如芳香环、官能团、手性中心。说白了,你在做的是让模型学会“看分子的眼睛”——哪些原子连接在一起、它们之间的成键方式、空间邻近关系。
我过去对比过MLP+指纹和GCN学到的表征,发现GCN对分子量、极性表面积、logP的预测明显更准。特别是那些包含桥环、螺环的复杂分子,传统指纹往往丢失拓扑信息,而GCN能保留完整的图邻接关系。
4.6 代码片段:PyG中的简单GCN层
用PyTorch Geometric实现一个GCN卷积层,你只需要几行:
import torch
import torch.nn.functional as F
from torch_geometric.nn import GCNConv
class SimpleDrugGCN(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: 节点特征矩阵 [N, in_channels]
# edge_index: 边索引 [2, E]
x = self.conv1(x, edge_index)
x = F.relu(x)
x = self.conv2(x, edge_index)
return x # 节点表征,用于下游任务
这里的 edge_index 是COO格式的边列表,PyG内部会自动做对称归一化和自环处理。你只需要关注特征设计和层数选择即可。
好,这一章我们重点梳理了GCN的三个核心操作及其在药物知识图谱中的落地方式。下一部分我们会深入GAT——注意力机制如何让消息传递变得更聪明。不过现在先消化一下这些基础概念,动手跑一个简单的分子性质预测实验,你会有更深的体感。