# 09 实验演进时间线 (Experiment Timeline) > 本文档面向"懂一点 ML 但不完全懂图推荐系统"的读者,从第一性原理梳理 CS3319 Project 2 这条 author↔paper 阅读推荐链路从 baseline 到最终 0.96626 的完整演进。每一阶段都讲清楚"为什么这么做""验证上拿到什么数字""学到了什么教训",所有数值均带可核验来源(以 `validation_runs/` 下 CSV 与 README/reports 为准),无法核验的项明确标注 **not explicitly confirmed**。 --- ## 1. 任务回顾与本时间线的坐标系 任务是异构学术网络上的二部图链路预测:对 ~2.05M 测试对输出 0/1,公开榜只评 50% 测试对,指标是 F1。我们在全程使用了**同一把验证尺子** —— `make_notebook_style_split(seed=202, train_frac=0.9)`(`code/train_val_lgcn_ensemble.py:132-165`):留出 10% 已知正边(68,242 对)作正例,再等量随机采样非边作负例,得到 **136,484 对、1:1 的人工平衡验证集**(`val_labels_seed202.npy`)。 为什么强调这一点?因为只有验证集固定,后续每一个阶段的"验证 F1"才可比。而正因为验证集是人为 1:1 平衡的、与测试集真实分布不同,我们最终**放弃验证最优概率阈值,改用 rank cutoff top 50%** 决策 —— 这是贯穿全时间线的一条主线,详见 §5。 下文表格里的两类分数: - **Validation F1**:同 seed=202 切分下的 5-fold StratifiedKFold OOF F1(leak-free,由 `best_f1()` 取 PR 曲线 argmax)。 - **Public F1**:该阶段对应提交 CSV 在 Kaggle 公开榜的实际分数(口径不同,数值不必单调等于验证 F1)。 --- ## 2. 实验演进总表(可直接转论文结果表) 下表是本文档的核心产物,数字全部有原始 CSV 或 README/reports 来源,无来源处标 **not explicitly confirmed**。 | Stage | Method | Main idea | Validation F1 | Public F1 | Key lesson | |---|---|---|---|---|---| | 0 | 官方 notebook 基线 (`run_baseline.py`, legacy) | `HeteroMeanConv` + hinge/ranking,cosine 打分 + 阈值搜索 | ≈0.8850 | not explicitly confirmed | 简单 mean 聚合 + 简单负采样太弱;复杂非线性 GNN 在该任务上不如轻量 CF | | 1 | LightGCN 异构 CF 集成 | 纯加权邻居聚合(无 W 无非线性)+ BPR + 硬负采样,多 seed/维集成 | 0.93857648 | 0.93044 (6-model `sub_ens6_t0.36.csv`) | 去掉 MLP/非线性反而更稳更强;CF 范式天然贴合推荐任务 | | 2 | + 显式图 / meta-path stacking | 把 LightGCN 分数与手工图特征(A-A-P / A-P-P / A-P-A-P、度、common 邻居)拼成 ~76 维,LightGBM 5-fold OOF | 0.9559916871555707 | not explicitly confirmed (post95 ≈0.95760) | **单阶段最大增益 +0.0174**;meta-path 补上了 LightGCN 看不见的多跳结构盲区,真正突破 0.95 | | 3 | Post95 变体特征 stacker | 在 stack 基础上把 20 个 LightGCN 变体分数做 zscore+rank01 聚合(+top-k 内容相似度/负证据) | 0.9571095502729724 | ≈0.95760 | 同源分数的多变体聚合能稳定小幅提升;但单组特征(如 neg/topk)单独贡献微弱甚至微负 | | 4 | + Content mean-cos | 作者历史论文 embedding 均值 · 候选论文向量,作第 4 个分数源 | 0.9576285428276249 | — | 语义内容信号是 CF 之外的互补视角 | | 5 | + BPR-MF | 矩阵分解 BPR(dim=256)作独立 CF 分数源,纳入 84 维 X_base | 0.959308321934092 | ≈0.95996 | 矩阵分解与 LightGCN 互补,二者联合比任一单 CF 更强 | | 6 | + rich content (18 维, `feature.pkl`) | 直接从 512-d USE 论文嵌入抽 18 维作者/候选内容画像 | 0.95990 (figures / notes) | — | 深层内容画像进一步缩小 CF 盲区 | | 7 | + DeepWalk / Node2Vec | 在二部/合著/引用图上随机游走 + Word2Vec,产接近度嵌入,作新分数源 | 0.9621153367764501 (+node2vec) | ≈0.96252 | 随机游走捕获**全局**接近度,是 LightGCN 局部 CF 看不到的尺度 | | 8 | + 7 RW blocks 一致性聚合 | 系统 7 块游走配置(DeepWalk/Node2Vec, d128/d256, l40/l80),每块 11 维 + 11 维一致性聚合 | 0.9649210283178503 | not explicitly confirmed (r0.500 公开约 0.96229, **低于**上一阶段) | **关键教训**:验证 F1 随 RW 数量上升,但公开分反而略降 —— validation 对"堆更多同源模型"系统性偏乐观,过拟合 seed=202 | | 9 | + 高阶引用传播 (undirected → directed, 259 维) | $H_k = R\cdot C^k$(作者历史论文经 k 步引用扩散)+ $G_k = S\cdot R\cdot C^k$(合著者历史论文扩散),fwd/bwd/undir 三向 | **0.966873736337297** | **0.96626** | **最终方法**。显式建模"作者读过的论文被引用链路指向的候选论文",是有向、可解释的结构先验,把验证 0.9649 → 0.9669、公开 0.96252 → 0.96626 | 来源说明:Stage 1 验证分 `dynamic_summary.csv` 首行 `dyn202_l2d512_bpr_bigbatch_more=0.93857648`;Stage 2 `stack_threshold_summary.csv` seed202 行 `0.9559916871555707`;Stage 3–5 `extra_score_ablation.csv`(`post95_lgbm_baseline=0.9571095502729724` / `+content_mean_cos=0.9576285428276249` / `+bpr_mf=0.959308321934092`);Stage 6 rich content 18 维增益到 0.95990 据 reports/notes,缺独立 CSV 复核;Stage 7 `node2vec_deepwalk_ablation.csv`(`+node2vec=0.9621153367764501`);Stage 8 `ensemble_7_ablation.csv=0.9649210283178503`;Stage 9 `high_order_graph_stack/validation_summary.csv`(`rich_rw7_highorder_directed=0.966873736337297`)。公开分链条 0.93044 → 0.95760 → 0.95996 → 0.96252 → 0.96626 见 `README.md:42-46`。 --- ## 3. 三段大跃迁的第一性原理解读 ### 3.1 跃迁一:从"复杂 GNN"到"轻量 CF"(Stage 0→1,+0.054 验证) 早期我们试过官方 `HeteroMeanConv`、PyG `SAGEConv`+残差/layernorm/MLP 解码器、SAGEConv+BPR(见 `notes/experiment_history.md` §1–3,脚本 `run_baseline.py`/`run_improved.py`/`run_v2.py`,均为 legacy,硬编码 `/home/lzc` 路径)。直觉上"更强的网络应该更好",但验证分长期停在 0.885–0.93 区间。 切换到 LightGCN(`code/train_val_lgcn_ensemble.py:49-104`)是关键转折:它**删掉了所有权重矩阵和非线性**,只做入度归一的邻居加权聚合(`LightGCNLayer`),用点积解码、BPR 损失 + 硬负采样(`sample_hard_negatives` 混合 random/popular/coauthor-pool)。验证分直接跳到 0.9386。教训是:**推荐任务里,显式特征交互往往由后端的 LightGBM 来学,前端 GNN 越简洁、越不容易过拟合到稀疏图(密度仅 1.29e-3、56% 作者度=1)的噪声上**。 > 口径提醒(见事实表 #10):6-model 公开 0.93044(5×256d/4层 + 1×384d,早期 `sub_ens6_t0.36.csv`)与动态 best 验证 0.9386(2 seed × 512d/2层 dot,`dyn202_l2d512_bpr_bigbatch_more`)是**两个不同阶段产物**,不要混用。 ### 3.2 跃迁二:从"单分数"到"meta-path stacking"(Stage 1→2,+0.0174 验证) LightGCN 单分数卡在 0.939 左右。真正突破 0.95 的是把 LightGCN 分数和**手工图/meta-path 特征**一起喂给 LightGBM(`code/stack_rank_calibration.py`)。这些特征包括显式的多跳共同邻居:`A-A-P`(合著者共同论文)、`A-P-P`(作者论文→引用论文)、`A-P-A-P` 等,加上度、rank 校准(`add_rank_features` 产 score/global_rank/author_pct/author_rank 4 列)。 为什么这是单阶段最大增益?因为 LightGCN 学的是**局部、隐式**的 CF 表征,它看不见"我的合著者读过哪些论文""我读过的论文引用了哪些候选论文"这种**显式、可数、多跳**的结构事实。把这些手工特征堆到第二阶段,等于把"模型不确定的"和"图结构明确告诉你的"两条信息源交给一个 calibrator 联合裁决。验证分稳定跨 4 seed 都在 0.9555–0.9560(`exploration_summary.md` §4.3 表),说明这是真信号而非 seed 噪声。 > 一处冗余(见事实表 #13):`ExplicitGraphFeatures.transform` 中 `out[i,12]` 与 `out[i,3]` 字面重复(co_read_count),LightGBM 会自动忽略共线列;"18 维有效信息"实为 17 维独立。 ### 3.3 跃迁三:从"局部 CF"到"高阶有向引用传播"(Stage 8→9,+0.0020 验证 / +0.0037 公开) 最后一步是高阶引用传播(`code/high_order_graph_stack.py` 的 `build_high_order*`,硬编码稀疏矩阵形状 6611×79937)。核心两式: $$H_k = R \cdot C^k, \qquad G_k = S \cdot R \cdot C^k$$ 其中 $R$ 是作者-论文二部矩阵,$C$ 是有向引用邻接(fwd/bwd/undir 三向),$S$ 是合著邻接。直觉解读:$H_k$ 表示"作者历史论文经 $k$ 步引用扩散到候选论文"的强度,$G_k$ 把"合著者的历史论文"也扩散进来。这把"我读过的论文 → 它引用/被引用的论文 → 我的候选论文"这条**有向、可解释的语义链路**显式建模成特征。 `validation_summary.csv` 的四行消融清楚展示了这一增量: | stage | n_features | validation F1 | 增量 | |---|---|---|---| | base_highorder | 108 | 0.9642697338013148 | — | | rich_rw7 | 190 | 0.9649474248055991 | +0.000678 | | rich_rw7_highorder (+undirected, 24 维) | 214 | 0.9665557233547776 | +0.001608 | | **rich_rw7_highorder_directed (+directed, 45 维)** | **259** | **0.966873736337297** | +0.000318 | 关键观察:undirected(无向)贡献最大(+0.0016),directed(有向)再补 +0.0003,但**正是这 +0.0003 把公开分从 ≈0.96252 推到 0.96626**。方向性在这里不是锦上添花,而是抓住了"引用是被引→引用还是引用→被引"这种 LightGCN/无向聚合无法区分的真实因果结构。 --- ## 4. 两条贯穿全程的方法论教训 **教训 A:验证集与公开榜存在系统性偏差。** Stage 8 是最典型例子 —— 7 RW blocks 验证 F1 0.964921 比初版 Node2Vec 0.962115 高,但其 r0.500 公开分仅约 0.96229(reports 记载值,未在权威事实表核验;`exploration_summary.md` §16),**低于**上一阶段的 0.96252。原因:验证集是人为 1:1 平衡的,堆更多同源 RW 模型会过拟合到这个特定切分。这直接导致我们最终不信任"验证最优概率阈值"。 **教训 B:决策用 rank cutoff 而非概率阈值。** `validation_summary.csv` 给出的验证最优阈值是 0.46173080801963806,但 `threshold_submission_summary.csv` 显示:把这个阈值迁移到 test,正例比例漂移到 **0.5241947537735766**(应为 ~0.5 量级却多出 2.4 个百分点,对应 `changed_vs_anchor=55638` 对翻转)。改用 rank top 50%(`submission_summary.csv` 的 r0.500000 行)后,正例比例精确锁在 0.5000(`changed_vs_anchor=34863`)。决策规则再加一道 `cached_scores/test_known_mask.npy` 强制把 train/test 重叠的 524,083 条已知正边(占测试 25.6%)置 1。这套规则就是最终 0.96626 的来源。 --- ## 5. 流程图:从原始数据到最终提交 ``` 原始边 (data_and_docs/) │ 682,421 训练正边 / 2,047,262 测试对 / 9,663 合著 / 327,113 引用 / feature.pkl(79,937×512) ▼ make_notebook_style_split(seed=202) 验证集 (136,484 对, 1:1) │ ├── Stage 1: train_val_lgcn_ensemble.py → LightGCN 主分数 │ (0.9386 验证 / 0.93044 公开 6-model) ├── Stage 2: stack_rank_calibration.py → explicit18 + rank4 → LightGBM │ (0.9560 验证) ├── Stage 3-5: generate_post95 + extra_score_sources → variant43 + content_mean4 + bpr4 │ (0.9576 → 0.9593 验证 / ≈0.95996 公开) ├── Stage 6-7: content_rich + node2vec_deepwalk → rich18 + RW 初版 │ (0.9621 验证 / ≈0.96252 公开) ├── Stage 8: randomwalk_systematic → 7×11 RW + 11 聚合 │ (0.9649 验证, 但公开偏乐观) └── Stage 9: high_order_graph_stack.build_high_order* → undir24 + directed45 (0.9669 验证 / 0.96626 公开, 最终 259 维 LightGBM) │ ▼ rank top 50% + test_known_mask 强制 1 submission_rich_rw7_highorder_directed_r0.500000.csv ``` --- ## 6. 关于本时间线的可信度说明 - 所有"验证 F1"均可在 `validation_runs/dynamic_seed202/` 对应 CSV 中逐字核验(已在本审计中实测);唯一例外是 Stage 6 rich content 18 维的 0.95990,仅见 reports/figures caption,缺独立 CSV,据实标注。 - "公开 F1"来自 README/reports 与提交文件名;除最终 0.96626、6-model 0.93044 外,中间若干公开分(0.95760 / 0.95996 / 0.96252)为 README 记载的约值,**not explicitly confirmed** 到小数末位。 - `WORKSPACE_STATUS.md`(2026-06-17)曾称 `validation_runs/dynamic_seed202/` 等关键目录缺失,实测为误(目录均存在且含真实产物),本时间线以当前文件系统与 CSV 为准。 --- ## 可迁移到论文中的写法 以下表述可直接进入 ACM 中文论文的"实验/结果"章节,数值与口径与本仓库原始 CSV 完全一致。 **结果表(建议作为论文主表 Table 1):** > 我们在固定的 seed=202 验证切分(68,242 正例 + 68,242 随机负例,共 136,484 对)上以 5-fold StratifiedKFold OOF F1 评估每个阶段,并在 Kaggle 公开榜(测试集的 50%)验证泛化性。表 1 汇总了从启发式基线到最终方法的演进。最大单阶段增益来自显式 meta-path 堆叠(0.9386 → 0.9560,+0.0174),最终的高阶有向引用传播 $H_k=R\cdot C^k$、$G_k=S\cdot R\cdot C^k$ 在 259 维 LightGBM 二级 meta-learner 上取得验证 F1 = 0.96687、公开 F1 = 0.96626。 **方法叙述段(可直接进 Experiments):** > 实验演进可归纳为三段跃迁:(1) 从复杂异构 GNN 转向无权重、无非线性的 LightGCN(+0.054 验证),表明在极稀疏(密度 $1.29\times10^{-3}$)的长尾图上,轻量协同过滤优于过参数化编码器;(2) 引入显式多跳 meta-path 特征并与 LightGCN 分数联合送入 LightGBM(+0.0174 验证),补足了 CF 无法捕获的可数结构先验;(3) 提出高阶有向引用传播,将"作者历史论文经引用链扩散至候选"建模为稀疏矩阵幂 $R\cdot C^k$ 与 $S\cdot R\cdot C^k$,在 fwd/bwd/undir 三向特征上把验证 F1 由 0.9649 提升至 0.9669,公开 F1 由 0.96252 提升至 0.96626。 **决策规则段(可直接进 Implementation Details):** > 由于 1:1 平衡验证集与测试集存在分布偏移,验证最优概率阈值 0.4617 迁移至测试后正例率漂移至 0.5242。我们改用 rank cutoff:按预测分对测试对排序取 top 50% 为正,并将 524,083 条训练-测试重叠的已知正边(mask 占比 25.6%)强制置 1,得到稳定且校准的最终提交。 **消融表述(对应论文消融小节):** > 表 2 给出高阶传播的逐层消融:base(108 维)0.96427 → +rich_rw7(190 维)0.96495 → +无向高阶(214 维)0.96656 → +有向高阶(259 维)0.96687;其中无向传播贡献 +0.00161,有向传播贡献 +0.00032,AUC 由 0.99405 升至 0.99492,验证了方向性引用结构对推荐排序的增益。