summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorhaoyuren <13851610112@163.com>2026-07-28 07:53:50 +0800
committerhaoyuren <13851610112@163.com>2026-07-28 07:53:50 +0800
commit99b95217a30f75e9e35b5044850ced4ab802bc87 (patch)
tree9aca82fc9f9165b6e7585eefe1061956638462aa /scripts
parente65dd2e2460b1da48c83a930304cf9b269fc4447 (diff)
Redesign main figure panels and track figure-source CSVs
Panel 1a: rename to Backward rules; DFA drawn as direct side-to-side arrows from the error to each hidden layer. Panel 1b: replace the dual y-axis with stacked guarantee/cost sub-axes. Figure 2: reader-facing panel titles; legend renames (linear extrapolation, relinearization). Track the regenerated figure-source CSVs (verified bit-exact against the frozen paper numbers) so figures can be rebuilt without rerunning experiments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'scripts')
-rw-r--r--scripts/plot_aaai_main_figures.py88
1 files changed, 56 insertions, 32 deletions
diff --git a/scripts/plot_aaai_main_figures.py b/scripts/plot_aaai_main_figures.py
index cd12011..f0eb0c3 100644
--- a/scripts/plot_aaai_main_figures.py
+++ b/scripts/plot_aaai_main_figures.py
@@ -46,7 +46,7 @@ def draw_schematic(ax: plt.Axes) -> None:
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.axis("off")
- rows = [(0.82, "BP", "#2f6f9f"), (0.50, "FA", "#c65f16"), (0.18, "DFA", "#2b8a3e")]
+ rows = [(0.84, "BP", "#2f6f9f"), (0.54, "FA", "#c65f16"), (0.16, "DFA", "#2b8a3e")]
x_positions = [0.18, 0.42, 0.66, 0.90]
labels = ["$h_1$", "$h_2$", "$h_3$", "error"]
for y, name, color in rows:
@@ -65,15 +65,21 @@ def draw_schematic(ax: plt.Axes) -> None:
)
ax.text((left + right) / 2, y + 0.035, arrow_label, ha="center", fontsize=8)
else:
+ rads = {x_positions[0]: 0.26, x_positions[1]: 0.32, x_positions[2]: 0.0}
for left in x_positions[:-1]:
ax.annotate(
"",
- xy=(left + 0.04, y),
- xytext=(x_positions[-1] - 0.04, y),
- arrowprops=dict(arrowstyle="->", color=color, lw=1.2, alpha=0.9),
+ xy=(left + 0.045, y),
+ xytext=(x_positions[-1] - 0.045, y),
+ arrowprops=dict(
+ arrowstyle="->",
+ color=color,
+ lw=1.4,
+ connectionstyle=f"arc3,rad={rads[left]}",
+ ),
)
- ax.text(0.56, y + 0.055, "direct random maps", ha="center", fontsize=8)
- ax.set_title("Fixed random feedback rules", fontsize=10)
+ ax.text(0.54, y - 0.145, "direct random maps", ha="center", fontsize=8)
+ ax.set_title("Backward rules", fontsize=10)
def figure_one(args: argparse.Namespace) -> Path:
@@ -83,45 +89,58 @@ def figure_one(args: argparse.Namespace) -> Path:
theory_rows = read_csv(args.null_dir / "theory_gap_summary.csv")
empirical_rows = read_csv(args.null_dir / "empirical_gap_summary.csv")
- fig, axes = plt.subplots(1, 3, figsize=(14.2, 4.1), dpi=220)
- draw_schematic(axes[0])
- panel_label(axes[0], "a")
+ fig = plt.figure(figsize=(14.2, 4.7), dpi=220)
+ gs = fig.add_gridspec(2, 3, height_ratios=[1.0, 1.5], hspace=0.14, wspace=0.28)
+ ax_schem = fig.add_subplot(gs[:, 0])
+ ax_top = fig.add_subplot(gs[0, 1])
+ ax_bottom = fig.add_subplot(gs[1, 1], sharex=ax_top)
+ ax_cal = fig.add_subplot(gs[:, 2])
+
+ draw_schematic(ax_schem)
+ panel_label(ax_schem, "a")
- ax = axes[1]
theory_by_width = {int(row["width"]): float(row["mean"]) for row in theory_rows}
empirical_by_width = {int(row["width"]): float(row["mean"]) for row in empirical_rows}
widths = sorted(set(theory_by_width) & set(empirical_by_width))
- ax.plot(
+
+ ax_top.plot(widths, [1.0 / (width * width) for width in widths], "k--", lw=1.4, label="$1/D$")
+ ax_top.set_yscale("log")
+ ax_top.set_ylabel("guaranteed\nalignment", fontsize=8)
+ ax_top.grid(alpha=0.16, which="both")
+ ax_top.legend(fontsize=7, loc="upper right")
+ ax_top.tick_params(labelbottom=False)
+ ax_top.set_title("Matrix mismatch is not optimization cost", fontsize=10)
+ panel_label(ax_top, "b")
+
+ ax_bottom.plot(
widths,
[theory_by_width[width] for width in widths],
"o-",
color="#8c6d31",
label="random-direction deletion",
)
- ax.plot(
+ ax_bottom.plot(
widths,
[empirical_by_width[width] for width in widths],
"s-",
color="#c65f16",
label="measured FA cost",
)
- ax.set_yscale("log")
- ax.set_xlabel("width")
- ax.set_ylabel("finite-time loss gap")
- ax.grid(alpha=0.16, which="both")
- ax2 = ax.twinx()
- ax2.plot(widths, [1.0 / (width * width) for width in widths], "k--", lw=1.4, label="$1/D$")
- ax2.set_yscale("log")
- ax2.set_ylabel("best guaranteed squared alignment")
- handles1, labels1 = ax.get_legend_handles_labels()
- handles2, labels2 = ax2.get_legend_handles_labels()
- ax.legend(handles1 + handles2, labels1 + labels2, fontsize=7, loc="upper right")
+ ax_bottom.set_yscale("log")
+ ax_bottom.set_xlabel("width")
+ ax_bottom.set_ylabel("finite-time loss gap", fontsize=8)
+ ax_bottom.grid(alpha=0.16, which="both")
+ ax_bottom.legend(fontsize=7, loc="upper right")
worst_ratio = max(theory_by_width[width] / empirical_by_width[width] for width in widths)
- ax.text(0.04, 0.05, f"null overestimate: up to {worst_ratio:.0f}×", transform=ax.transAxes, fontsize=8)
- ax.set_title("Matrix mismatch is not optimization cost", fontsize=10)
- panel_label(ax, "b")
+ ax_bottom.text(
+ 0.04,
+ 0.07,
+ f"null overestimate: up to {worst_ratio:.0f}×",
+ transform=ax_bottom.transAxes,
+ fontsize=8,
+ )
- ax = axes[2]
+ ax = ax_cal
depths = sorted({int(row["depth"]) for row in init_rows})
cmap = plt.cm.viridis
depth_color = {depth: cmap(index / max(len(depths) - 1, 1)) for index, depth in enumerate(depths)}
@@ -254,22 +273,27 @@ def figure_two(args: argparse.Namespace) -> Path:
ax.errorbar(depths, means, yerr=sems, marker=marker, capsize=2, label=f"width {width}")
ax.set_xlabel("hidden-layer depth")
ax.set_ylabel("FA loss - BP loss")
- ax.set_title("Later cost changes with training dynamics", fontsize=10)
+ ax.set_title("Finite time cost under a shared training schedule", fontsize=10)
ax.grid(alpha=0.16)
ax.legend(fontsize=7)
panel_label(ax, "a")
- scatter_predictor(axes[1], rows, [("fixed_gap", "^", "frozen $K(0)$")], "Frozen initialization misses drift")
+ scatter_predictor(
+ axes[1],
+ rows,
+ [("fixed_gap", "^", "frozen $K(0)$")],
+ "Freezing the initialization operators\nmisses their evolution",
+ )
panel_label(axes[1], "b")
scatter_predictor(
axes[2],
rows,
[
- ("velocity_gap", "o", "linear velocity"),
- ("retangent_gap", "s", "early retangent"),
+ ("velocity_gap", "o", "linear extrapolation"),
+ ("retangent_gap", "s", "relinearization"),
],
- "Two snapshots recover the later cost",
+ "Two operator measurements\nrecover the later cost",
)
panel_label(axes[2], "c")