| """Figure 3 — Performance evolution roadmap (hero figure). |
| |
| Two tracks — validation F1 (solid, filled) and public-leaderboard F1 (dashed, hollow) |
| — over the method stages, with the three conceptual leaps shaded and annotated. |
| """ |
| from pathlib import Path |
| import numpy as np |
| import matplotlib.pyplot as plt |
| from matplotlib.patches import FancyArrowPatch |
| from style import apply, save, PALETTE as C, COL2 |
|
|
| KEY = "fig3_roadmap" |
| TITLE = "Figure 3. Performance evolution roadmap" |
|
|
| |
| STAGES = [ |
| ("GNN\nbaseline", "BPR-MF backbone", 0.8850, None), |
| ("LightGCN\nensemble", "collaborative signal", 0.9386, 0.93044), |
| ("+ graph/meta-path\nstacking", "LightGBM stacker", 0.9560, 0.95760), |
| ("+ content\n+ BPR-MF", "semantic content", 0.9593, 0.95996), |
| ("+ DeepWalk\n/ Node2Vec", "random-walk embed.", 0.9621, 0.96252), |
| ("+ 7-block RW\nensemble", "diverse RW blocks", 0.9649, None), |
| ("+ high-order\npropagation","directed citation propagation", 0.9669, 0.96626), |
| ] |
|
|
|
|
| def make(root, out): |
| apply() |
| labels = [s[0] for s in STAGES] |
| modules = [s[1] for s in STAGES] |
| val = np.array([s[2] for s in STAGES]) |
| pub = np.array([s[3] for s in STAGES]) |
| x = np.arange(len(STAGES)) |
|
|
| fig, ax = plt.subplots(figsize=(COL2, 4.1)) |
|
|
| |
| for (x0, x1, c, t) in [(0, 1.5, C[7], "I · CF signal"), |
| (1.5, 4.5, C[1], "II · structural stacking"), |
| (4.5, 6.5, C[2], "III · high-order semantics")]: |
| ax.axvspan(x0, x1, color=c, alpha=0.06) |
| ax.text((x0 + x1) / 2, 0.984, t, ha="center", fontsize=7.5, color=c, fontweight="bold") |
|
|
| ax.plot(x, val, "-o", color=C[0], lw=1.9, ms=7, label="validation F1 (seed=202)", zorder=5) |
| xp = x[pub != None] |
| ax.plot(xp, pub[pub != None], "--s", color=C[3], lw=1.7, ms=6, mfc="white", |
| label="public-leaderboard F1", zorder=5) |
|
|
| |
| for xi, vi, m in zip(x, val, modules): |
| ax.annotate(m, (xi, vi), textcoords="offset points", xytext=(0, 12), |
| ha="center", fontsize=6.8, color="dimgray", rotation=0) |
|
|
| |
| leaps = [(1, 2, "+0.0174\nstacking"), (4, 5, "+0.0028\nRW blocks"), (5, 6, "+0.0020\nhigh-order")] |
| for a, b, txt in leaps: |
| y = val[a] - 0.012 |
| ax.add_patch(FancyArrowPatch((x[a], y), (x[b], y), arrowstyle="<->", color=C[2], lw=1.1)) |
| ax.text((x[a] + x[b]) / 2, y - 0.006, txt, ha="center", fontsize=6.8, color=C[2]) |
|
|
| |
| ax.plot(x[-1], val[-1], marker="*", ms=18, color="gold", mec=C[0], mew=0.8, zorder=6) |
| ax.annotate(f"final\n0.9669 val\n0.9663 public", (x[-1], val[-1]), |
| xytext=(-8, -34), textcoords="offset points", fontsize=7.2, ha="center", |
| fontweight="bold", color=C[0], |
| arrowprops=dict(arrowstyle="->", color=C[0], lw=0.9)) |
|
|
| ax.set_xticks(x); ax.set_xticklabels(labels, fontsize=7) |
| ax.set_ylabel("F1-score"); ax.set_ylim(0.875, 0.990) |
| ax.legend(loc="lower right", fontsize=7.5) |
| ax.set_title("Performance evolution: three leaps toward public F1 = 0.9663", fontsize=10) |
| save(fig, KEY, out) |
| return dict(key=KEY, title=TITLE, status="ok", |
| files=[f"{KEY}.pdf", f"{KEY}.png", f"{KEY}.svg"], |
| sources=["validation_runs/dynamic_summary.csv", "post95/extra/content/node2vec/high_order ablations", |
| "README public-leaderboard stage table"], |
| caption=( |
| "Performance evolution roadmap. Validation F1 (blue) and public-leaderboard F1 (green, " |
| "dashed) across the method stages. Three conceptual leaps are shaded: (I) LightGCN " |
| "captures the collaborative-filtering main signal; (II) explicit graph / meta-path features " |
| "with a LightGBM stacker fill the local-structure blind spot (+0.0174 val); (III) random-walk " |
| "embeddings and directed high-order citation propagation inject high-order structural " |
| "semantics. The final model — rich content + 7 random-walk blocks + directed high-order " |
| "propagation + LightGBM — reaches public F1 = 0.9663.")) |
|
|
|
|
| if __name__ == "__main__": |
| from style import ensure_dirs |
| r = make(Path("."), ensure_dirs(Path("."))) |
| print(r["key"], r["status"]) |
|
|