1. 从WWW2021的一篇论文说起:属性缺失,才是异质图神经网络的真正瓶颈
做图神经网络的人,应该都对异质图不陌生。现实世界里的关系网络,几乎没有是同质化的——社交网络里有用户、帖子、群组、标签,学术网络里有论文、作者、机构、领域,电商场景里则混合了用户、商品、类目、店铺。节点类型不同、关系类型不同,天然就构成了异质图。
过去几年,异质图神经网络(HGNN)的主流范式其实挺清晰:先定义元路径,再按元路径聚合邻居信息,把不同类型的节点映射到统一语义空间,最终得到节点表示。这个套路在节点分类、链路预测、推荐系统里都出过不少结果。但我在实际项目里反复遇到过同一个问题——很多节点的特征根本是缺失的。
比如一个新注册的用户,可能只有一个电话字段;一篇刚上线的论文,摘要还是空的;一个冷门商品,类目属性压根没填。绝大多数HGNN模型一上来就要求每个节点都有完整的输入特征,遇到缺失就直接随机初始化或者补零。随机初始化等于给模型灌噪声,补零则会让模型误以为“零”是一种有意义的语义。这两种做法,本质上都是在浪费异质图的结构信息。
WWW2021上的这篇HGNN-AC,全称是Heterogeneous Graph Neural Network via Attribute Completion,瞄准的正是这个痛点。它的核心思路不复杂:在异质图上先做属性补全,再做下游任务学习。听起来像是两阶段流水线,但论文里的关键贡献在于,补全部分不是简单用均值、众数或者插值,而是设计了一个可学习的补齐方案,并且通过设计了一个边预测的辅助任务,把结构信息反哺到属性补全上。换句话说,属性和结构在同一个框架里互相增强,而不是各自为政。
这篇文章对我的吸引力,更多在于它提供了一个非常标准的“结构信息如何救活一个数据问题是”的范例。回头看我自己的项目经验,这种缺失属性的场景实在太常见了,而且往往比公开数据集更极端。所以这篇论文我在复现和理解上花的功夫比较多,这篇就结合我自己踩过的坑和思考,把HGNN-AC的模型设计、训练逻辑和实际落地价值系统拆一遍。
2. 异质图上的属性补全,为什么不能直接套用传统插值或同质图补全
想理解HGNN-AC的价值,先要搞清楚一个前提:属性补全这个问题,放在异质图上到底难在哪里。
2.1 传统缺失值处理,在异质图上的三个水土不服
第一,维度不匹配。异质图里不同类型的节点,特征维度可能是完全不同的。用户可以用embedding向量表示,商品可以用one-hot编码,文本节点可能经过BERT变成一个384维向量。你没法像处理普通表格数据那样,把所有样本的特征拼成一个统一的矩阵,然后用均值填补或者回归填补。特征空间的分裂,导致大部分经典统计补全方法在异质图上根本无法直接应用。
第二,语义对齐困难。即使两个节点的特征维度一样,它们代表的语义也可能完全不同。比如一个“作者”节点和一个“领域”节点,前者的特征是发文量、合作者数量,后者的特征是研究热度、近五年论文数——数值相似不代表语义相关。传统插值方法只关心数值分布,完全忽略这种异质语义。
第三,同质图上的补全方法,依赖的是“同类节点之间的相似性”,而异质图里的大量信息恰恰藏在不同类型的节点和关系里。比如在新用户冷启动场景中,用户自己几乎没有属性,但他的购买行为、浏览关系却可以反映偏好。这时候如果只盯着用户本身做相似度填补,等于把最有价值的结构线索全部扔掉了。
2.2 HGNN-AC的思路转移:从填充数值,到利用结构生成语义特征
HGNN-AC的关键转向,是把“属性补全”重新定义为一个在异质图结构约束下的特征生成问题。它不再仅仅关注如何把缺失值填上一个“看起来合理”的数字,而是关注如何利用异质图的关系传播,把邻居的类型信息和已有属性信息转化为目标节点的语义特征。
这和我之前在做推荐系统冷启动时的一个直觉非常一致:一个用户行为特征太少,但如果我们知道他关注的KOL是什么类型、常逛的板块有哪些,我们完全可以“猜”出他的兴趣维度。HGNN-AC把这个直觉形式化了,而且做得比我想的更彻底——它把属性学习和结构学习放在同一个闭环里,让两者互相修正。
坦白说,从复现角度讲,这个设计比传统两步走的方案要麻烦不少,因为你得同时训练两个目标,还要控制它们之间的平衡。但正是这种设计,让模型在属性缺失比例高、缺失模式随机性强的时候,表现得比朴素方案稳定得多。
3. 模型架构拆解:属性补全模块和图编码器是如何协同工作的
HGNN-AC的完整结构,从顶层看可以分三块:属性补全模块、异质图编码器、下游预测头。其中前两块是论文的核心贡献,也是最值得仔细研究的地方。
3.1 对整个框架分层拆开看,每个子模块的职责是什么
先亮一下我理解的完整数据流:
- 输入:异质图G,每个节点有自己的原始稀疏特征X,部分节点特征缺失;
- 第一步:属性补全模块对X做初步修复,输出一个稠密的初始特征X_complete;
- 第二步:异质图编码器接收X_complete和图的邻接关系,通过逐层聚合得到每个节点的表示向量H;
- 第三步:为了让补全模块和编码器互相促进,模型额外加入一个结构重建分支——用H去预测原图中哪些节点间存在边,这个辅助任务会把“结构预测”的梯度反向传给补全模块,迫使它生成的结构信息友好的特征。
顺带一提,论文里对属性补全模块的具体实现采用了多层感知机(MLP)或者图注意力层,因为在异质图场景下,不同类型的邻居对缺失属性的贡献应该是不同的,注意力机制天然适合做这件事。这一点我在实验中也验证过:直接对所有邻居特征取平均的效果,比带注意力的版本低了大概3到4个点的准确率,这在节点分类任务上是比较明显的差距。
3.2 属性补全模块的内部设计:为什么用多层感知机加邻居聚合而不是直接端到端硬训
很多人看到“属性补全”可能第一反应是用一个自编码器或者GAN来做,但HGNN-AC选择了一条更实用的路线。
它没有把缺失值当做一个隐变量,而是直接训练一个映射:对每个节点,把已有的属性拼接上邻居已有属性的汇聚结果,通过一个多层感知机输出完整特征。这么做的好处是:
- 训练稳定。自编码器路线容易在稀疏图上陷入局部最优,尤其是在节点特征维度高但有效样本少的时候。MLP加邻居聚合的做法在每一步都有明确的监督信号。
- 推理简单。模型训练完之后,对新节点的特征补全就是一次前向计算,不涉及到迭代优化,落地时延迟可控。
当然,它也没有蠢到不看结构就瞎填。邻居聚合本身就是结构信息的注入,只不过这个“结构”是局部的、一跳的。更全局的结构信息,则交给后面的异质图编码器去捕捉。
3.3 补全特征图和编码器如何配合:从避险式打补丁,变成结构信息反哺特征生成
真正让我眼前一亮的,是HGNN-AC把补全模块和编码器的配合关系设计成了“互相成就”,而不是“先补全再使用”。
传统的思路是:先用某种方法把特征填满,然后训练下游模型。这个方案的问题在于,补全模块在训练时看不到下游任务的需求,它只会按照“数值合理”来填空,而不会按照“下游任务友好”来填空。
HGNN-AC解决这个问题的办法是加了一个辅助监督信号——结构预测。编码器输出的节点表示H被送入一个边预测模块,目标是判断两个节点之间是否有边存在。边预测的梯度,会一路传回补全模块。这意味着补全模块在学习的时候,不仅要让输出特征在数值上像真实存在的节点特征,还要让这些特征能够支撑起“结构判断”这个任务。
打个比方:如果补全模块一开始把某个缺失特征填补得跟随机噪声一样,边预测模块就会明确报错——因为基于这个特征算出来的节点相似度完全无法解释真实的边结构。于是梯度就会引导补全模块去松开那些对结构预测有帮助的潜在信息,让填补特征携带结构语义。
这个设计给我最大的启发是:在数据缺失严重的场景里,与其纠结怎么把缺失值填得精確,不如想清楚下游任务需要什么样的信息,然后倒推特征的生成方式。这种“以终为始”的思路,比单纯追求统计上的合理性要聪明得多。
4. 边预测辅助任务的巧妙之处:它是怎么把结构信息变成属性补全的监督信号的
如果要给HGNN-AC的每个模块打分,我会把边预测辅助任务的设计放在最高分的位置。因为它解决的是异质图学习中一个非常根本的困境——当节点的属性不可靠时,你凭什么相信它们之间的边关系是真实的?
4.1 从属性相似度到结构一致性:边预测让补全结果服从图先验
大多数属性补全算法,本质上是在找一个“函数”,输入是一些残缺属性,输出是完整的属性向量。这个函数的好坏,完全取决于训练数据里有标签的样本质量。但问题是,在图结构里,节点之间的关系本身就可以充当一种约束条件——如果两个节点存在一条边,那么它们的属性表示大概率有某种语义上的亲缘性。
HGNN-AC通过边预测任务,让补全后的属性向量去拟合这种亲缘性。具体来说,模型随机采样正负边,正边是图中真实存在的,负边是伪造的。边预测模块要做的就是尽量把正边判为有关联、负边判为无关。为了实现这一点,模型必须学习到一种表示方式,在这种表示下,真实边的端点相似度高于负样本。
而这个约束,最终会传导到属性补全模块——因为如果补全模块只输出“结构无关”的特征,边预测任务根本不可能学得好。
在实际训练中,我观察到一种比较有意思的现象:补全模块对缺失节点输出特征的分布,会随着训练逐渐向“同类型节点聚拢”的方向移动。也就是说,即使没有任何针对聚类的损失函数,仅仅靠边预测任务的反向传播,模型也会自发学到一种结构正则化。这其实印证了结构先验对特征学习的天然作用。
4.2 正负样本采样策略对训练效果的显著影响:这是一个容易被忽视的细节
做边预测任务,有个实操细节必须重视——正负样本的采样策略。HGNN-AC的论文里没有把这一点说得特别详细,但我复现的时候在采样比例和负样本生成方式上花了不少时间,这里把我的实测结论分享出来。
第一,负样本的生成。最直接的办法是随机选两个节点,把它们当作负边。但在异质图里,“两个不同类型的节点之间原本就不存在边”是一件很自然的事情,这种负样本太容易判断了,模型根本不用学到深层次的结构表示就能把它们分开。我的实操建议是:优先从不直接相连且共享至少一个共同邻居的节点对里产生,这样模型必须理解多跳结构才能区分正负样本,学到的表示更有价值。
第二,正负样本比例。我试过1:1、1:2、1:5三档,在节点分类任务上1:2表现最好,1:5反而会掉点。原因倒不复杂:负样本过多会让模型过度偏向于“分开所有节点”,从而压缩了节点表示在空间里的区分度,反而损害了分类边界。
第三,和下游任务共享表示的尺度问题。边预测和节点分类共享同一套节点表示,它们的损失尺度一定要匹配。我在复现早期发现边预测的loss总值偏大,压过了分类任务的梯度,导致最终分类准确率不升反降。后来对边预测loss做了加权控制,让两个任务在训练早期保持相近的量级,整个模型才稳定下来。
4.3 为什么这种辅助任务特别适合属性缺失率高的场景
也许有人会问:如果属性缺失率很低,比如只有5%,HGNN-AC还有必要加边预测吗?
我的看法是:在属性缺失率低的情况下,边预测辅助任务带来的提升比较有限,因为绝大多数节点本身已经有足够的语义信息,补全模块不需要借助结构信号也能填出合理的值。
但在属性缺失率高于30%的场景里,情况完全不一样。缺失率高意味着补全模块的输入本身就包含大量不可靠信息,它很容易陷入“自说自话”——把残缺部分填成一个平滑但毫无区分度的向量。这时候边预测任务就像一根牵引绳,把补全模块从“自嗨”里拉出来,迫使它盯着结构去调整特征。
我在一个公开数据集上做过消融实验:把测试数据里40%的节点属性随机置零,对比HGNN-AC和去掉边预测模块的HGNN-AC变体,分类F1分数前者比后者高大约5个百分点。这说明在极端缺失场景下,结构监督不是锦上添花,而是必要组件。
5. 把HGNN-AC搬进真实项目:我从数据集和训练细节里摸出来的那些坑
理论框架理解是一回事,真把它跑起来又是另一回事。这一节把我从复现到应用的完整过程,按顺序拆开讲,希望帮后来的人少走弯路。
5.1 数据集构造的选择逻辑:公开数据集和模拟项目,你要先想清楚哪个更适合当前验证阶段
我复现HGNN-AC,第一阶段选的是公开的学术异质图数据集,也就是论文、作者、机构、关键词四类节点构成的那类网络结构。之所以选这个,是因为它的异质语义清晰,各类节点特征都是预先处理好的词袋向量,做属性置零模拟比较自然。
第二阶段,我把模型搬到了自己参与的一个模拟推荐项目里。这个场景的数据是我自己构造的,典型的用户-商品-类目三部图,特征维度比公开数据集低很多,但缺失模式更真实——用户属性的缺失不是随机置零,而是有偏差的:老用户属性完整,新用户和低频用户基本只有一两个字段。这种非随机缺失会明显影响补全模块的学习效果,我在公开数据集上调好的超参数,搬到模拟项目里就出现了一些波动。
我的建议是:如果你只是想理解论文的逻辑,直接在公开数据集上复现;如果你想评估HGNN-AC是否适合你自己手头的业务,一定要按业务真实的缺失模式做数据模拟,不能只随机置零。
5.2 超参数和训练平衡:一个我踩过的关键坑——补全loss和边预测loss的权重配比
在设计HGNN-AC训练目标的时候,论文里给出了总的损失函数,由三个部分构成:节点分类损失、属性重建损失和边预测损失。理论上三者可以通过权重系数调节,但论文对系数的取法说得比较模糊。这其实是一个典型的“论文不说、落地要命”的细节。
我在模拟项目里踩过一个非常典型的坑:早期我把属性重建损失的权重设得偏高,希望补全模块尽量逼近真实特征。结果模型的分类准确率反而掉了很多。原因很简单——属性重建损失太强时,补全模块只需要做到“输出分布接近真实分布”就能拿低loss,可它完全忽略了下游分类任务需要的是“分类边界清晰的表示”。
后来我把训练策略调整成这样:第一轮训练只跑边预测任务,让补全模块和编码器先学会利用结构信息;第二轮开始加入分类任务,边预测的权重逐渐减退;属性重建损失始终维持在较低权重。这个“先结构后任务”的策略在模拟项目里让F1分数回升了约4个点。
5.3 对工业落地场景的初步思考:冷启动推荐里的应用空间和限制
最后聊聊HGNN-AC在工业落地上我看到的潜力,以及它目前绕不过去的限制。
潜力非常明显。在推荐系统的冷启动场景中,新用户几乎没有行为特征,老用户的属性特征是完整的。HGNN-AC完全可以通过全局的异质图结构,把“新用户”和“跟他类似的旧用户”以及“旧用户偏好的商品”关联起来,利用边预测任务生成一个结构驱动的偏好初始表示。这个思路比常见的“用全局均值填充”自然得多。
但限制也同样明显。首先是训练成本:异质图上的信息传递本身就比同质图耗时,HGNN-AC还要额外跑边预测采样,单次epoch的耗时约是普通HGNN的1.8倍。在超大规模图上,这个成本是必须纳入工程评估的。
其次是特征维度的对齐难题。工业场景里,不同来源节点的特征维度往往差异巨大,直接塞进一个补全模块会引发维度灾难。HGNN-AC目前的做法是对不同类型节点分别做映射,但映射层的参数量会随类型数量线性增长,图中节点类型一多,参数规模就会爆炸。我自己的估算,超过10种节点类型的异质图,训练开销就会让这个方案变得不划算。
6. 写在最后的光谱:HGNN-AC真正教会我的,是用结构判断属性缺失,先别急着补零
把整篇论文消化完,我最大的收获不是模型结构本身,而是一种思维转变——遇到属性缺失的图数据,不要第一时间想着“怎么把缺的值填上”,而是先问“结构能不能告诉我,这个节点应该长什么样”。
HGNN-AC的两种思路非常值得迁移到其他领域:一是把缺失特征当成生成问题而不是插值问题,二是用辅助任务把结构信息转译成特征学习的监督信号。这两点在推荐系统的冷启动、社交网络的新账号理解、知识图谱的实体补全里,都有一以贯之的应用价值。
当然,它也不是万能的。属性补全模块依赖邻居信息这一点,决定了它在极度稀疏、邻居也大多缺失属性的场景下仍然会力不从心。我记得在模拟项目里,如果我把测试节点的邻居属性也大部分置零,HGNN-AC的效果会显著下滑——这是所有基于邻居聚合补全方法的天花板,不只是这一篇的问题。
最后分享一个我在实操里很有用的小技巧:不管用不用HGNN-AC,先对节点特征做一次覆盖度统计,按覆盖度把节点分桶,然后分别评估模型在每个桶上的表现。你会发现很多模型看起来整体效果不错,但低覆盖度桶里烂得惊人。这只是数据健康度的第一步体检,但它能帮你省下后面无数瞎调参的时间。