cs3319-project2 / figures_paper /scripts /fig5_ablation_highorder.py
NLP-beginner's picture
CS3319 Project 2 final deliverable (public F1 = 0.96626)
f28d994
Raw
History Blame Contribute Delete
4.41 kB
"""Figure 5 — Ablation of high-order propagation (waterfall).
Reads high_order_graph_stack/validation_summary.csv. A waterfall shows the incremental
validation-F1 gain of each stage; the directed-citation step is the decisive final lift.
A twin axis reports AUC. Falls back to reported numbers if the CSV is absent.
"""
from pathlib import Path
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from style import apply, save, PALETTE as C, COL1 # noqa: E402
KEY = "fig5_ablation_highorder"
TITLE = "Figure 5. Ablation of high-order propagation"
# fallback (order = display order, low->high complexity)
FALLBACK = pd.DataFrame({
"stage": ["base_highorder", "rich_rw7", "rich_rw7_highorder", "rich_rw7_highorder_directed"],
"validation_f1": [0.9642697338013148, 0.9649474248055991, 0.9665557233547776, 0.966873736337297],
"auc": [0.994052111749616, 0.9945549026665483, 0.9948903494937357, 0.9949182985645343],
"n_features": [108, 190, 214, 259],
})
LABEL = {
"base_highorder": "base + undirected\nhigh-order (108-d)",
"rich_rw7": "+ rich content +\n7 RW blocks (190-d)",
"rich_rw7_highorder": "+ undirected\nhigh-order (214-d)",
"rich_rw7_highorder_directed": "+ directed\ncitation prop. (259-d)",
}
def make(root, out):
apply()
csv = root / "validation_runs" / "dynamic_seed202" / "high_order_graph_stack" / "validation_summary.csv"
if csv.exists():
df = pd.read_csv(csv).set_index("stage")
status = "ok"
sources = [str(csv)]
else:
df = FALLBACK.set_index("stage")
status = "fallback"
sources = ["reported numbers (CSV missing)"]
order = ["base_highorder", "rich_rw7", "rich_rw7_highorder", "rich_rw7_highorder_directed"]
df = df.loc[order]
f1 = df.validation_f1.to_numpy()
auc = df.auc.to_numpy()
nf = df.n_features.to_numpy()
floor = f1.min() - (f1.max() - f1.min()) * 0.35
fig, ax = plt.subplots(figsize=(COL1 * 1.5, 4.0))
x = np.arange(len(order))
# base bar: full from floor
ax.bar(x[0], f1[0] - floor, bottom=floor, color=C[7], alpha=0.85, width=0.62)
ax.text(x[0], f1[0] + 0.0001, f"{f1[0]:.5f}", ha="center", fontsize=7, fontweight="bold")
# floating gain bars
for i in range(1, len(order)):
col = C[3] if i < len(order) - 1 else C[2] # highlight directed
ax.bar(x[i], f1[i] - f1[i - 1], bottom=f1[i - 1], color=col, alpha=0.85, width=0.62,
edgecolor=col, linewidth=1.2)
ax.plot([x[i - 1] + 0.31, x[i] - 0.31], [f1[i - 1], f1[i - 1]], color="gray", lw=0.7, ls=":")
gain = f1[i] - f1[i - 1]
ax.text(x[i], f1[i] + 0.00012, f"+{gain:.5f}", ha="center", fontsize=6.8,
color=col, fontweight="bold")
ax.text(x[i], f1[i - 1] + gain / 2, f"{f1[i]:.5f}", ha="center", fontsize=6.4, color="white")
ax.set_xticks(x)
ax.set_xticklabels([LABEL[s] for s in order], fontsize=6.6)
ax.set_ylabel("validation F1")
ax.set_ylim(floor, f1.max() + 0.0008)
# twin AUC
ax2 = ax.twinx()
ax2.plot(x, auc, "D-", color=C[4], ms=4.5, lw=1.2)
ax2.set_ylabel("AUC", color=C[4])
ax2.set_ylim(auc.min() - 0.0006, auc.max() + 0.0003)
ax2.tick_params(axis="y", labelcolor=C[4])
ax2.grid(False)
for xi, a in zip(x, auc):
ax2.text(xi, a + 0.00005, f"{a:.5f}", ha="center", fontsize=6.2, color=C[4])
ax.set_title("High-order propagation ablation (directed = final lift)", fontsize=9)
save(fig, KEY, out)
return dict(key=KEY, title=TITLE, status=status,
files=[f"{KEY}.pdf", f"{KEY}.png", f"{KEY}.svg"], sources=sources,
caption=(
"Ablation of high-order citation propagation (validation, seed=202). Bars are incremental "
"F1 gains over a broken-axis floor; diamonds show AUC. Adding rich content and the 7-block "
"random-walk ensemble lifts F1 by +0.00068; re-introducing undirected high-order propagation "
"gives the largest single jump (+0.00161); the directed citation-aware variant contributes "
"the decisive final +0.00032, reaching F1 = 0.96687 / AUC = 0.99492 and the public-best "
"0.9663. (Numbers from validation_summary.csv; fallback values used if the file is absent.)"))
if __name__ == "__main__":
from style import ensure_dirs
r = make(Path("."), ensure_dirs(Path(".")))
print(r["key"], r["status"])