§4.8 v2 variants: registered predictions, computed verdicts, families registered everywhere
Browse files- make_report.py +106 -8
make_report.py
CHANGED
|
@@ -20,7 +20,7 @@ REPO = "eac123/ptr-recurrent-depth"
|
|
| 20 |
PLOTS = "plots"
|
| 21 |
|
| 22 |
FAMILIES = ["fixed4x1", "rec1x4", "rec2x2", "rec1xdynamic", "rec4x4",
|
| 23 |
-
"fixed4-long", "rec1aligned"]
|
| 24 |
LABELS = {
|
| 25 |
"fixed4x1": "fixed-4 (4 blocks x 1)",
|
| 26 |
"rec1x4": "recurrent 1x4 (train R=4)",
|
|
@@ -29,9 +29,12 @@ LABELS = {
|
|
| 29 |
"rec4x4": "recurrent 4x4 (train R=4)",
|
| 30 |
"fixed4-long": "fixed-4 long (100k steps)",
|
| 31 |
"rec1aligned": "rec-1x-aligned (train R=k)",
|
|
|
|
|
|
|
| 32 |
}
|
| 33 |
TRAIN_ITERS = {"fixed4x1": 1, "rec1x4": 4, "rec2x2": 2, "rec1xdynamic": 8,
|
| 34 |
-
"rec4x4": 4, "fixed4-long": 1, "rec1aligned": None
|
|
|
|
| 35 |
|
| 36 |
|
| 37 |
def pull():
|
|
@@ -121,7 +124,7 @@ def plot_acc_vs_iters(evals, out,
|
|
| 121 |
|
| 122 |
def plot_perk_zoom(evals, out,
|
| 123 |
fams=("fixed4x1", "rec1x4", "rec1xdynamic", "rec4x4",
|
| 124 |
-
"rec1aligned"), kmax=16):
|
| 125 |
"""Within-and-just-beyond training range: accuracy per hop count k."""
|
| 126 |
fig, ax = plt.subplots(figsize=(8.5, 5))
|
| 127 |
ks = np.arange(1, kmax + 1)
|
|
@@ -144,6 +147,17 @@ def plot_perk_zoom(evals, out,
|
|
| 144 |
fig.tight_layout(); fig.savefig(out, dpi=150); plt.close(fig)
|
| 145 |
|
| 146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
def perk_rows(evals, fams, klo, khi):
|
| 148 |
ks = list(range(klo, khi + 1))
|
| 149 |
lines = ["| family | " + " | ".join(f"k={k}" for k in ks) + " |",
|
|
@@ -208,7 +222,8 @@ def plot_training(evals, out):
|
|
| 208 |
|
| 209 |
SHORT = {"fixed4x1": "fixed-4", "rec1x4": "rec-1x4", "rec2x2": "rec-2x2",
|
| 210 |
"rec1xdynamic": "rec-1x-dyn", "rec4x4": "rec-4x4",
|
| 211 |
-
"fixed4-long": "fixed-4-long", "rec1aligned": "rec-1x-aligned"
|
|
|
|
| 212 |
|
| 213 |
|
| 214 |
def plot_headline_bars(evals, out):
|
|
@@ -261,13 +276,15 @@ def plot_budget_map(evals, out):
|
|
| 261 |
OFF = {"fixed4x1": (8, -14, "left"), "rec1x4": (0, -16, "center"),
|
| 262 |
"rec2x2": (10, -4, "left"), "rec1xdynamic": (8, 8, "left"),
|
| 263 |
"rec4x4": (-8, 10, "right"), "fixed4-long": (8, -14, "left"),
|
| 264 |
-
"rec1aligned": (8, -16, "left")
|
|
|
|
| 265 |
for f in FAMILIES:
|
| 266 |
if not evals[f]:
|
| 267 |
continue
|
| 268 |
p, fl = budget_of(evals[f])
|
| 269 |
ax.scatter(p, fl, s=90, zorder=3,
|
| 270 |
-
marker="s" if f =
|
|
|
|
| 271 |
dx, dy, ha = OFF[f]
|
| 272 |
ax.annotate(LABELS[f], (p, fl), fontsize=7, ha=ha,
|
| 273 |
xytext=(dx, dy), textcoords="offset points")
|
|
@@ -509,6 +526,49 @@ def report(evals, rows):
|
|
| 509 |
f"{lm:.3f}, k=16 {le['acc_vs_k']['16']:.3f}, "
|
| 510 |
f"k=64 {le['acc_vs_k']['64']:.3f} -> {verdict}",
|
| 511 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 512 |
lines = [
|
| 513 |
"# Technical report: recurrent depth vs fixed depth on pointer chasing",
|
| 514 |
"",
|
|
@@ -695,10 +755,12 @@ def report(evals, rows):
|
|
| 695 |
"mappings per cell, mean over seeds):",
|
| 696 |
"",
|
| 697 |
*perk_rows(evals, ["fixed4x1", "rec1x4", "rec2x2", "rec1xdynamic",
|
| 698 |
-
"rec4x4", "fixed4-long", "rec1aligned"
|
|
|
|
| 699 |
"",
|
| 700 |
*perk_rows(evals, ["fixed4x1", "rec1x4", "rec2x2", "rec1xdynamic",
|
| 701 |
-
"rec4x4", "fixed4-long", "rec1aligned"
|
|
|
|
| 702 |
"",
|
| 703 |
"",
|
| 704 |
"",
|
|
@@ -790,6 +852,42 @@ def report(evals, rows):
|
|
| 790 |
"",
|
| 791 |
*al_lines,
|
| 792 |
"",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 793 |
"## 5. Conclusions",
|
| 794 |
"",
|
| 795 |
"1. **Recurrent depth buys learnability that neither parameters nor",
|
|
|
|
| 20 |
PLOTS = "plots"
|
| 21 |
|
| 22 |
FAMILIES = ["fixed4x1", "rec1x4", "rec2x2", "rec1xdynamic", "rec4x4",
|
| 23 |
+
"fixed4-long", "rec1aligned", "rec1m4", "rec1m4dense"]
|
| 24 |
LABELS = {
|
| 25 |
"fixed4x1": "fixed-4 (4 blocks x 1)",
|
| 26 |
"rec1x4": "recurrent 1x4 (train R=4)",
|
|
|
|
| 29 |
"rec4x4": "recurrent 4x4 (train R=4)",
|
| 30 |
"fixed4-long": "fixed-4 long (100k steps)",
|
| 31 |
"rec1aligned": "rec-1x-aligned (train R=k)",
|
| 32 |
+
"rec1m4": "rec-1x m4 (R=k+4, end-of-chain loss)",
|
| 33 |
+
"rec1m4dense": "rec-1x m4+dense (R=k+4, stem, dense loss)",
|
| 34 |
}
|
| 35 |
TRAIN_ITERS = {"fixed4x1": 1, "rec1x4": 4, "rec2x2": 2, "rec1xdynamic": 8,
|
| 36 |
+
"rec4x4": 4, "fixed4-long": 1, "rec1aligned": None,
|
| 37 |
+
"rec1m4": None, "rec1m4dense": None}
|
| 38 |
|
| 39 |
|
| 40 |
def pull():
|
|
|
|
| 124 |
|
| 125 |
def plot_perk_zoom(evals, out,
|
| 126 |
fams=("fixed4x1", "rec1x4", "rec1xdynamic", "rec4x4",
|
| 127 |
+
"rec1aligned", "rec1m4", "rec1m4dense"), kmax=16):
|
| 128 |
"""Within-and-just-beyond training range: accuracy per hop count k."""
|
| 129 |
fig, ax = plt.subplots(figsize=(8.5, 5))
|
| 130 |
ks = np.arange(1, kmax + 1)
|
|
|
|
| 147 |
fig.tight_layout(); fig.savefig(out, dpi=150); plt.close(fig)
|
| 148 |
|
| 149 |
|
| 150 |
+
def frontier(m, thr=0.9):
|
| 151 |
+
"""Last k with mean accuracy >= thr (scanning from k=1)."""
|
| 152 |
+
best = 0
|
| 153 |
+
for k in range(1, len(m) + 1):
|
| 154 |
+
if m[k - 1] >= thr:
|
| 155 |
+
best = k
|
| 156 |
+
else:
|
| 157 |
+
break
|
| 158 |
+
return best
|
| 159 |
+
|
| 160 |
+
|
| 161 |
def perk_rows(evals, fams, klo, khi):
|
| 162 |
ks = list(range(klo, khi + 1))
|
| 163 |
lines = ["| family | " + " | ".join(f"k={k}" for k in ks) + " |",
|
|
|
|
| 222 |
|
| 223 |
SHORT = {"fixed4x1": "fixed-4", "rec1x4": "rec-1x4", "rec2x2": "rec-2x2",
|
| 224 |
"rec1xdynamic": "rec-1x-dyn", "rec4x4": "rec-4x4",
|
| 225 |
+
"fixed4-long": "fixed-4-long", "rec1aligned": "rec-1x-aligned",
|
| 226 |
+
"rec1m4": "m4", "rec1m4dense": "m4+dense"}
|
| 227 |
|
| 228 |
|
| 229 |
def plot_headline_bars(evals, out):
|
|
|
|
| 276 |
OFF = {"fixed4x1": (8, -14, "left"), "rec1x4": (0, -16, "center"),
|
| 277 |
"rec2x2": (10, -4, "left"), "rec1xdynamic": (8, 8, "left"),
|
| 278 |
"rec4x4": (-8, 10, "right"), "fixed4-long": (8, -14, "left"),
|
| 279 |
+
"rec1aligned": (8, -16, "left"),
|
| 280 |
+
"rec1m4": (8, 8, "left"), "rec1m4dense": (8, -16, "left")}
|
| 281 |
for f in FAMILIES:
|
| 282 |
if not evals[f]:
|
| 283 |
continue
|
| 284 |
p, fl = budget_of(evals[f])
|
| 285 |
ax.scatter(p, fl, s=90, zorder=3,
|
| 286 |
+
marker="s" if f != "rec1x4" and f.startswith("rec1")
|
| 287 |
+
else "o")
|
| 288 |
dx, dy, ha = OFF[f]
|
| 289 |
ax.annotate(LABELS[f], (p, fl), fontsize=7, ha=ha,
|
| 290 |
xytext=(dx, dy), textcoords="offset points")
|
|
|
|
| 526 |
f"{lm:.3f}, k=16 {le['acc_vs_k']['16']:.3f}, "
|
| 527 |
f"k=64 {le['acc_vs_k']['64']:.3f} -> {verdict}",
|
| 528 |
]
|
| 529 |
+
|
| 530 |
+
# --- v2 aligned variants (stage 5) ---
|
| 531 |
+
def variant_lines(fam, stair_margin=4):
|
| 532 |
+
ev = evals[fam]
|
| 533 |
+
if not ev:
|
| 534 |
+
return [f"*(stage-5 runs not finished: {fam})*"]
|
| 535 |
+
acc = np.array([[e["acc_vs_k"][str(k)] for k in range(1, 17)]
|
| 536 |
+
for e in ev])
|
| 537 |
+
m = acc.mean(0)
|
| 538 |
+
fm = frontier(m)
|
| 539 |
+
hi, lo = [], []
|
| 540 |
+
for e in ev:
|
| 541 |
+
for r, cells in e["acc_vs_iters"].items():
|
| 542 |
+
for kk, v in cells.items():
|
| 543 |
+
(hi if int(kk) <= int(r) - stair_margin else lo).append(v)
|
| 544 |
+
st = float(np.mean(hi) - np.mean(lo))
|
| 545 |
+
return [
|
| 546 |
+
f"- in-distribution (k=1..8 mean): {ms(fam, id_mean)}",
|
| 547 |
+
f"- frontier (last k with >=0.9 mean acc): **k={fm}**",
|
| 548 |
+
f"- k=16 {m[15]:.3f}, k=32 "
|
| 549 |
+
f"{np.mean([e['acc_vs_k']['32'] for e in ev]):.3f}, k=64 "
|
| 550 |
+
f"{np.mean([e['acc_vs_k']['64'] for e in ev]):.3f}",
|
| 551 |
+
f"- staircase check (acc high iff k <= R-{stair_margin}): "
|
| 552 |
+
f"{st:+.3f}",
|
| 553 |
+
]
|
| 554 |
+
|
| 555 |
+
al2_lines = (["**A / m4 (margin, end-of-chain loss):**"]
|
| 556 |
+
+ variant_lines("rec1m4")
|
| 557 |
+
+ ["", "**B / m4+dense (margin + stem + dense):**"]
|
| 558 |
+
+ variant_lines("rec1m4dense"))
|
| 559 |
+
if evals["rec1m4"] and evals["rec1m4dense"]:
|
| 560 |
+
p1 = np.mean([id_mean(e) for e in evals["rec1m4"]]) < 0.5
|
| 561 |
+
mB = float(np.array([[e["acc_vs_k"][str(k)] for k in range(1, 9)]
|
| 562 |
+
for e in evals["rec1m4dense"]]).mean())
|
| 563 |
+
mBm = np.array([[e["acc_vs_k"][str(k)] for k in range(1, 17)]
|
| 564 |
+
for e in evals["rec1m4dense"]]).mean(0)
|
| 565 |
+
p2 = mB >= 0.9
|
| 566 |
+
p3 = frontier(mBm) >= 12
|
| 567 |
+
al2_lines += [
|
| 568 |
+
f"- (P1) {('**supported**' if p1 else '**not supported**')}",
|
| 569 |
+
f"- (P2) {('**supported**' if p2 else '**not supported**')}",
|
| 570 |
+
f"- (P3) {('**supported**' if p3 else '**not supported**')}",
|
| 571 |
+
]
|
| 572 |
lines = [
|
| 573 |
"# Technical report: recurrent depth vs fixed depth on pointer chasing",
|
| 574 |
"",
|
|
|
|
| 755 |
"mappings per cell, mean over seeds):",
|
| 756 |
"",
|
| 757 |
*perk_rows(evals, ["fixed4x1", "rec1x4", "rec2x2", "rec1xdynamic",
|
| 758 |
+
"rec4x4", "fixed4-long", "rec1aligned", "rec1m4",
|
| 759 |
+
"rec1m4dense"], 1, 8),
|
| 760 |
"",
|
| 761 |
*perk_rows(evals, ["fixed4x1", "rec1x4", "rec2x2", "rec1xdynamic",
|
| 762 |
+
"rec4x4", "fixed4-long", "rec1aligned", "rec1m4",
|
| 763 |
+
"rec1m4dense"], 9, 16),
|
| 764 |
"",
|
| 765 |
"",
|
| 766 |
"",
|
|
|
|
| 852 |
"",
|
| 853 |
*al_lines,
|
| 854 |
"",
|
| 855 |
+
"### 4.8 v2 aligned variants: margin R = k+4, stem layer, dense supervision",
|
| 856 |
+
"",
|
| 857 |
+
"The 4.7 diagnosis says the R=k objective fails because of (i) exact",
|
| 858 |
+
"halting at depth k, and (ii) no per-step signal. The v2 variants",
|
| 859 |
+
"(user design) target both, separately:",
|
| 860 |
+
"",
|
| 861 |
+
"- **A / m4** (3 seeds): R = k + 4 with end-of-chain supervision",
|
| 862 |
+
" only. The margin removes exact halting - the model needs 'at",
|
| 863 |
+
" least k unrolls', and can idle for the last 4. Everything else",
|
| 864 |
+
" identical to rec-1x4.",
|
| 865 |
+
"- **B / m4+dense** (3 seeds): R = k + 4, one untied stem block",
|
| 866 |
+
" before the loop, and dense supervision - every unroll t gets an",
|
| 867 |
+
" auxiliary head predicting f^{min(t,k)}(x), i.e. each unroll does",
|
| 868 |
+
" its own hop and then holds. This is the per-step signal the R=k",
|
| 869 |
+
" run lacked, and it acts as a curriculum: unroll 1 only has to",
|
| 870 |
+
" learn the lookup, later unrolls inherit it.",
|
| 871 |
+
"",
|
| 872 |
+
"At evaluation both read out at R = k + 4 for every k - the trained",
|
| 873 |
+
"inference mode - including k > 8.",
|
| 874 |
+
"",
|
| 875 |
+
"*Predictions registered before the runs:*",
|
| 876 |
+
"",
|
| 877 |
+
"- (P1) margin alone does **not** rescue learning (the credit-",
|
| 878 |
+
" assignment problem, not exact halting, was the blocker); it",
|
| 879 |
+
" should learn faster than R=k but plateau low.",
|
| 880 |
+
"- (P2) dense + stem **does** rescue: >= 0.9 mean accuracy across",
|
| 881 |
+
" k = 1..8, and a fixed-R sweep showing a staircase (accuracy high",
|
| 882 |
+
" iff k <= R - 4) rather than a peak at one R.",
|
| 883 |
+
"- (P3) if the fix is real, B extends beyond k=8 better than every",
|
| 884 |
+
" existing family (R = k+4 is its trained mode at any k), bounded",
|
| 885 |
+
" by OOD step embeddings (unroll indices > 11 were never trained).",
|
| 886 |
+
"",
|
| 887 |
+
"*Results:*",
|
| 888 |
+
"",
|
| 889 |
+
*al2_lines,
|
| 890 |
+
"",
|
| 891 |
"## 5. Conclusions",
|
| 892 |
"",
|
| 893 |
"1. **Recurrent depth buys learnability that neither parameters nor",
|