cs3319-project2 / docs_first_principles /02_architecture_overview.md
NLP-beginner's picture
CS3319 Project 2 final deliverable (public F1 = 0.96626)
f28d994
|
Raw
History Blame Contribute Delete
16.3 kB

02 系统架构总览(两阶段 Stacking)

本文面向"懂一点 ML,但不完全懂图推荐系统"的读者。我们从为什么出发,讲清楚最终系统长什么样、每一块为什么存在、它们之间如何互补。所有数字来自权威事实表(docs_first_principles/_fact_sheet.md);代码位置带行号,便于回溯。


1. 一句话定位

最终系统是一个两阶段 stacking(stacked generalization)架构

  • Stage-1(生产者):多个相互独立、来源各异的模型/规则,各自把"作者-论文"候选对映射成一组 raw 分数或特征向量;
  • Stage-2(融合者):一个 259 维LightGBM 二级 meta-learner,把 Stage-1 的所有输出拼接成一张特征表,用 5-fold OOF(Out-of-Fold)做 leak-free 验证;
  • 决策层:测试阶段不用概率阈值,而用 **rank-cutoff top 50%**,并强制训练-测试重叠的已知正边为 1。

最终公开榜 F1 = 0.96626submission_rich_rw7_highorder_directed_r0.500000.csv)。

为什么要两阶段而不是一个端到端 GNN?下一节从第一性原理讲清。


2. 为什么不直接用一个 GNN:互补性论证

单一 GNN(本项目用 LightGCN)的本质是协同过滤:它通过 author·paper 点积(code/train_val_lgcn_ensemble.py:102-104)和异构图邻居传播,学习一组隐向量,使"被同一类作者读过的论文"在嵌入空间里靠近。它结构上无法表达以下信息(这正是 Stage-1 其它模块要补的盲区):

LightGCN 的盲区 谁来补 信号性质
显式引用重叠(A 的历史论文恰好引用/被引候选 p) 显式图 / meta-path 特征 确定性、typed 证据
内容语义相似度(USE 嵌入余弦) content mean-cos / rich profile 与 CF 正交的内容维度
矩阵分解互补视角 BPR-MF(dim=256) 另一种 CF 分解,捕获 LightGCN 漏掉的协同信号
全局网络接近度(随机游走意义上的"远亲") DeepWalk / Node2Vec(7 块) 不可训练的固定游走嵌入
高阶引用传播(作者历史阅读谱沿引用图扩散 k 跳) 高阶有向传播 H_k=R·C^k meta-path 的高阶、稠密化版本

经验上,单一 LightGCN 集成的验证 F1 仅 0.9386;把这些正交信号一层层 stack 上去,最终验证 F1 提升到 0.9669(验证)/ 0.96626(公开)。单一模型无法触及这个上限,因为不同信号分布在不同的数学空间里,一个损失函数无法同时最优化它们。 Stacking 的作用就是让一个非线性的 meta-learner(LightGBM)去学习"在什么情况下信哪个源"。


3. 总体 Pipeline(文本树状图)

Official data (data_and_docs/)
 ├─ author-paper 二部图 (bipartite_train_ann.txt, 682,421 行)
 │     └─→ LightGCN 集成 ──→ 主分数 + rank 特征 (4 列)
 │           code/train_val_lgcn_ensemble.py
 │
 ├─ coauthor 无向图 (author_file_ann.txt, 9,663 行)
 │     + citation 有向图 (paper_file_ann.txt, 327,113 行)
 │     └─→ 显式图 / meta-path (A-A-P, A-P-P, A-P-A-P) ──→ 18 列手工特征
 │           code/stack_rank_calibration.py (ExplicitGraphFeatures)
 │
 ├─ feature.pkl (79,937 × 512 USE 论文嵌入)
 │     └─→ content mean-cos + rich content profile ──→ 4 + 18 列
 │           code/extra_score_sources_ablation.py / content_rich_ablation.py
 │
 ├─ mixed graph (二部 + 合著 + 引用)
 │     └─→ DeepWalk / Node2Vec (7 块 × 11 列 + 11 列聚合) ──→ 77 + 11 列
 │           code/randomwalk_systematic_ablation.py
 │           code/generate_randomwalk_ensemble_submission.py
 │
 └─ citation 矩阵 (paper→paper 有向)
       └─→ 高阶引用传播 H_k=R·C^k, G_k=S·R·C^k (fwd/bwd/undir) ──→ 24 + 45 列
             code/high_order_graph_stack.py (build_high_order*)

 Stage-1 分数/特征 ↑          259 维特征表 ↓

 All features ──→ LightGBM 5-fold OOF stacker (259 维) ──→ rank cutoff top 50%
                   code/high_order_graph_stack.py (fit_lgb_oof / fit_full_predict)
                                                                      │
                                                                      ▼
                                   submission_rich_rw7_highorder_directed_r0.500000.csv
                                   (+ cached_scores/test_known_mask.npy 强制 known=1)
                                   → 公开 LB F1 = 0.96626

4. Stage-1:分数源 / 特征源全景

下表把每个 Stage-1 模块抽象成"输入 → 输出 → 作用"四元组,是本文的核心查表。

模块 输入 输出(特征) 作用 / 为什么需要它 代码位置
LightGCN 集成 二部图 + 论文 USE 嵌入(线性投影输入) 主分数 → rank 4 列 端到端可学习 CF 主分数;整个 stacker 的"地基" train_val_lgcn_ensemble.py
显式图 / meta-path 合著图 + 引用图 + 二部图 18 列手工特征(含 1 列冗余,见台账 #13) typed 路径证据,单一最大增量源 (+0.0174,把验证 F1 从 0.9386 推到 0.9560) stack_rank_calibration.py:108-145
add_rank_features 任意标量分数 4 列(score / global_rank / author_pct / author_rank) 把绝对分数转成"作者组内相对位置",缓解跨作者量纲漂移 stack_rank_calibration.py:148-160
content mean-cos feature.pkl + train_refs 4 列 作者历史论文的 USE 均值 · 候选向量,纯语义相似度 extra_score_sources_ablation.py:80-97
rich content profile feature.pkl 18 列 深度内容画像(max/top-k 聚合等),比 mean-cos 更细 content_rich_ablation.py:54-129
BPR-MF 二部图 4 列(dim=256 MF 分数) 与 LightGCN 互补的另一类 CF 分解 extra_score_sources_ablation.py:100-163
variant43 20 个 LightGCN 变体分数 43 列(20×2 zscore+rank01 + 3 聚合) 把多 run/多 seed 的 LightGCN 分数多样性榨干 generate_post95_submission.py:175-183
topk3 feature.pkl + train_refs 3 列(max, top-3, top-5) 作者历史论文中与候选最相似的前若干篇的相似度 generate_post95_submission.py:186-216
negative_evidence X_hand 8 列 负证据/交互特征,刻画"为什么不读"的另一面 post95_ablation.py:176-192
DeepWalk / Node2Vec × 7 mixed graph 7×11 = 77 列 + 11 列 aggregate 不可训练的固定游走嵌入,补 LightGCN 缺的全局接近度 randomwalk_systematic_ablation.py:216-270
高阶引用传播(无向) R, C, S 稀疏矩阵 24 列 H_k=R·C^k(k=1..4)+ G_k=S·R·C^k(k=0..3),每路径 {raw, popnorm, log} high_order_graph_stack.py:74-122
高阶引用传播(有向) R, C(fwd/bwd/undir), S 45 列 三方向 fwd/bwd/undir;ap 路径 {raw,popnorm,delta}、aa_ap 路径 {raw,popnorm} high_order_graph_stack.py:125-176

X_base 小计核对:4(rank)+18(explicit)+8(neg)+3(topk)+43(variant)+4(content)+4(mf) = 84 维(randomwalk_systematic_ablation.py:273-300 build_base_features 实测)。其余为 rich content(18)、7 RW 块(77)、RW aggregate(11)、undirected(24)、directed(45),合计 259


5. Stage-2:LightGBM stacker 做什么

Stage-2 不是简单加权平均,而是一个带正则的梯度提升树 meta-learner,它把 259 维特征表当成普通监督学习输入。两套训练路径(code/high_order_graph_stack.py):

路径 用途 关键超参
fit_lgb_oof (L179-198) 5-fold StratifiedKFold OOF,产 leak-free 验证 F1 n_est=1200, lr=0.025, num_leaves=15, reg_lambda=8.0, min_child_samples=100, subsample/colsample=0.9
fit_full_predict (L201-216) 全量训练后预测测试对 n_est=1400, lr=0.022(其余同上)

为什么用 OOF:如果直接在验证集上 fit 再在验证集上算 F1,meta-learner 会过拟合到 Stage-1 的分数噪声(尤其 variant43 等由验证分数派生的特征)。5-fold OOF 保证每个验证样本的预测来自"没见过它"的模型,从而 F1 可信。最终 OOF F1 = 0.9668737,AUC = 0.9949183,最优概率阈值 = 0.4617308

为什么用 LightGBM 而不是线性回归:259 维里大量是共线/异构特征(如 out[i,3] 与 out[i,12] 恒等,台账 #13),LightGBM 自动忽略冗余列,并能学到"在 popularity 高时信 content,在长尾时信 high-order"这类非线性交互。

为何叫 two-stage stacking

"Two-stage"指两个训练阶段解耦:Stage-1 各模型独立训练(LightGCN 反传 BPR、DeepWalk 跑 SkipGram、BPR-MF 跑矩阵分解),Stage-2 在它们的冻结输出上再训一个模型。这是 Wolpert 的 stacked generalization 的标准形态,相对端到端微调有两个工程优势:(1) 每个 Stage-1 模型可独立缓存/替换;(2) Stage-2 看不到原始图,只在分数层做融合,避免梯度噪声互相干扰。


6. 决策层:为何用 rank-cutoff 而非概率阈值

验证集是人工 1:1 构造的(68,242 正 + 68,242 负,见 make_notebook_style_split L132-165),但测试集真实正例比例未知(台账 #25)。LightGBM 概率在 1:1 上调出的最优阈值 0.4617 迁移到测试集会发生漂移(漂移后正例率 ~0.5242)。

因此测试决策故意绕开概率阈值,改用排序截断:按 LightGBM 分数对 ~2.05M 测试对排序,取 top 50% 标 1(high_order_graph_stack.py:219-229 write_ratio_submission),再强制 test_known_mask.npy 中的训练-测试重叠正边为 1。ratio 在 {0.498..0.502} 间扫描,公开最佳为 0.500000


7. 最终方法 vs 早期 LightGCN ensemble 的区别

维度 早期 LightGCN ensemble(preliminary 阶段) 最终方法
模型形态 6 个 checkpoint(5×256d/4层 + 1×384d)的均值集成 259 维 LightGBM 两阶段 stacking
用到的信号 仅 CF 隐向量 CF + 显式图 + 内容 + MF + 随机游走 + 高阶传播
验证 F1 0.9386(dynamic best,未单独提交) 0.9669
公开 F1 0.93044(sub_ens6_t0.36.csv 0.96626
决策 概率阈值 t=0.36 rank-cutoff top 50% + known mask

注意(台账 #10):早期 6-model 与 dynamic best(dyn202_l2d512_bpr_bigbatch_more,2 seed × 512d/2层 dot)是两个不同阶段产物,都常被笼统称作"LightGCN ensemble"。最终 stacking 用的主分数是后者(验证 0.9386)。

baseline → final 的演进主线

阶段 验证 F1 单步增量
0 baseline(启发式/度) 0.8850
1 LightGCN 异构 CF 集成 0.9386 +0.0536
2 + 显式图 / meta-path stacking 0.9560 +0.0174(单一最大)
3 + Post95 变体 + content + BPR-MF + rich content 0.95990 +0.0039
4 + DeepWalk / Node2Vec → 7 块 ensemble 0.964921 +0.0050
5 + 高阶引用传播(undirected → directed, 259 维) 0.966874 +0.0020

可以看到,最大单步增益不在 GNN 本身,而在把 CF 与显式图结构融合(第 2 步);之后的高阶传播、有向化是锦上添花(验证 F1 0.9649 → 0.9669)。


8. 模块间的互补关系(一句话版)

  • LightGCN:学会"谁和谁一起读"——CF 共现隐式表示。
  • 显式图/meta-path:直接数"引用重叠、合著者共读、A-P-A-P"——CF 学不到的确定性证据。
  • content / rich:看"论文说了什么"——与 CF 正交的语义维度。
  • BPR-MF:另一种矩阵分解,补 LightGCN 漏掉的协同因子。
  • DeepWalk/Node2Vec:跑随机游走得全局接近度——补 LightGCN 的局部传播盲区。
  • 高阶有向传播:把"作者历史阅读谱沿引用图扩散 k 跳"——meta-path 的高阶、稠密、方向感知版本。

这六个源覆盖了协同、结构、语义、全局游走、高阶传播五个互补视角,meta-learner 负责按上下文加权组合。


9. 数据流一致性说明

make_notebook_style_split(seed=202, train_frac=0.9) 产出的 136,484 对验证集(68,242 正 + 68,242 负)是所有 Stage-1 分数与 Stage-2 OOF 的统一对齐基准。因为 split seed 烘焙进每一个 .npy改 seed 会整链失效。最终 OOF 与 val_labels_seed202.npy 已复算对齐(F1 0.966874,AUC 0.994918),无泄漏。


可迁移到论文中的写法

以下为可直接进 ACM 中文论文 TeX 的正式表述,数字与权威事实表一致。

系统架构段(建议放在 Method 节首)

本文提出的推荐系统采用两阶段堆叠(two-stage stacking)架构。
Stage-1 由若干相互独立、信号正交的模块构成,包括:基于异构图的
LightGCN 协同过滤集成、显式元路径(A-A-P / A-P-P / A-P-A-P)手工
特征、基于预训练 512 维 Universal Sentence Encoder 的内容相似度
与画像特征、BPR 矩阵分解、7 个 DeepWalk/Node2Vec 随机游走嵌入块,
以及本文核心贡献——高阶有向引用传播。各模块在统一的全连接异构图
(6{,}611 位作者、79{,}937 篇论文)上独立训练,其输出拼接为 259
维特征向量,送入 Stage-2 的 LightGBM 二级元学习器。

元学习器采用 5 折分层交叉验证的 Out-of-Fold (OOF) 策略以获得
无泄漏的验证性能(F1 = 0.9669,AUC = 0.9949)。测试阶段,由于
验证集为人工构造的 1:1 平衡集,其最优概率阈值无法直接迁移,因此
采用排序截断(rank-cutoff)决策:按元学习器分数对约 2.05M 测试对
排序,取前 50% 预测为正,并强制训练-测试重叠的已知正边为正。
最终在公开榜单取得 F1 = 0.96626。

核心贡献公式段

记 $R \in \mathbb{R}^{|A|\times|P|}$ 为行归一化的作者-论文邻接矩阵,
$C \in \mathbb{R}^{|P|\times|P|}$ 为行归一化的论文引用转移矩阵
(分别按前向 / 后向 / 无向三种边方向构造),$S \in \mathbb{R}^{|A|\times|A|}$
为行归一化的合著矩阵。定义两类高阶传播分数:
\[
H_k = R\,C^k,\qquad G_k = S\,R\,C^k .
\]
$H_k[a,p]$ 衡量作者 $a$ 的历史阅读谱沿引用网络传播 $k$ 跳后到达候选
论文 $p$ 的累积亲和度;$G_k[a,p]$ 则将合著邻居的历史阅读谱聚合为
"合作者群体共识"后再传播 $k$ 跳。三种边方向 × 多跳 × {原始值、
popularity 归一化、对数压缩 / 逐跳增量} 共产生 69 维高阶特征,
与 Stage-1 其它特征联合送入元学习器。

消融贡献段(替代 LightGBM gain,因本机无 lightgbm)

由于运行环境未安装 LightGBM,无法直接报告基于 gain 的特征重要性。
本文改用消融瀑布(ablation waterfall)刻画各模块的边际贡献:
在固定 seed=202 的 1:1 OOF 上,相对 LightGCN 基线(F1 = 0.9386),
引入显式元路径带来 +0.0174(单一最大增益),BPR/内容/画像特征共
+0.0039,7 块随机游走集成 +0.0050,高阶有向引用传播 +0.0020,
最终达到 F1 = 0.9669。

模块—输入—输出—作用表(可直接转 LaTeX tabular)

模块 输入 输出特征 作用
LightGCN 集成 二部图 + USE 嵌入 主分数 + rank (4 维) CF 协同过滤主分数
显式图 / meta-path 合著图 + 引用图 + 二部图 18 维 typed 路径证据(最大增量源)
content mean-cos / rich profile feature.pkl 4 + 18 维 内容语义相似度与画像
BPR-MF 二部图 4 维 (dim=256) 互补 CF 分解
variant / topk / negative LightGCN 多变体 + feature.pkl 43 + 3 + 8 维 多样性与负证据
DeepWalk / Node2Vec (×7) mixed graph 77 + 11 维 全局随机游走接近度
高阶引用传播 R, C(fwd/bwd/undir), S 24 + 45 维 高阶稠密化 meta-path
LightGBM meta-learner 上述 259 维 OOF F1 0.9669 非线性融合
rank-cutoff 决策 测试分数 + known mask submission CSV 公开 F1 0.96626