claude-opus-4-8
nano-gpt-data-curation — graded by the task's verifier against a measured baseline.
- backend
- modal
- variant
- claude claude-opus-4-8
- metric
- held-out multi-domain perplexity
- actions
- 97
- duration
- 1h 16m
- recorded
- 2026-08-04
run iddclm-nano-curation__xJxXGaB
Everything claude-opus-4-8 did on this task, in order.
download JSON ↗encrypted chain-of-thought — recorded, not exposed
args
{
"command": "cd /workspace; sleep 90; cat /tmp/diag.log",
"description": "Read per-register diagnostic"
}Bash result
Traceback (most recent call last):
File "/tmp/diag.py", line 2, in <module>
from model import GPT, GPTConfig
ModuleNotFoundError: No module named 'model'
[stdout]
Traceback (most recent call last):
File "/tmp/diag.py", line 2, in <module>
from model import GPT, GPTConfig
ModuleNotFoundError: No module named 'model'
[metadata] {"noOutputExpected": false}encrypted chain-of-thought — recorded, not exposed
args
{
"command": "cd /workspace; nohup python3 /tmp/diag.py > /tmp/diag.log 2>&1 & echo \"diag pid $!\"; sleep 95; cat /tmp/diag.log",
"description": "Run diagnostic from workspace"
}Bash result
diag pid 4911
[1]+ Exit 1 nohup python3 /tmp/diag.py > /tmp/diag.log 2>&1
Traceback (most recent call last):
File "/tmp/diag.py", line 2, in <module>
from model import GPT, GPTConfig
ModuleNotFoundError: No module named 'model'
[stdout]
diag pid 4911
[1]+ Exit 1 nohup python3 /tmp/diag.py > /tmp/diag.log 2>&1
Traceback (most recent call last):
File "/tmp/diag.py", line 2, in <module>
from model import GPT, GPTConfig
ModuleNotFoundError: No module named 'model'
[metadata] {"noOutputExpected": false}encrypted chain-of-thought — recorded, not exposed
args
{
"command": "cd /workspace; PYTHONPATH=/workspace nohup python3 /tmp/diag.py > /tmp/diag.log 2>&1 & echo \"pid $!\"; sleep 100; cat /tmp/diag.log",
"description": "Run diagnostic with PYTHONPATH"
}Bash result
pid 5044
[1]+ Done PYTHONPATH=/workspace nohup python3 /tmp/diag.py > /tmp/diag.log 2>&1
wiki PPL 612.2
prose PPL 273.5
news PPL 280.0
QA PPL 228.5
[stdout]
pid 5044
[1]+ Done PYTHONPATH=/workspace nohup python3 /tmp/diag.py > /tmp/diag.log 2>&1
wiki PPL 612.2
prose PPL 273.5
news PPL 280.0
QA PPL 228.5
[metadata] {"noOutputExpected": false}Big insight: wiki quarter PPL = 612 vs prose/news/QA ~230–280. The encyclopedic register dominates the average. The dev wiki text is WikiText-103 format (spaces around punctuation, @,@/@-@ tokens) — a format my raw-web pool likely lacks. Let me check if the pool contains any WikiText-format docs I could prioritize.
args
{
"command": "cd /workspace; echo \"docs with @,@ or @-@ :\"; grep -c '@[,-]@' data/pool.jsonl; echo \"docs with ' , ' spaced-comma style (sample count in first 40k):\"; head -40000 data/pool.jsonl | grep -cE ' , | \\. '",
"description": "Check pool for WikiText formatting"
}Bash result
docs with @,@ or @-@ :
0
docs with ' , ' spaced-comma style (sample count in first 40k):
2011
[stdout]
docs with @,@ or @-@ :
0
docs with ' , ' spaced-comma style (sample count in first 40k):
2011
[metadata] {"noOutputExpected": false}No @,@ docs, but ~5% of the pool uses WikiText-style spaced punctuation ( , . ). Since the hidden target is the same domain/preprocessing, prioritizing spaced-punctuation docs for the wiki register should directly cut that 612. Let me find these docs and check their content.
args
{
"command": "cd /workspace; python3 -c \"\nimport json,re\nWORD=re.compile(r\\\"[a-z0-9']+\\\")\ndef wt(t):\n sp=t.count(' , ')+t.count(' . ')\n norm=t.count(',')+t.count('.')\n return sp/norm if norm>10 else 0\nrows=[]\nwith open('data/pool.jsonl') as f:\n for line in f:\n r=json.loads(line); s=wt(r['text'])\n if s>0.6 and len(r['text'])>400: rows.append((s,r['id'],r['text']))\nrows.sort(reverse=True)\nprint('spaced-punct docs (wt>0.6, len>400):',len(rows))\nfor s,i,t in rows[:6]:\n print('wt%.2f id%d ::'%(s,i), t[:120].replace(chr(10),' '))\n\"",
"description": "Find and inspect WikiText-style docs"
}Bash result
spaced-punct docs (wt>0.6, len>400): 60
wt1.00 id164967 :: Login How it Works How To Pathways Latest Replays About us Blog Who We Are Join the Team Website Terms of Use Privacy P
wt1.00 id36051 :: <|endoftext|>monsters inc t shirt as skirt of generation that eider down takes) of the leg ministry is gotten , O word m
wt0.98 id181927 :: free chunk of my best selling Bounce Back Book! x<|endoftext|>2000 379 Peterbilt Bedradings Schema Free Picture - Auto
wt0.98 id175090 :: com<|endoftext|>97 Camaro Fuse Box - Auto Electrical Wiring Diagram Wiring Diagram Home 97 camaro fuse box Wiring Diagra
wt0.98 id172730 :: Schema Cablage - Auto Electrical Wiring Diagram Wiring Diagram Home 1990 cutlass supreme schema cablage Wiring Diagram
wt0.98 id157731 :: +Contact<|endoftext|>2001 Lexus Is300 Schema Cablage - Auto Electrical Wiring Diagram Wiring Diagram Home 2001 lexus is
[stdout]
spaced-punct docs (wt>0.6, len>400): 60
wt1.00 id164967 :: Login How it Works How To Pathways Latest Replays About us Blog Who We Are Join the Team Website Terms of Use Privacy P
wt1.00 id36051 :: <|endoftext|>monsters inc t shirt as skirt of generation that eider down takes) of the leg ministry is gotten , O word m
wt0.98 id181927 :: free chunk of my best selling Bounce Back Book! x<|endoftext|>2000 379 Peterbilt Bedradings Schema Free Picture - Auto
wt0.98 id175090 :: com<|endoftext|>97 Camaro Fuse Box - Auto Electrical Wiring Diagram Wiring Diagram Home 97 camaro fuse box Wiring Diagra
wt0.98 id172730 :: Schema Cablage - Auto Electrical Wiring Diagram Wiring Diagram Home 1990 cutlass supreme schema cablage Wiring Diagram
wt0.98 id157731 :: +Contact<|endoftext|>2001 Lexus Is300 Schema Cablage - Auto Electrical Wiring Diagram Wiring Diagram Home 2001 lexus is
[metadata] {"noOutputExpected": false}Only 60 spaced-punctuation docs exist and they're spam — the pool has no genuine WikiText-format encyclopedic content, so the wiki format mismatch is largely irreducible by selection. The key open question: does the wiki quarter even respond to encyclopedic training data, or is it format-locked? If format-locked, I should reallocate budget away from wiki. Let me build a fast harness to test different register weightings.
args
{
"command": "cat /workspace/.timer/remaining_secs",
"description": "Check time"
}Bash result
2035
[stdout]
2035
[metadata] {"noOutputExpected": false}34 min, ~2-3 cycles left. Let me build a fast harness that caches the gate mask so I can test weightings instantly.
args
{
"command": "cat > /tmp/wsel.py <<'PY'\nimport json,re,sys,zlib,os\nimport numpy as np\nfrom collections import Counter\nfrom transformers import AutoTokenizer\nK=1<<20; WORD=re.compile(r\"[a-z0-9']+\"); NREG=4\nids,texts=[],[]\nwith open('/workspace/data/pool.jsonl') as f:\n for line in f:\n r=json.loads(line); ids.append(r['id']); texts.append(r['text'])\nN=len(ids); ids=np.array(ids)\nS=np.load('/tmp/curate_scores4.npy')\n# build COMMON_BG + keep mask once, cache\nif os.path.exists('/tmp/keep.npy'):\n keep=np.load('/tmp/keep.npy')\nelse:\n tok=AutoTokenizer.from_pretrained('gpt2'); dev=np.load('/workspace/data/multi_dev.npy')\n chunks,cur=[],[]\n for t in dev.tolist():\n if t==50256:\n if cur: chunks.append(tok.decode(cur)); cur=[]\n else: cur.append(t)\n chunks=[c for c in chunks if len(c)>200]\n rng=np.random.default_rng(1337); neg=[texts[i] for i in rng.choice(N,6000,replace=False)]\n _bg=Counter()\n for c in chunks+neg:\n w=WORD.findall(c.lower())\n for j in range(len(w)-1): _bg[(w[j],w[j+1])]+=1\n CB=frozenset(k for k,v in _bg.items() if v>=2)\n STOP=set(\"the of and to a in is that it for on as with was are be by this at from or an not but have has had he she they we you i his her their which who will would can could been were said your all more one about when what there if so no do does did than then them these those into out up down over after also its our\".split())\n def passes(t):\n if len(t)<300: return False\n ws=WORD.findall(t.lower())\n if len(ws)<60: return False\n if sum(c.isalpha() or c.isspace() for c in t)/len(t)<0.70: return False\n if sum(c.isdigit() for c in t)/len(t)>0.15: return False\n if sum(1 for w in ws if w in STOP)/len(ws)<0.22: return False\n if len(ws)>1 and sum(1 for j in range(len(ws)-1) if (ws[j],ws[j+1]) in CB)/(len(ws)-1)<0.25: return False\n lines=[l for l in t.split(chr(10)) if l.strip()]\n if lines and Counter(lines).most_common(1)[0][1]/len(lines)>0.5: return False\n wc=Counter(ws)\n if len(wc)/len(ws)<0.30: return False\n if wc.most_common(1)[0][1]/len(ws)>0.10: return False\n return True\n keep=np.array([passes(t) for t in texts],dtype=bool)\n np.save('/tmp/keep.npy',keep)\nkept=np.where(keep)[0]\nranked=[list(kept[np.argsort(-S[kept,k])]) for k in range(NREG)]\nW=[int(x) for x in sys.argv[1].split(',')] # per-register weights\nOUT=sys.argv[2]\nseen,selection,ptr=set(),[],[0]*NREG\nwhile len(selection)<40000:\n prog=False\n for k in range(NREG):\n for _ in range(W[k]):\n while ptr[k]<len(ranked[k]):\n i=ranked[k][ptr[k]]; ptr[k]+=1\n sig=re.sub(r'\\s+',' ',texts[i].lower()).strip()[:400]; h=zlib.crc32(sig.encode())\n if h in seen: continue\n seen.add(h); selection.append(int(ids[i])); prog=True; break\n if len(selection)>=40000: break\n if len(selection)>=40000: break\n if not prog: break\njson.dump(selection,open(OUT,'w'))\nprint('wrote',len(selection),'weights',W,'->',OUT)\nPY\ncd /workspace; python3 /tmp/wsel.py 2,1,1,1 /tmp/sel_wikiheavy.json",
"description": "Build wiki-heavy selection variant"
}Bash result
Command did not complete within its 120s timeout and was moved to the background (ID: bsyi8sysw). Output is being written to: /tmp/claude-0/-workspace/056a17ab-c7c2-448d-bef2-fc697ffa5283/tasks/bsyi8sysw.output. You will be notified when it completes. To check interim output, use Read on that file path.
Session cwd remains /workspace; directory changes made by the backgrounded command do not apply to subsequent commands.
[metadata] {"noOutputExpected": false, "backgroundTaskId": "bsyi8sysw", "timedOutAfterMs": 120000, "backgroundCwdHint": "Session cwd remains /workspace; directory changes made by the backgrounded command do not apply to subsequent commands."}encrypted chain-of-thought — recorded, not exposed
args
{
"command": "cd /workspace; sleep 30; cat /tmp/claude-0/-workspace/056a17ab-c7c2-448d-bef2-fc697ffa5283/tasks/bsyi8sysw.output 2>/dev/null; ls -la /tmp/keep.npy 2>/dev/null && echo \"keep cached\"",
"description": "Check wsel completion"
}Bash result
wrote 40000 weights [2, 1, 1, 1] -> /tmp/sel_wikiheavy.json
-rw-r--r-- 1 root root 182144 Aug 4 04:21 /tmp/keep.npy
keep cached
[stdout]
wrote 40000 weights [2, 1, 1, 1] -> /tmp/sel_wikiheavy.json
-rw-r--r-- 1 root root 182144 Aug 4 04:21 /tmp/keep.npy
keep cached
[metadata] {"noOutputExpected": false}keep mask cached — now weightings are instant. Let me pack the wiki-heavy variant and run the 4-quarter diagnostic.
args
{
"command": "cd /workspace; python3 pack_selection.py /tmp/sel_wikiheavy.json /tmp/dev_train.npy 2>&1 | tail -1; PYTHONPATH=/workspace nohup python3 /tmp/diag.py > /tmp/diag_wh.log 2>&1 & echo \"pid $!\"",
"description": "Pack and diag wiki-heavy variant"
}Bash result
packed 12000000 tokens from 40000 ids -> /tmp/dev_train.npy
pid 5609
[stdout]
packed 12000000 tokens from 40000 ids -> /tmp/dev_train.npy
pid 5609
[metadata] {"noOutputExpected": false}encrypted chain-of-thought — recorded, not exposed
args
{
"command": "cd /workspace; sleep 85; echo \"=== wiki-heavy (2,1,1,1) ===\"; cat /tmp/diag_wh.log; echo \"=== balanced (1,1,1,1) was: wiki 612 prose 273 news 280 QA 228 -> overall 322 ===\"",
"description": "Read wiki-heavy diagnostic"
}Bash result
=== wiki-heavy (2,1,1,1) ===
wiki PPL 568.5
prose PPL 275.9
news PPL 279.6
QA PPL 229.7
=== balanced (1,1,1,1) was: wiki 612 prose 273 news 280 QA 228 -> overall 322 ===
[stdout]
=== wiki-heavy (2,1,1,1) ===
wiki PPL 568.5
prose PPL 275.9
news PPL 279.6
QA PPL 229.7
=== balanced (1,1,1,1) was: wiki 612 prose 273 news 280 QA 228 -> overall 322 ===
[metadata] {"noOutputExpected": false}Wiki responds to more encyclopedic data (612→568) at negligible cost to others — overall improves to ~317. Let me push further with 3,1,1,1 and 4,1,1,1.
args
{
"command": "cd /workspace; python3 /tmp/wsel.py 3,1,1,1 /tmp/sel_w3.json; python3 /tmp/wsel.py 4,1,1,1 /tmp/sel_w4.json; python3 pack_selection.py /tmp/sel_w3.json /tmp/dev_train.npy 2>&1 | tail -1; PYTHONPATH=/workspace nohup python3 /tmp/diag.py > /tmp/diag_w3.log 2>&1 & echo \"pid $!\"",
"description": "Generate and diag 3,1,1,1 variant"
}Bash result
wrote 40000 weights [3, 1, 1, 1] -> /tmp/sel_w3.json
wrote 40000 weights [4, 1, 1, 1] -> /tmp/sel_w4.json
packed 12000000 tokens from 40000 ids -> /tmp/dev_train.npy
pid 5940
[stdout]
wrote 40000 weights [3, 1, 1, 1] -> /tmp/sel_w3.json
wrote 40000 weights [4, 1, 1, 1] -> /tmp/sel_w4.json
packed 12000000 tokens from 40000 ids -> /tmp/dev_train.npy
pid 5940
[metadata] {"noOutputExpected": false}encrypted chain-of-thought — recorded, not exposed
args
{
"command": "cd /workspace; sleep 85; echo \"=== w3 (3,1,1,1) ===\"; cat /tmp/diag_w3.log; python3 -c \"\nimport math\nfor name,v in [('w3',[])]:\n pass\ndef ov(w,p,n,q): \n import math; m=(math.log(w)+math.log(p)+math.log(n)+math.log(q))/4; return math.exp(m)\nprint('overall w3 =', round(ov(*[float(x.split()[2]) for x in open('/tmp/diag_w3.log')]),1))\n\"",
"description": "Read w3 diagnostic and overall"
}Bash result
=== w3 (3,1,1,1) ===
wiki PPL 564.5
prose PPL 273.4
news PPL 275.9
QA PPL 227.0
overall w3 = 313.6
[stdout]
=== w3 (3,1,1,1) ===
wiki PPL 564.5
prose PPL 273.4
news PPL 275.9
QA PPL 227.0
overall w3 = 313.6
[metadata] {"noOutputExpected": false}w3 (3,1,1,1) → overall 313.6, still improving and others held steady. Let me test 4,1,1,1.
args
{
"command": "cd /workspace; python3 pack_selection.py /tmp/sel_w4.json /tmp/dev_train.npy 2>&1 | tail -1; PYTHONPATH=/workspace nohup python3 /tmp/diag.py > /tmp/diag_w4.log 2>&1 & echo \"pid $!\"; sleep 88; echo \"=== w4 (4,1,1,1) ===\"; cat /tmp/diag_w4.log; python3 -c \"import math;v=[float(x.split()[2]) for x in open('/tmp/diag_w4.log')];print('overall w4 =',round(math.exp(sum(math.log(a) for a in v)/4),1))\"",
"description": "Pack and diag 4,1,1,1"
}Bash result
Command did not complete within its 120s timeout and was moved to the background (ID: bl4g2wp1e). Output is being written to: /tmp/claude-0/-workspace/056a17ab-c7c2-448d-bef2-fc697ffa5283/tasks/bl4g2wp1e.output. You will be notified when it completes. To check interim output, use Read on that file path.
Session cwd remains /workspace; directory changes made by the backgrounded command do not apply to subsequent commands.
[metadata] {"noOutputExpected": false, "backgroundTaskId": "bl4g2wp1e", "timedOutAfterMs": 120000, "backgroundCwdHint": "Session cwd remains /workspace; directory changes made by the backgrounded command do not apply to subsequent commands."}encrypted chain-of-thought — recorded, not exposed
args
{
"command": "cat /tmp/claude-0/-workspace/056a17ab-c7c2-448d-bef2-fc697ffa5283/tasks/bl4g2wp1e.output; cat /workspace/.timer/remaining_secs",
"description": "Read w4 result and time"
}Bash result
packed 12000000 tokens from 40000 ids -> /tmp/dev_train.npy
pid 6142
1384
[stdout]
packed 12000000 tokens from 40000 ids -> /tmp/dev_train.npy
pid 6142
1384
[metadata] {"noOutputExpected": false}encrypted chain-of-thought — recorded, not exposed
args
{
"command": "cd /workspace; sleep 25; echo \"=== w4 (4,1,1,1) ===\"; cat /tmp/diag_w4.log; python3 -c \"import math;v=[float(x.split()[2]) for x in open('/tmp/diag_w4.log')];print('overall w4 =',round(math.exp(sum(math.log(a) for a in v)/4),1))\" 2>/dev/null",
"description": "Read w4 diagnostic"
}Bash result
=== w4 (4,1,1,1) ===
wiki PPL 594.6
prose PPL 280.5
news PPL 286.5
QA PPL 243.9
overall w4 = 328.6
[stdout]
=== w4 (4,1,1,1) ===
wiki PPL 594.6
prose PPL 280.5
news PPL 286.5
QA PPL 243.9
overall w4 = 328.6
[metadata] {"noOutputExpected": false}w3 (3,1,1,1) → 313.6 is the sweet spot (w4 overshoots to 328.6). Mixture summary: balanced 321.9 → 2:1:1:1 316.7 → 3:1:1:1 313.6 → 4:1:1:1 328.6. Let me lock in w3: update curate.py to the weighted round-robin and verify it reproduces the selection.
args
{
"replace_all": false,
"file_path": "/workspace/submission/curate.py",
"old_string": "# ---------- round-robin fill -> balanced priority order, with dedup ----------\nseen, selection, ptr = set(), [], [0, 0, 0, 0]\nTARGET = 40000\nwhile len(selection) < TARGET:\n progressed = False\n for k in range(NREG):\n while ptr[k] < len(ranked[k]):\n i = ranked[k][ptr[k]]; ptr[k] += 1\n sig = re.sub(r\"\\s+\", \" \", texts[i].lower()).strip()[:400]\n h = zlib.crc32(sig.encode())\n if h in seen: continue\n seen.add(h); selection.append(int(ids[i])); progressed = True\n break\n if len(selection) >= TARGET: break\n if not progressed: break",
"new_string": "# ---------- weighted round-robin fill -> priority order, with dedup ----------\n# Per-register token quota. The eval is equal-parts, but the ENCYCLOPEDIC register\n# (reg 0) is by far the hardest for a from-scratch 30M model on this pool: its dev\n# text is WikiText-style (spaced punctuation) that raw-web docs don't match, so its\n# per-register perplexity is ~2x the others and dominates the equal-weight average.\n# Measured on the frozen recipe, over-sampling encyclopedic docs 3:1 lowers the\n# encyclopedic quarter's PPL (612->565) with negligible cost to the other three,\n# and minimizes the overall held-out PPL (321.9 balanced -> 313.6 at 3:1; 4:1\n# overshoots to 328.6 as it starves the other registers and reaches weaker docs).\nWEIGHTS = [3, 1, 1, 1]\nseen, selection, ptr = set(), [], [0] * NREG\nTARGET = 40000\nwhile len(selection) < TARGET:\n progressed = False\n for k in range(NREG):\n for _ in range(WEIGHTS[k]):\n while ptr[k] < len(ranked[k]):\n i = ranked[k][ptr[k]]; ptr[k] += 1\n sig = re.sub(r\"\\s+\", \" \", texts[i].lower()).strip()[:400]\n h = zlib.crc32(sig.encode())\n if h in seen: continue\n seen.add(h); selection.append(int(ids[i])); progressed = True\n break\n if len(selection) >= TARGET: break\n if len(selection) >= TARGET: break\n if not progressed: break"
}Edit result
The file /workspace/submission/curate.py has been updated successfully. (note: the file had been modified on disk since you last read it — the edit applied cleanly, but the file contains other changes not in your context. Read it before edits that depend on surrounding content.)
[metadata] {"filePath": "/workspace/submission/curate.py", "oldString": "# ---------- round-robin fill -> balanced priority order, with dedup ----------\nseen, selection, ptr = set(), [], [0, 0, 0, 0]\nTARGET = 40000\nwhile len(selection) < TARGET:\n progressed = False\n for k in range(NREG):\n while ptr[k] < len(ranked[k]):\n i = ranked[k][ptr[k]]; ptr[k] += 1\n sig = re.sub(r\"\\s+\", \" \", texts[i].lower()).strip()[:400]\n h = zlib.crc32(sig.encode())\n if h in seen: continue\n seen.add(h); selection.append(int(ids[i])); progressed = True\n break\n if len(selection) >= TARGET: break\n if not progressed: break", "newString": "# ---------- weighted round-robin fill -> priority order, with dedup ----------\n# Per-register token quota. The eval is equal-parts, but the ENCYCLOPEDIC register\n# (reg 0) is by far the hardest for a from-scratch 30M model on this pool: its dev\n# text is WikiText-style (spaced punctuation) that raw-web docs don't match, so its\n# per-register perplexity is ~2x the others and dominates the equal-weight average.\n# Measured on the frozen recipe, over-sampling encyclopedic docs 3:1 lowers the\n# encyclopedic quarter's PPL (612->565) with negligible cost to the other three,\n# and minimizes the overall held-out PPL (321.9 balanced -> 313.6 at 3:1; 4:1\n# overshoots to 328.6 as it starves the other registers and reaches weaker docs).\nWEIGHTS = [3, 1, 1, 1]\nseen, selection, ptr = set(), [], [0] * NREG\nTARGET = 40000\nwhile len(selection) < TARGET:\n progressed = False\n for k in range(NREG):\n for _ in range(WEIGHTS[k]):\n while ptr[k] < len(ranked[k]):\n i = ranked[k][ptr[k]]; ptr[k] += 1\n sig = re.sub(r\"\\s+\", \" \", texts[i].lower()).strip()[:400]\n h = zlib.crc32(sig.encode())\n if h in seen: continue\n seen.add(h); selection.append(int(ids[i])); progressed = True\n break\n if len(selection) >= TARGET: break\n if len(selection) >= TARGET: break\n if not progressed: break", "originalFile": "\"\"\"Curate a raw web pool for a small LM whose eval target is a BROAD, EQUAL-PARTS\nmulti-domain high-quality English corpus: encyclopedic (Wikipedia), general\nhigh-quality web prose, news, and technical Q&A.\n\nSTATED CRITERION\n----------------\nTwo ideas, composed:\n\n1. Clean-English-prose gate. Keep only documents that read like real English\n prose: long enough, mostly alphabetic, not table/listing/code, a healthy\n function-word (stopword) ratio (nav menus, keyword spam, and non-English text\n fail this), no degenerate line/token repetition. This removes the bulk of raw\n web junk regardless of topic.\n\n2. Register-balanced target-likeness. The disclosed target is EQUAL parts of four\n registers, and the dev target stream (data/multi_dev.npy) is laid out in four\n positional blocks (wiki -> technical/web prose -> news -> Q&A). We split it into\n four register corpora and fit one linear Naive-Bayes log-odds model per register\n (register chunks = positive, a random pool sample = negative). Every surviving\n pool doc gets four per-token log-odds scores. We then fill the priority list by\n ROUND-ROBIN across the four registers, each round taking the next highest-scoring\n unused doc for that register. The 12M-token budget is therefore split ~evenly\n across the four target registers instead of collapsing onto the most abundant one\n (news), matching the eval's equal-parts composition.\n\nReproducible, deterministic, pure numpy/stdlib (no sklearn). No hand-picked ids.\n\"\"\"\nimport json, re, sys, zlib, math, os\nimport numpy as np\nfrom collections import Counter\n\nPOOL = \"/workspace/data/pool.jsonl\"\nDEV = \"/workspace/data/multi_dev.npy\"\nOUT = \"/workspace/submission/selection.json\"\nSEED = 1337\nK = 1 << 20\nNREG = 4\nWORD = re.compile(r\"[a-z0-9']+\")\nrng = np.random.default_rng(SEED)\n\ndef feats(text, cap=2000):\n w = WORD.findall(text.lower())[:cap]\n out = [zlib.crc32(x.encode()) & (K - 1) for x in w]\n for i in range(len(w) - 1):\n out.append(zlib.crc32((w[i] + \" \" + w[i + 1]).encode()) & (K - 1))\n return out\n\n# ---------- load pool ----------\nids, texts = [], []\nwith open(POOL) as f:\n for line in f:\n r = json.loads(line)\n ids.append(r[\"id\"]); texts.append(r[\"text\"])\nN = len(ids)\nids = np.array(ids)\nprint(f\"pool: {N} docs\", file=sys.stderr)\n\n# ---------- register positives from the disclosed dev target ----------\nfrom transformers import AutoTokenizer\ntok = AutoTokenizer.from_pretrained(\"gpt2\")\ndev = np.load(DEV)\nchunks, cur = [], []\nfor t in dev.tolist():\n if t == 50256:\n if cur: chunks.append(tok.decode(cur)); cur = []\n else:\n cur.append(t)\nif cur: chunks.append(tok.decode(cur))\nchunks = [c for c in chunks if len(c) > 200]\n# four positional register groups (dev is laid out wiki|prose|news|qa)\nper = len(chunks) / NREG\nreg_pos = [chunks[int(k * per):int((k + 1) * per)] for k in range(NREG)]\nprint(\"register sizes:\", [len(r) for r in reg_pos], file=sys.stderr)\n\n# ---------- negatives: random pool sample ----------\nneg_idx = rng.choice(N, size=min(6000, N), replace=False)\nneg_texts = [texts[i] for i in neg_idx]\n\n# reference \"common English word BIGRAMS\" (fluency model) from positives+negatives.\n# Fraction of a doc's word-bigrams that are attested is a fluency signal: it rejects\n# \"spun\"/word-salad SEO spam (common words in nonsensical order) that otherwise scores\n# HIGH on the encyclopedic register (WikiText's diverse vocabulary) and pollutes a\n# whole quarter of the budget. Vocabulary alone can't catch these; bigrams can.\n_bg = Counter()\nfor c in [x for g in reg_pos for x in g] + neg_texts:\n w = WORD.findall(c.lower())\n for j in range(len(w) - 1):\n _bg[(w[j], w[j + 1])] += 1\nCOMMON_BG = frozenset(k for k, v in _bg.items() if v >= 2)\nprint(f\"common bigrams: {len(COMMON_BG)}\", file=sys.stderr)\ndef fluency(ws):\n if len(ws) < 2: return 0.0\n return sum(1 for j in range(len(ws) - 1) if (ws[j], ws[j + 1]) in COMMON_BG) / (len(ws) - 1)\nneg_c = np.zeros(K, dtype=np.float64)\nfor p in neg_texts:\n fe = feats(p)\n if fe: np.add.at(neg_c, np.array(fe), 1.0)\nNtot = neg_c.sum()\n\n# ---------- one NB log-odds weight vector per register ----------\nweights = np.zeros((NREG, K), dtype=np.float32)\na = 1.0\nfor k in range(NREG):\n pos_c = np.zeros(K, dtype=np.float64)\n for p in reg_pos[k]:\n fe = feats(p)\n if fe: np.add.at(pos_c, np.array(fe), 1.0)\n Ptot = pos_c.sum()\n w = (np.log(pos_c + a) - math.log(Ptot + a * K)) - \\\n (np.log(neg_c + a) - math.log(Ntot + a * K))\n c0 = math.log(Ntot + a * K) - math.log(Ptot + a * K) # center: neutral seen feature ~ 0\n w = w - c0\n w[(pos_c + neg_c) < 3] = 0.0 # trust only observed features\n weights[k] = w.astype(np.float32)\n\n# ---------- score every pool doc against all four registers ----------\n_CACHE = \"/tmp/curate_scores4.npy\"\nif os.path.exists(_CACHE) and np.load(_CACHE).shape == (N, NREG):\n S = np.load(_CACHE)\n print(\"loaded cached scores\", file=sys.stderr)\nelse:\n S = np.full((N, NREG), -1e9, dtype=np.float32)\n for i in range(N):\n fe = feats(texts[i])\n if fe:\n idx = np.array(fe)\n n = len(fe)\n for k in range(NREG):\n S[i, k] = weights[k][idx].sum() / n\n if i % 40000 == 0: print(f\"scored {i}\", file=sys.stderr)\n np.save(_CACHE, S)\n\n# ---------- clean-English-prose gate ----------\nSTOP = set(\"the of and to a in is that it for on as with was are be by this at \"\n \"from or an not but have has had he she they we you i his her their \"\n \"which who will would can could been were said your all more one about \"\n \"when what there if so no do does did than then them these those into \"\n \"out up down over after also its our\".split())\ndef passes(t):\n if len(t) < 300: return False\n ws = WORD.findall(t.lower())\n if len(ws) < 60: return False\n if sum(c.isalpha() or c.isspace() for c in t) / len(t) < 0.70: return False\n if sum(c.isdigit() for c in t) / len(t) > 0.15: return False\n if sum(1 for w in ws if w in STOP) / len(ws) < 0.22: return False # non-English/nav/code\n if fluency(ws) < 0.25: return False # gibberish / spun word-salad spam\n lines = [l for l in t.split(\"\\n\") if l.strip()]\n if lines and Counter(lines).most_common(1)[0][1] / len(lines) > 0.5: return False\n wc = Counter(ws)\n if len(wc) / len(ws) < 0.30: return False\n if wc.most_common(1)[0][1] / len(ws) > 0.10: return False\n return True\nkeep = np.array([passes(t) for t in texts], dtype=bool)\nprint(f\"passed gates: {int(keep.sum())}\", file=sys.stderr)\n\n# ---------- per-register ranked candidate lists (gated) ----------\nkept = np.where(keep)[0]\nranked = []\nfor k in range(NREG):\n o = kept[np.argsort(-S[kept, k])]\n ranked.append(list(o))\n\n# ---------- round-robin fill -> balanced priority order, with dedup ----------\nseen, selection, ptr = set(), [], [0, 0, 0, 0]\nTARGET = 40000\nwhile len(selection) < TARGET:\n progressed = False\n for k in range(NREG):\n while ptr[k] < len(ranked[k]):\n i = ranked[k][ptr[k]]; ptr[k] += 1\n sig = re.sub(r\"\\s+\", \" \", texts[i].lower()).strip()[:400]\n h = zlib.crc32(sig.encode())\n if h in seen: continue\n seen.add(h); selection.append(int(ids[i])); progressed = True\n break\n if len(selection) >= TARGET: break\n if not progressed: break\n\njson.dump(selection, open(OUT, \"w\"))\nprint(f\"wrote {len(selection)} ids -> {OUT}\", file=sys.stderr)\n", "structuredPatch": [{"oldStart": 160, "oldLines": 19, "newStart": 160, "newLines": 30, "lines": [" o = kept[np.argsort(-S[kept, k])]", " ranked.append(list(o))", " ", "-# ---------- round-robin fill -> balanced priority order, with dedup ----------", "-seen, selection, ptr = set(), [], [0, 0, 0, 0]", "+# ---------- weighted round-robin fill -> priority order, with dedup ----------", "+# Per-register token quota. The eval is equal-parts, but the ENCYCLOPEDIC register", "+# (reg 0) is by far the hardest for a from-scratch 30M model on this pool: its dev", "+# text is WikiText-style (spaced punctuation) that raw-web docs don't match, so its", "+# per-register perplexity is ~2x the others and dominates the equal-weight average.", "+# Measured on the frozen recipe, over-sampling encyclopedic docs 3:1 lowers the", "+# encyclopedic quarter's PPL (612->565) with negligible cost to the other three,", "+# and minimizes the overall held-out PPL (321.9 balanced -> 313.6 at 3:1; 4:1", "+# overshoots to 328.6 as it starves the other registers and reaches weaker docs).", "+WEIGHTS = [3, 1, 1, 1]", "+seen, selection, ptr = set(), [], [0] * NREG", " TARGET = 40000", " while len(selection) < TARGET:", " progressed = False", " for k in range(NREG):", "- while ptr[k] < len(ranked[k]):", "- i = ranked[k][ptr[k]]; ptr[k] += 1", "- sig = re.sub(r\"\\s+\", \" \", texts[i].lower()).strip()[:400]", "- h = zlib.crc32(sig.encode())", "- if h in seen: continue", "- seen.add(h); selection.append(int(ids[i])); progressed = True", "- break", "+ for _ in range(WEIGHTS[k]):", "+ while ptr[k] < len(ranked[k]):", "+ i = ranked[k][ptr[k]]; ptr[k] += 1", "+ sig = re.sub(r\"\\s+\", \" \", texts[i].lower()).strip()[:400]", "+ h = zlib.crc32(sig.encode())", "+ if h in seen: continue", "+ seen.add(h); selection.append(int(ids[i])); progressed = True", "+ break", "+ if len(selection) >= TARGET: break", " if len(selection) >= TARGET: break", " if not progressed: break", " "]}], "userModified": false, "replaceAll": false, "staleRecovered": true}encrypted chain-of-thought — recorded, not exposed
args
{
"command": "cd /workspace; python3 submission/curate.py > /tmp/cur_w3.log 2>&1; grep -E \"wrote|passed\" /tmp/cur_w3.log; python3 -c \"\nimport json\na=json.load(open('submission/selection.json'))\nb=json.load(open('/tmp/sel_w3.json'))\nprint('curate.py output len',len(a),'== sel_w3:',a==b)\nprint('unique',len(set(a)),'min',min(a),'max',max(a))\n\"",
"description": "Run curate.py and verify it reproduces w3"
}Bash result
Command did not complete within its 120s timeout and was moved to the background (ID: b4l7vuf8c). Output is being written to: /tmp/claude-0/-workspace/056a17ab-c7c2-448d-bef2-fc697ffa5283/tasks/b4l7vuf8c.output. You will be notified when it completes. To check interim output, use Read on that file path.
Session cwd remains /workspace; directory changes made by the backgrounded command do not apply to subsequent commands.
[metadata] {"noOutputExpected": false, "backgroundTaskId": "b4l7vuf8c", "timedOutAfterMs": 120000, "backgroundCwdHint": "Session cwd remains /workspace; directory changes made by the backgrounded command do not apply to subsequent commands."}encrypted chain-of-thought — recorded, not exposed
args
{
"command": "cd /workspace; for i in $(seq 1 40); do if grep -q wrote /tmp/cur_w3.log; then break; fi; sleep 4; done; grep -E \"wrote|passed\" /tmp/cur_w3.log; python3 -c \"\nimport json\na=json.load(open('submission/selection.json'))\nb=json.load(open('/tmp/sel_w3.json'))\nprint('curate.py output len',len(a),'IDENTICAL to sel_w3:',a==b)\nprint('unique',len(set(a)),'min',min(a),'max',max(a),'all<182016:',max(a)<182016)\n\"",
"description": "Verify curate reproduces w3"
}Bash result
passed gates: 126782
wrote 40000 ids -> /workspace/submission/selection.json
curate.py output len 40000 IDENTICAL to sel_w3: True
unique 40000 min 2 max 181988 all<182016: True
[stdout]
passed gates: 126782
wrote 40000 ids -> /workspace/submission/selection.json
curate.py output len 40000 IDENTICAL to sel_w3: True
unique 40000 min 2 max 181988 all<182016: True
[metadata] {"noOutputExpected": false}curate.py reproduces the w3 selection exactly, and it's now the submission (40k unique ids, all in range). Let me run one final official-style dev_eval.sh to confirm the number, then finalize claim.md.
args
{
"command": "cd /workspace; cat /workspace/.timer/remaining_secs; nohup bash dev_eval.sh > /tmp/eval_final.log 2>&1 & echo \"final eval pid $!\"",
"description": "Final official-style dev eval"
}Bash result
1153
final eval pid 6598
[stdout]
1153
final eval pid 6598
[metadata] {"noOutputExpected": false}19 min left. Let me update claim.md with the mixture-optimization finding while the eval runs.
args
{
"file_path": "/workspace/claim.md",
"content": "# Claim: fluency-gated, register-weighted curation beats quantity\n\n## Hypothesis\nUnder a fixed 12M-token budget drawn from a 200M-token raw web pool, held-out\nperplexity of the frozen 30M-GPT on a **broad, equal-parts** high-quality English\ntarget (Wikipedia / general web prose / news / technical Q&A) is minimized not by\nmaximizing tokens but by three composable choices:\n\n1. **Clean-prose + fluency filtering.** Keep only well-formed English prose\n (length, alphabetic ratio, function-word ratio, low repetition) and reject\n *fluency-poor* text — nav/keyword spam, non-English, and \"spun\" word-salad SEO\n spam — using a word-**bigram** attestation score.\n2. **Register targeting.** Fit four per-register \"target-vs-pool\" Naive-Bayes\n log-odds models (positives = the four positional blocks of the disclosed dev\n target; negatives = a random pool sample) and rank pool docs per register.\n3. **Register weighting for the training mix.** Fill the budget by *weighted*\n round-robin across the four registers, over-sampling the register the model is\n worst at (encyclopedic) rather than splitting the budget equally.\n\nI predict this reaches held-out PPL far below a random-selection baseline of equal\ntoken count.\n\n## Mechanism / prediction (observable *other* than the final score)\nThe single most informative observable is the **per-register held-out perplexity**\n(measured by evaluating the trained model on the four quarters of `multi_dev.npy`\nseparately). Predictions that do not reference the final aggregate PPL:\n\n- **The encyclopedic register is the bottleneck and is ~2× harder than the others.**\n Its dev text is WikiText-style (spaced punctuation, `@,@`/`@-@`) that raw-web pool\n docs never match, so from-scratch it sits far above the rest. *Measured:* wiki 612\n vs prose 273 / news 280 / QA 228 for the equal-split selection.\n- **That register still responds to more in-register data, monotonically up to a\n point, then reverses.** Over-sampling encyclopedic docs lowers the wiki quarter\n with negligible cost to the other three — until the quota reaches into weaker docs\n and starves the others. *Measured mixture sweep* (encyclopedic : prose : news : QA):\n\n | training mix | wiki | prose | news | QA | overall dev PPL |\n |---|---|---|---|---|---|\n | random pool sample | — | — | — | — | 438.5 |\n | single classifier, top-scored | — | — | — | — | 343.7 |\n | 1 : 1 : 1 : 1 (equal) | 612 | 273 | 280 | 228 | 321.9 |\n | 2 : 1 : 1 : 1 | 569 | 276 | 280 | 230 | 316.7 |\n | **3 : 1 : 1 : 1 (submitted)** | **565** | **273** | **276** | **227** | **313.6** |\n | 4 : 1 : 1 : 1 | 595 | 281 | 287 | 244 | 328.6 |\n\n- **Removing the fluency gate re-pollutes the encyclopedic register specifically**\n with rare-vocabulary word-salad spam (WikiText's diverse vocabulary makes that\n register maximally vulnerable), so wiki PPL rises while the others barely move.\n\n## Falsification\nThe hypothesis is falsified on the frozen recipe if any hold:\n- A random selection of equal token count reaches held-out PPL ≤ the curated one.\n- Register targeting gives no lift over the single target-vs-pool classifier.\n- The per-register profile is *flat* (encyclopedic not the worst), or over-sampling\n the worst register does **not** lower overall PPL — i.e., the mix is irrelevant.\n- Removing the fluency gate neither raises PPL nor re-introduces word-salad spam\n into the encyclopedic register's top picks.\n\n## Transfer\nThe criterion is a stated, data-driven recipe (no hand-picked ids), so it transfers:\n- **Budget.** Ids are emitted in priority order, so the same file trains any budget;\n a larger budget consumes deeper into each register's ranked list at the same 3:1:1:1\n ratio. The ordering curated < random should persist.\n- **Target domain.** For a different broad HQ target, re-derive the per-register\n positives from a small sample of that target and re-run `curate.py` unchanged; it\n should again beat random. The mechanism generalizes: re-measure the per-register\n profile and up-weight whichever register the model is worst at.\n- **Pool.** On a different raw web pool the gates (fluency, function-word,\n repetition) reject the same junk classes, so the method generalizes without\n retuning.\n"
}Write result
The file /workspace/claim.md has been updated successfully. (file state is current in your context — no need to Read it back)
[metadata] {"type": "update", "filePath": "/workspace/claim.md", "content": "# Claim: fluency-gated, register-weighted curation beats quantity\n\n## Hypothesis\nUnder a fixed 12M-token budget drawn from a 200M-token raw web pool, held-out\nperplexity of the frozen 30M-GPT on a **broad, equal-parts** high-quality English\ntarget (Wikipedia / general web prose / news / technical Q&A) is minimized not by\nmaximizing tokens but by three composable choices:\n\n1. **Clean-prose + fluency filtering.** Keep only well-formed English prose\n (length, alphabetic ratio, function-word ratio, low repetition) and reject\n *fluency-poor* text — nav/keyword spam, non-English, and \"spun\" word-salad SEO\n spam — using a word-**bigram** attestation score.\n2. **Register targeting.** Fit four per-register \"target-vs-pool\" Naive-Bayes\n log-odds models (positives = the four positional blocks of the disclosed dev\n target; negatives = a random pool sample) and rank pool docs per register.\n3. **Register weighting for the training mix.** Fill the budget by *weighted*\n round-robin across the four registers, over-sampling the register the model is\n worst at (encyclopedic) rather than splitting the budget equally.\n\nI predict this reaches held-out PPL far below a random-selection baseline of equal\ntoken count.\n\n## Mechanism / prediction (observable *other* than the final score)\nThe single most informative observable is the **per-register held-out perplexity**\n(measured by evaluating the trained model on the four quarters of `multi_dev.npy`\nseparately). Predictions that do not reference the final aggregate PPL:\n\n- **The encyclopedic register is the bottleneck and is ~2× harder than the others.**\n Its dev text is WikiText-style (spaced punctuation, `@,@`/`@-@`) that raw-web pool\n docs never match, so from-scratch it sits far above the rest. *Measured:* wiki 612\n vs prose 273 / news 280 / QA 228 for the equal-split selection.\n- **That register still responds to more in-register data, monotonically up to a\n point, then reverses.** Over-sampling encyclopedic docs lowers the wiki quarter\n with negligible cost to the other three — until the quota reaches into weaker docs\n and starves the others. *Measured mixture sweep* (encyclopedic : prose : news : QA):\n\n | training mix | wiki | prose | news | QA | overall dev PPL |\n |---|---|---|---|---|---|\n | random pool sample | — | — | — | — | 438.5 |\n | single classifier, top-scored | — | — | — | — | 343.7 |\n | 1 : 1 : 1 : 1 (equal) | 612 | 273 | 280 | 228 | 321.9 |\n | 2 : 1 : 1 : 1 | 569 | 276 | 280 | 230 | 316.7 |\n | **3 : 1 : 1 : 1 (submitted)** | **565** | **273** | **276** | **227** | **313.6** |\n | 4 : 1 : 1 : 1 | 595 | 281 | 287 | 244 | 328.6 |\n\n- **Removing the fluency gate re-pollutes the encyclopedic register specifically**\n with rare-vocabulary word-salad spam (WikiText's diverse vocabulary makes that\n register maximally vulnerable), so wiki PPL rises while the others barely move.\n\n## Falsification\nThe hypothesis is falsified on the frozen recipe if any hold:\n- A random selection of equal token count reaches held-out PPL ≤ the curated one.\n- Register targeting gives no lift over the single target-vs-pool classifier.\n- The per-register profile is *flat* (encyclopedic not the worst), or over-sampling\n the worst register does **not** lower overall PPL — i.e., the mix is irrelevant.\n- Removing the fluency gate neither raises PPL nor re-introduces word-salad spam\n into the encyclopedic register's top picks.\n\n## Transfer\nThe criterion is a stated, data-driven recipe (no hand-picked ids), so it transfers:\n- **Budget.** Ids are emitted in priority order, so the same file trains any budget;\n a larger budget consumes deeper into each register's ranked list at the same 3:1:1:1\n ratio. The ordering curated < random should persist.\n- **Target domain.** For a different broad HQ target, re-derive the per-register\n positives from a small sample of that target and re-run `curate.py` unchanged; it\n should again beat random. The mechanism generalizes: re-measure the per-register\n profile and up-weight whichever register the model is worst at.\n- **Pool.** On a different raw web pool the gates (fluency, function-word,\n repetition) reject the same junk classes, so the method generalizes without\n retuning.\n", "structuredPatch": [{"oldStart": 1, "oldLines": 70, "newStart": 1, "newLines": 70, "lines": ["-# Claim: register-balanced, fluency-gated curation beats quantity", "+# Claim: fluency-gated, register-weighted curation beats quantity", " ", " ## Hypothesis", "-Under a fixed 12M-token training budget drawn from a 200M-token raw web pool, the", "-held-out perplexity of the frozen 30M-GPT on a **broad, equal-parts** high-quality", "-English target (Wikipedia / general web prose / news / technical Q&A) is minimized", "-not by maximizing tokens but by two composable choices:", "+Under a fixed 12M-token budget drawn from a 200M-token raw web pool, held-out", "+perplexity of the frozen 30M-GPT on a **broad, equal-parts** high-quality English", "+target (Wikipedia / general web prose / news / technical Q&A) is minimized not by", "+maximizing tokens but by three composable choices:", " ", "-1. **Clean-prose + fluency filtering** — keep only well-formed English prose", "+1. **Clean-prose + fluency filtering.** Keep only well-formed English prose", " (length, alphabetic ratio, function-word ratio, low repetition) and reject", "- *fluency-poor* text (nav/keyword spam, non-English, and \"spun\" word-salad SEO", "- spam) using a word-**bigram** attestation score.", "-2. **Register balancing** — split the 12M budget ~evenly across the four target", "- registers via four per-register \"target-vs-pool\" classifiers and a round-robin", "- fill, rather than letting the single most abundant register (news) dominate.", "+ *fluency-poor* text — nav/keyword spam, non-English, and \"spun\" word-salad SEO", "+ spam — using a word-**bigram** attestation score.", "+2. **Register targeting.** Fit four per-register \"target-vs-pool\" Naive-Bayes", "+ log-odds models (positives = the four positional blocks of the disclosed dev", "+ target; negatives = a random pool sample) and rank pool docs per register.", "+3. **Register weighting for the training mix.** Fill the budget by *weighted*", "+ round-robin across the four registers, over-sampling the register the model is", "+ worst at (encyclopedic) rather than splitting the budget equally.", " ", "-I predict this selection reaches held-out perplexity well below a random-selection", "-baseline of equal token count.", "+I predict this reaches held-out PPL far below a random-selection baseline of equal", "+token count.", " ", " ## Mechanism / prediction (observable *other* than the final score)", "-The classifier assigns each doc a per-token log-odds of belonging to each target", "-register; the gates measure prose-fluency. Two concrete, checkable predictions that", "-do **not** reference the final PPL number:", "+The single most informative observable is the **per-register held-out perplexity**", "+(measured by evaluating the trained model on the four quarters of `multi_dev.npy`", "+separately). Predictions that do not reference the final aggregate PPL:", " ", "-- **Composition shift.** The selected corpus that fills the budget has a", "- dramatically higher mean bigram-fluency and function-word ratio than a random", "- draw of the pool, and its documents are ~evenly split across the four registers", "- instead of ~news-dominated. (Verifiable by inspecting the packed docs.)", "-- **Ablation ordering of dev loss.** Turning knobs one at a time moves held-out", "- loss monotonically: random ⟶ single-score classifier ⟶ +register-balance ⟶", "- +fluency-gate should each *lower* dev perplexity, and removing the fluency gate", "- should specifically re-pollute the *encyclopedic* register's top picks with", "- rare-vocabulary word-salad spam (because WikiText's diverse vocabulary makes that", "- register maximally vulnerable to it).", "+- **The encyclopedic register is the bottleneck and is ~2× harder than the others.**", "+ Its dev text is WikiText-style (spaced punctuation, `@,@`/`@-@`) that raw-web pool", "+ docs never match, so from-scratch it sits far above the rest. *Measured:* wiki 612", "+ vs prose 273 / news 280 / QA 228 for the equal-split selection.", "+- **That register still responds to more in-register data, monotonically up to a", "+ point, then reverses.** Over-sampling encyclopedic docs lowers the wiki quarter", "+ with negligible cost to the other three — until the quota reaches into weaker docs", "+ and starves the others. *Measured mixture sweep* (encyclopedic : prose : news : QA):", " ", "-Measured dev perplexities (multi_dev.npy, frozen recipe, seed 1337) confirmed the", "-ordering while curating:", "+ | training mix | wiki | prose | news | QA | overall dev PPL |", "+ |---|---|---|---|---|---|", "+ | random pool sample | — | — | — | — | 438.5 |", "+ | single classifier, top-scored | — | — | — | — | 343.7 |", "+ | 1 : 1 : 1 : 1 (equal) | 612 | 273 | 280 | 228 | 321.9 |", "+ | 2 : 1 : 1 : 1 | 569 | 276 | 280 | 230 | 316.7 |", "+ | **3 : 1 : 1 : 1 (submitted)** | **565** | **273** | **276** | **227** | **313.6** |", "+ | 4 : 1 : 1 : 1 | 595 | 281 | 287 | 244 | 328.6 |", " ", "-| selection (equal 12M-token budget) | dev PPL |", "-|---|---|", "-| random pool sample | 438.5 |", "-| single \"target-vs-pool\" classifier, top-scored | 343.7 |", "-| + register-balanced round-robin (4 registers) | 326.9 |", "-| + bigram-fluency gate (final `curate.py`) | 322.0 |", "+- **Removing the fluency gate re-pollutes the encyclopedic register specifically**", "+ with rare-vocabulary word-salad spam (WikiText's diverse vocabulary makes that", "+ register maximally vulnerable), so wiki PPL rises while the others barely move.", " ", " ## Falsification", "-The hypothesis is falsified if any of these hold on the frozen recipe:", "-- A random selection of equal token count reaches held-out PPL ≤ the curated", "- selection (i.e., curation gives no lift over do-nothing).", "-- Register-balancing does **not** reduce PPL versus taking the top of a single", "- target-vs-pool classifier (i.e., matching the equal-parts composition is", "- irrelevant).", "-- Removing the fluency gate does **not** raise PPL and does **not** re-introduce", "- word-salad spam into the encyclopedic register's top picks.", "-Any of these would show the mechanism (clean, fluent, register-balanced tokens) is", "-not what drives the gain.", "+The hypothesis is falsified on the frozen recipe if any hold:", "+- A random selection of equal token count reaches held-out PPL ≤ the curated one.", "+- Register targeting gives no lift over the single target-vs-pool classifier.", "+- The per-register profile is *flat* (encyclopedic not the worst), or over-sampling", "+ the worst register does **not** lower overall PPL — i.e., the mix is irrelevant.", "+- Removing the fluency gate neither raises PPL nor re-introduces word-salad spam", "+ into the encyclopedic register's top picks.", " ", " ## Transfer", "-The criterion is a stated, data-driven recipe, not a hand-picked id list, so it", "-transfers:", "-- **Budget.** Because ids are emitted in priority order, the same file trains any", "- budget; doubling to 24M tokens simply consumes deeper into each register's ranked", "- list. The relative ordering (curated < random) should hold at other budgets.", "-- **Target domain.** For a different broad HQ target, re-deriving the per-register", "- positives from a small sample of that target and re-running `curate.py` unchanged", "- should again beat random — the machinery (register classifiers + prose/fluency", "- gates) is domain-agnostic; only the disclosed positive sample changes.", "-- **Pool.** Applied to a different raw web pool, the gates (fluency, function-word,", "- repetition) reject the same junk classes, so the method should generalize without", "- retuning thresholds.", "+The criterion is a stated, data-driven recipe (no hand-picked ids), so it transfers:", "+- **Budget.** Ids are emitted in priority order, so the same file trains any budget;", "+ a larger budget consumes deeper into each register's ranked list at the same 3:1:1:1", "+ ratio. The ordering curated < random should persist.", "+- **Target domain.** For a different broad HQ target, re-derive the per-register", "+ positives from a small sample of that target and re-run `curate.py` unchanged; it", "+ should again beat random. The mechanism generalizes: re-measure the per-register", "+ profile and up-weight whichever register the model is worst at.", "+- **Pool.** On a different raw web pool the gates (fluency, function-word,", "+ repetition) reject the same junk classes, so the method generalizes without", "+ retuning."]}], "originalFile": "# Claim: register-balanced, fluency-gated curation beats quantity\n\n## Hypothesis\nUnder a fixed 12M-token training budget drawn from a 200M-token raw web pool, the\nheld-out perplexity of the frozen 30M-GPT on a **broad, equal-parts** high-quality\nEnglish target (Wikipedia / general web prose / news / technical Q&A) is minimized\nnot by maximizing tokens but by two composable choices:\n\n1. **Clean-prose + fluency filtering** — keep only well-formed English prose\n (length, alphabetic ratio, function-word ratio, low repetition) and reject\n *fluency-poor* text (nav/keyword spam, non-English, and \"spun\" word-salad SEO\n spam) using a word-**bigram** attestation score.\n2. **Register balancing** — split the 12M budget ~evenly across the four target\n registers via four per-register \"target-vs-pool\" classifiers and a round-robin\n fill, rather than letting the single most abundant register (news) dominate.\n\nI predict this selection reaches held-out perplexity well below a random-selection\nbaseline of equal token count.\n\n## Mechanism / prediction (observable *other* than the final score)\nThe classifier assigns each doc a per-token log-odds of belonging to each target\nregister; the gates measure prose-fluency. Two concrete, checkable predictions that\ndo **not** reference the final PPL number:\n\n- **Composition shift.** The selected corpus that fills the budget has a\n dramatically higher mean bigram-fluency and function-word ratio than a random\n draw of the pool, and its documents are ~evenly split across the four registers\n instead of ~news-dominated. (Verifiable by inspecting the packed docs.)\n- **Ablation ordering of dev loss.** Turning knobs one at a time moves held-out\n loss monotonically: random ⟶ single-score classifier ⟶ +register-balance ⟶\n +fluency-gate should each *lower* dev perplexity, and removing the fluency gate\n should specifically re-pollute the *encyclopedic* register's top picks with\n rare-vocabulary word-salad spam (because WikiText's diverse vocabulary makes that\n register maximally vulnerable to it).\n\nMeasured dev perplexities (multi_dev.npy, frozen recipe, seed 1337) confirmed the\nordering while curating:\n\n| selection (equal 12M-token budget) | dev PPL |\n|---|---|\n| random pool sample | 438.5 |\n| single \"target-vs-pool\" classifier, top-scored | 343.7 |\n| + register-balanced round-robin (4 registers) | 326.9 |\n| + bigram-fluency gate (final `curate.py`) | 322.0 |\n\n## Falsification\nThe hypothesis is falsified if any of these hold on the frozen recipe:\n- A random selection of equal token count reaches held-out PPL ≤ the curated\n selection (i.e., curation gives no lift over do-nothing).\n- Register-balancing does **not** reduce PPL versus taking the top of a single\n target-vs-pool classifier (i.e., matching the equal-parts composition is\n irrelevant).\n- Removing the fluency gate does **not** raise PPL and does **not** re-introduce\n word-salad spam into the encyclopedic register's top picks.\nAny of these would show the mechanism (clean, fluent, register-balanced tokens) is\nnot what drives the gain.\n\n## Transfer\nThe criterion is a stated, data-driven recipe, not a hand-picked id list, so it\ntransfers:\n- **Budget.** Because ids are emitted in priority order, the same file trains any\n budget; doubling to 24M tokens simply consumes deeper into each register's ranked\n list. The relative ordering (curated < random) should hold at other budgets.\n- **Target domain.** For a different broad HQ target, re-deriving the per-register\n positives from a small sample of that target and re-running `curate.py` unchanged\n should again beat random — the machinery (register classifiers + prose/fluency\n gates) is domain-agnostic; only the disclosed positive sample changes.\n- **Pool.** Applied to a different raw web pool, the gates (fluency, function-word,\n repetition) reject the same junk classes, so the method should generalize without\n retuning thresholds.\n", "userModified": false}encrypted chain-of-thought — recorded, not exposed
args
{
"command": "cd /workspace; for i in $(seq 1 30); do if grep -q HELDOUT /tmp/eval_final.log; then break; fi; sleep 5; done; grep HELDOUT /tmp/eval_final.log",
"description": "Get final eval perplexity"
}Bash result
HELDOUT_PPL 313.695 loss 5.7484 train_s 51 /tmp/dev_train.npy
[stdout]
HELDOUT_PPL 313.695 loss 5.7484 train_s 51 /tmp/dev_train.npy
[metadata] {"noOutputExpected": false}Confirmed: official-style dev_eval.sh gives 313.7, matching the diagnostic. Final validity check on all deliverables.
args
{
"command": "cd /workspace; echo \"=== deliverables ===\"; ls -la submission/selection.json submission/curate.py claim.md; echo; echo \"=== selection.json validity ===\"; python3 -c \"\nimport json\ns=json.load(open('submission/selection.json'))\nassert isinstance(s,list) and all(isinstance(x,int) for x in s)\nassert len(s)==len(set(s)), 'dupes'\nassert min(s)>=0 and max(s)<182016, 'range'\nprint('valid list:',len(s),'unique ids, range [%d,%d], no dupes'%(min(s),max(s)))\n\"; echo; echo \"=== summary ===\"; echo \"random baseline: 438.5 | final submission (3:1:1:1): 313.7 dev PPL (28.5% below random)\"",
"description": "Final deliverable validity check"
}Bash result
=== deliverables ===
-rw-r--r-- 1 root root 4255 Aug 4 04:34 claim.md
-rw-r--r-- 1 root root 8372 Aug 4 04:30 submission/curate.py
-rw-r--r-- 1 root root 285033 Aug 4 04:33 submission/selection.json
=== selection.json validity ===
valid list: 40000 unique ids, range [2,181988], no dupes
=== summary ===
random baseline: 438.5 | final submission (3:1:1:1): 313.7 dev PPL (28.5% below random)
[stdout]
=== deliverables ===
-rw-r--r-- 1 root root 4255 Aug 4 04:34 claim.md
-rw-r--r-- 1 root root 8372 Aug 4 04:30 submission/curate.py
-rw-r--r-- 1 root root 285033 Aug 4 04:33 submission/selection.json
=== selection.json validity ===
valid list: 40000 unique ids, range [2,181988], no dupes
=== summary ===
random baseline: 438.5 | final submission (3:1:1:1): 313.7 dev PPL (28.5% below random)
[metadata] {"noOutputExpected": false}All deliverables are complete and validated. Here's the final summary.
Result
| selection (equal 12M-token budget) | dev PPL |
|---|---|
| random baseline (do-nothing) | 438.5 |
| single target-vs-pool classifier | 343.7 |
| + register-balanced (1:1:1:1) | 321.9 |
| + fluency gate | 322.0 |
| + 3:1:1:1 register weighting (submitted) | 313.7 |
Final held-out dev perplexity: 313.7 — 28.5% below the random baseline.
What I built
`/workspace/submission/curate.py` — a reproducible, pure-numpy pipeline (verified to regenerate selection.json byte-identically):
- Clean-prose gates — length, alphabetic ratio, digit ratio, function-word (stopword) ratio, and type-token/dominant-token repetition checks strip the bulk of raw-web junk (nav menus, non-English, tables, code dumps).
- Bigram-fluency gate — rejects "spun" word-salad SEO spam that passes vocabulary checks but has nonsensical word order (a real failure I caught: it was polluting the whole encyclopedic slot).
- Per-register targeting — four Naive-Bayes log-odds models (one per target register: wiki / web-prose / news / Q&A), positives taken from the four positional blocks of the disclosed dev target.
- Weighted round-robin fill (3:1:1:1) — over-samples the encyclopedic register, which a per-register diagnostic showed is ~2× harder than the others (a WikiText format the pool can't match) and dominates the equal-parts average.
`/workspace/submission/selection.json` — 40,000 unique, in-range ids in priority order (fills 12M tokens with margin).
`/workspace/claim.md` — Hypothesis / Mechanism / Falsification / Transfer. The mechanism prediction is the observable per-register perplexity profile (wiki 612 vs ~230–280 for the rest, and its non-monotone response to over-sampling: 612→569→565→595 as the mix goes 1→2→3→4×), independent of the final aggregate score.
The key empirical discoveries that drove the score down: (a) mean-per-token classifier scoring rewards rare-token junk — fixed by centering + total-log-odds; (b) word-salad spam needs a fluency (bigram) signal, not a vocabulary one; and (c) the equal-parts target is bottlenecked by the encyclopedic register, so the optimal training mix is deliberately unequal (3:1:1:1), not balanced.