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

10 · 代码地图与入口脚本(System Code Map)

本文档是 code/ 目录的导航地图:30+ 个脚本各自干什么、谁是入口、谁是被运行时加载的"库"、谁是只能看不能跑的 legacy。读完本文你应当能在 30 秒内回答:"我想改 X,该动哪个文件?""我想重跑最终方法,敲哪条命令?""为什么没有 utils.py?" 代码定位以 docs_first_principles/_fact_sheet.md §6 为准(状态日期 2026-06-18)。路径相对仓库根,带行号。

目标读者:第一次进仓库、对"importlib 互相加载 + 无共享包"这套非典型工程组织感到困惑的人。先讲为什么这样组织(第一性原理),再给地图与入口


1. 第一性原理:为什么没有 utils.py,而是"脚本互加载"

1.1 典型工程 vs 本仓库

典型项目会把公共函数抽到 utils.py / common/ 包,其他模块 import本仓库没有。 它是研究/实验代码,追求"一个脚本跑完一个实验、改完立刻能跑"。于是演化出一种去中心化的代码复用:每个脚本用一段近乎相同的 load_module() helper 把同目录的另一个脚本当模块加载:

def load_module(name, path):
    spec = importlib.util.spec_from_file_location(name, path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

1.2 这样设计的后果(读者必须知道)

  • 没有显式接口/版本。函数签名、返回顺序全靠"加载方与被加载方的默契"。读下游脚本时,常需要跳到被加载脚本确认函数行为。
  • 两个脚本成了事实共享库(de-facto shared library),被 ~14–16 个脚本加载(见 §4)。改这两个文件的任何函数,等于同时改了十几个下游脚本的行为——blast radius 极大。
  • 每个脚本各自重定义本地 read_txt 等小工具,不统一。

这不是"坏味道"要重构,而是研究代码的合理权衡:快速试错优先于工程整洁。理解了这一点,才不会被"为什么这么乱"绊住,而能专注"数据怎么流"。


2. 脚本分类速览(四类)

类别 命名模式 角色 例子
训练 train_val_*.py Stage-1 模型训练 + 出验证/测试分数 train_val_lgcn_ensemble.py
特征/分数消融 *_ablation.py 单组特征验证 + 拼特征矩阵 randomwalk_systematic_ablation.py
submission 生成 generate_*submission.py 把分数/特征转成提交 CSV generate_post95_submission.py
stacking / 校准 / 搜索 stack_*score_level_*error_group_*search_*rich_randomwalk_stack.py 二阶段融合、阈值/比例搜索、误差分析 stack_rank_calibration.py
(存证) run_*.pycompare_gnn.py legacy,不可直接跑(见 §6) run_baseline.py 等 9 个

3. 三条核心入口命令

3.1 重跑最终方法(出 submission)

python code/high_order_graph_stack.py \
  --package-root . --split-seed 202 --seed 202 --n-splits 5 --make-submission
  • 命中全部缓存(feature_cache/randomwalk_systematic/models/、LightGCN 分数),CPU 秒级出 OOF + submission。
  • 不加 --make-submission = 仅验证。
  • 详见 03_data_flow_and_artifacts.md §9。

3.2 生成图表

本仓库有两套图表包(见记忆 figures-and-label-alignment):

# ACM 双栏正式版(3.25/6.75 in, pdf.fonttype=42),论文用
python figures_paper/scripts/make_all_figures.py --package-root .

# 早期 seaborn 版(更大字号),探索/汇报用
# (code/figures/ 下各 figN_*.py 独立可跑)

figures_paper/scripts/make_all_figures.py 自动建目录、逐图 try/except 隔离、读真实数据(缺失则优雅跳过)、生成 pdf/png/svg + README_figures.md单个图失败不会拖垮其余图。

3.3 编译论文 TeX

诚实说明(重要): 本仓库不包含论文 .tex 工程或编译命令docs_first_principles/SUMMARY_FOR_PAPER.md 提供的是可直接迁移进 TeX 的成稿段落、表格、公式、caption(中文,保留英文术语),以及建议的 \section 结构。实际编译发生在用户的独立论文项目里,把 SUMMARY 的段落复制进 .tex、按需改 \[ \] / equation 环境即可。本审计遵循"不覆盖/不改论文模板"的约束,未在仓库内增设任何 TeX 构建。


4. 两大事实共享库(改动 blast radius 最大)

被 ~14–16 个脚本经 load_module() 运行时加载,改它们 = 改全链:

4.1 train_val_lgcn_ensemble.py(数据 + 切分 + 评分)

函数/符号 行号 作用 为什么被到处加载
make_notebook_style_split 132-165 seed=202 切分 → 1:1 验证集(136,484 对) 所有下游都要用同一验证 pair/label
build_parts 168-236 加载 feature.pkl + 度特征 + popular 集 + coauthor_pool(L216 硬编码 range(6611)) 负采样/特征都依赖这些预计算
build_data 239-270 PyG HeteroData:4 边类型,引用/合著双向拼接,论文 515→embed 投影 LightGCN 训练数据装配
LightGCNLayer / encode / decode 49-104 纯加权邻居聚合(无 W 无非线性)+ 点积解码 模型本体
sample_hard_negatives 273-305 硬负采样 random50%/popular25%/coauthor25% 训练循环
best_f1 340-346 PR 曲线 argmax 最优 F1 阈值 + AUC 所有 stacker 算验证 F1 都用它
predict_scores 308-337 no_grad 批量打分(cos/dot/neg_l2) 推断

4.2 stack_rank_calibration.py(显式图特征 + rank + OOF)

函数/符号 行号 作用
ExplicitGraphFeatures.__init__ 54-106 默认 6611 作者 / 79937 论文;预计算 shared_paper_authors(A-P-A) + coauthor_paper_union(A-A-P)
ExplicitGraphFeatures.transform 108-145 18 维手工特征(L139 与 L130 重复一列,台账 #13)
add_rank_features 148-160 4 列:score/global_rank/author_pct/author_rank
fit_oof 163-184 5 折 StratifiedKFold;LGBM(1200, lr0.025, reg_lambda5)

关键事实:本文件全文无 scipy.sparse(台账 #12)——CLAUDE.md 所指的"sparse-matrix 高阶传播"在 high_order_graph_stack.py,在此文件。meta-path 全用 Python set 交/并集实现。

4.3 其他高频被加载的 peer

脚本 被加载的关键函数 用途
generate_post95_submission.py select_variant_val_scorestopk_content_similarity_fast 变体选择 + top-k 内容相似
post95_ablation.py negative_evidence_features 8 维负证据
extra_score_sources_ablation.py content_mean_scorescore_to_features content_mean-cos + BPR-MF
randomwalk_systematic_ablation.py build_base_featurespair_feature_block X_base 84 维 + 每块 11 维 RW
content_rich_ablation.py content_rich_features 从 feature.pkl 产 18 维 rich
generate_randomwalk_ensemble_submission.py aggregate 11 维 RW 一致性聚合

5. 最终入口:high_order_graph_stack.py 为什么是终点

它是唯一同时做三件事的脚本:① 拼接全部 259 维特征;② 训练最终 LightGBM OOF stacker;③ 出 submission。逻辑上它是 Stage-2 的实现 + 决策规则的实现,所以是"最终方法入口"。

关键符号 作用 备注
build_high_order* $H_k=R\cdot C^k$、$G_k=S\cdot R\cdot C^k$,fwd/bwd/undir scipy.sparse 幂乘 + top-k 剪枝(k=1500);硬编码稀疏矩阵形状 6611×79937(见 §7)
fit_full_predict LightGBM(num_leaves=15, reg_lambda=8, lr=0.022, n_est=1400) 5-fold OOF 最终超参(事实表 §2.7)
决策 sort → top 50% = 1 + test_known_mask.npy 强制已知正边 rank-cutoff(详见 08)

输出目录:validation_runs/dynamic_seed202/high_order_graph_stack/(validation_summary.csv*_oof.npy*_test_pred.npysubmissions/*.csv)。


6. Legacy 脚本:能看不能跑

以下 9 个是早期原型,硬编码 /home/lzc/cs3319-project 路径、无 argparse,仅作存证(provenance)。要跑必须先改路径,且功能已被现代 train_val_* / generate_* / *_ablation 取代——不要用它们复现结果:

run_baseline.py   run_improved.py   run_v2.py   run_final.py   run_ultimate.py
run_lgcn_final.py   run_lgcn_v2.py   run_graph_features.py   compare_gnn.py

它们的价值:记录"从复杂 GNN(HeteroMeanConv/SAGE)退回轻量 CF(LightGCN)"的方法演进史(见 09_experiment_timeline.md §3.1)。notes/experiment_history.md 有对应叙事。


7. 完整脚本地图表

"改动影响"列:🔴 共享库(改动影响十几个下游);🟡 多下游加载;🟢 独立入口(改了只影响自己);⚪ legacy(勿动)。 硬编码维度 6611(作者)/79937(论文)high_order_graph_stack.py(稀疏矩阵形状)与 train_val_lgcn_ensemble.py(L216 采样循环)字面出现,勿随手"重构"成变量(它们匹配本数据集,见 CLAUDE.md gotchas)。

脚本 角色 主要输入 主要输出 改动影响 备注
train_val_lgcn_ensemble.py LightGCN 训练 + 切分 + 评分 bipartite_train_ann.txtfeature.pkl scores/val_vanilla_ensemble_mean.npy、切分快照 🔴 两大共享库之一;best_f1/make_notebook_style_split 全链用
stack_rank_calibration.py 显式图/meta-path 特征 + rank + OOF LightGCN 分数、bipartite_train explicit18 + rank4 特征、fit_oof 🔴 两大共享库之一;无 sparse
high_order_graph_stack.py 最终 stacker + submission 全部 259 维特征(命中缓存) *_oof.npy*_test_pred.npysubmission_*.csv 🟢 最终入口(§3.1);有 sparse 幂乘
generate_post95_submission.py 变体选择 + top-k 内容 + submission LightGCN 变体分数、test 镜像 variant43、topk3、submission 🟡 select_variant_val_scores/topk_content_similarity_fast
post95_ablation.py 负证据特征 X_base 前 84 列 neg8 🟡 negative_evidence_features
extra_score_sources_ablation.py BPR-MF + content_mean-cos LightGCN 分数、feature.pkl val_mf_bpr_s202_d256.npy、content_mean 🟡 content_mean_score/score_to_features
content_rich_ablation.py rich content 画像 feature.pkl content_rich_*.npy(18 维,命中 feature_cache) 🟡 content_rich_features
randomwalk_systematic_ablation.py 7 块 DeepWalk/Node2Vec 异构图边 .model + pair_features/*.npz(11 维/块) 🟡 build_base_features/pair_feature_block;需 GPU/Word2Vec
generate_randomwalk_ensemble_submission.py RW 一致性聚合 + submission 7 块 pair 特征 aggregate 11 维、submission 🟡 aggregate()
score_level_*error_group_*search_* 阈值/比例搜索、误差分桶 OOF、各 submission 分析 CSV(出图用) 🟢 error_analysis_buckets.csv
rich_randomwalk_stack.py RW 专用 stacker RW 特征 RW 阶段 OOF/CSV 🟢 中间实验
generate_ens6_submission.py 早期 6-model ensemble submission checkpoints/ens6 6-model submission 🟢 0.93044 那版(早期)
run_*.pycompare_gnn.py(9 个) legacy 原型 硬编码 /home/lzc、无 argparse,勿直接跑(§6)

8. 常见问题速答

问题 答案
没有 utils.py 怎么共享代码? importlib.utilload_module() 运行时加载同目录脚本(§1)
改一个公共函数影响多大? train_val_lgcn_ensemble.py/stack_rank_calibration.py = 影响十几个下游(🔴);改入口脚本只影响自己(🟢)
为什么有的脚本没 argparse? 那是 run_*.py legacy,早期原型,已被现代脚本取代(§6)
最终方法入口是? high_order_graph_stack.py --make-submission(§3.1)
图表怎么出? figures_paper/scripts/make_all_figures.py --package-root .(§3.2)
论文 TeX 在哪编译? 仓库内没有 TeX 工程;SUMMARY_FOR_PAPER 提供可迁移段落,在用户独立项目编译(§3.3)
6611/79937 哪来的? 数据集固定规模,字面硬编码在两个脚本里,匹配本数据集勿重构(§7)

可迁移到论文中的写法

实现概述段(Implementation,可直译进 TeX)。 我们的系统由若干 Python 脚本组成,按"训练 / 特征消融 / 提交生成 / 堆叠校准"四类组织。其中两个脚本——train_val_lgcn_ensemble.py(数据装配、确定性验证切分、最优 F1 阈值)与 stack_rank_calibration.py(显式图与元路径特征、排名校准、out-of-fold 训练)——作为共享基础库,被约十余个下游脚本以运行时模块加载的方式复用,故其接口在整条流水线中保持一致。最终的两阶段堆叠与 rank-cutoff 决策由 high_order_graph_stack.py 实现:它将来自协同过滤、矩阵分解、随机游走、内容画像与有向高阶引用传播的全部 259 维特征横向拼接,以强正则 LightGBM 作二级元学习器融合。关键的高阶传播以稀疏矩阵幂乘实现($H_k=R,C^k$、$G_k=S,R,C^k$),并通过每次幂乘后的 top-k 行裁剪将 $|P|\times|P|$ 的稠密化控制在可计算规模。所有中间分数与特征均缓存于 validation_runs/ 下,并依确定性切分种子(seed=202)组织,保证实验可复现。