Skip to content
nano-gpt-data-curation

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
50
duration
46m
recorded
2026-08-04

run iddclm-nano-curation__xo46Sta

Everything claude-opus-4-8 did on this task, in order.

download JSON ↗
claude-codeclaude-opus-4-850 turns24 narration50 action50 observation26 reasoning1 final
#26
narration

My selection (502) is worse than random (477)! The mean-pooled classifier is picking degenerate/short docs. Let me diagnose what's actually being packed.

toolBashDiagnose packed docs
args
{
  "command": "cd /workspace; python3 -c \"\nimport json,numpy as np\nsel=json.load(open('submission/selection.json'))\ntext={}\nfor line in open('data/pool.jsonl'):\n    r=json.loads(line); text[r['id']]=r['text']\n# approx tokens by chars/4; find how many docs fill 12M tokens\nlens=[len(text[i]) for i in sel]\nclens=np.cumsum(lens)/4\nk=int(np.searchsorted(clens,12_000_000))\nprint('docs to fill ~12M tokens:', k)\nused=sel[:k]\nul=[len(text[i]) for i in used]\nprint('char len of used docs: median',int(np.median(ul)),'mean',int(np.mean(ul)),'min',min(ul),'max',max(ul))\nprint('frac used docs <600 chars:', np.mean(np.array(ul)<600))\nprint('=== sample of used docs (every 3000th) ===')\nfor i in used[::4000][:8]:\n    print(f'[{len(text[i])}c]', text[i][:180].replace(chr(10),' '));print()\n\"",
  "description": "Diagnose packed docs"
}
Bash result
docs to fill ~12M tokens: 11010
char len of used docs: median 2216 mean 4357 min 302 max 520446
frac used docs <600 chars: 0.04759309718437784
=== sample of used docs (every 3000th) ===
[554c]  for her role as Brittany on the BET comedy-drama series The Game. She appeared in 16 episodes of the series between 2006 and 2009. She earned her first professional acting credit 

[6802c]  States Supreme Court Justice Ruth Bader Ginsburg died on Sept. 18, thrusting the acrimonious struggle for control of the Supreme Court into public view. President Trump and Senate

[1238c]  winners.<|endoftext|>The pandemic is being used as cover for a no-deal Brexit | Mujtaba Rahman The Vote Leavers at the heart of government think the coronavirus crash will disguis

[stdout]
docs to fill ~12M tokens: 11010
char len of used docs: median 2216 mean 4357 min 302 max 520446
frac used docs <600 chars: 0.04759309718437784
=== sample of used docs (every 3000th) ===
[554c]  for her role as Brittany on the BET comedy-drama series The Game. She appeared in 16 episodes of the series between 2006 and 2009. She earned her first professional acting credit 

[6802c]  States Supreme Court Justice Ruth Bader Ginsburg died on Sept. 18, thrusting the acrimonious struggle for control of the Supreme Court into public view. President Trump and Senate

[1238c]  winners.<|endoftext|>The pandemic is being used as cover for a no-deal Brexit | Mujtaba Rahman The Vote Leavers at the heart of government think the coronavirus crash will disguis
[metadata] {"noOutputExpected": false}
#27
narration

Docs look fine individually, yet PPL is worse — likely register concentration (losing the target's 4-way balance) or near-duplicates. Let me measure both.

toolBashRegister + dup diagnostics
args
{
  "command": "cd /workspace; python3 -c \"\nimport json,numpy as np,re\nsel=json.load(open('submission/selection.json'))\ntext={}\nfor line in open('data/pool.jsonl'):\n    r=json.loads(line); text[r['id']]=r['text']\ndef fill(ids):\n    lens=np.cumsum([len(text[i]) for i in ids])/4\n    k=int(np.searchsorted(lens,12_000_000)); return ids[:k]\nused=fill(sel)\nrng=np.random.default_rng(42); allids=list(text)\nrnd=fill([int(i) for i in rng.permutation(allids)])\ndef markers(ids,name):\n    code=sum(('<code>' in text[i] or '<p>' in text[i] or 'function(' in text[i] or 'import ' in text[i]) for i in ids)\n    # near-dup by first 80 chars\n    pre=[text[i][:80] for i in ids]; dup=len(pre)-len(set(pre))\n    print(f'{name}: n={len(ids)} code/html={code/len(ids):.2%} nearDupPrefix={dup} ({dup/len(ids):.1%})')\nmarkers(used,'CLASSIFIER')\nmarkers(rnd,'RANDOM   ')\n\"",
  "description": "Register + dup diagnostics"
}
Bash result
CLASSIFIER: n=11010 code/html=0.80% nearDupPrefix=59 (0.5%)
RANDOM   : n=11277 code/html=1.36% nearDupPrefix=56 (0.5%)

[stdout]
CLASSIFIER: n=11010 code/html=0.80% nearDupPrefix=59 (0.5%)
RANDOM   : n=11277 code/html=1.36% nearDupPrefix=56 (0.5%)
[metadata] {"noOutputExpected": false}
#28
narration

Key finding: not duplicates. Both are ~1% code/html — the pool is thin on technical content. The real issue: ranking by score concentrated on easy clean prose and dropped coverage of the harder registers. Random's broad coverage wins. Better strategy: filter out the junk tail but preserve diversity rather than rank-concentrate. Let me test a "cleaned-broad" selection.

toolBashCleaned-broad selection eval
args
{
  "command": "cd /workspace; python3 -c \"\nimport json,numpy as np\nsc=np.load('/tmp/pool_scores.npy'); ids=np.load('/tmp/pool_ids.npy')\ntext={}\nfor line in open('data/pool.jsonl'):\n    r=json.loads(line); text[r['id']]=r['text']\ndef junk(t):\n    n=len(t)\n    if n<300: return True\n    if sum(c.isalpha() for c in t)/n<0.55: return True\n    return False\ngate=np.array([not junk(text[i]) for i in ids])\nthr=np.percentile(sc[gate],35)   # drop bottom 35% among gated\nkeep=gate & (sc>thr)\nkids=ids[keep]\nrng=np.random.default_rng(7); kids=rng.permutation(kids)\nsel=[int(i) for i in kids]\njson.dump(sel,open('/tmp/clean_broad.json','w'))\nprint('kept',len(sel),'thr',round(thr,3))\n\"\ntime bash dev_eval.sh /tmp/clean_broad.json 2>&1 | grep HELDOUT_PPL",
  "description": "Cleaned-broad selection eval"
}
Bash result
Command did not complete within its 120s timeout and was moved to the background (ID: bjqnh94x8). Output is being written to: /tmp/claude-0/-workspace/c504c517-4369-451f-ac2a-a4737203bba0/tasks/bjqnh94x8.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": "bjqnh94x8", "timedOutAfterMs": 120000, "backgroundCwdHint": "Session cwd remains /workspace; directory changes made by the backgrounded command do not apply to subsequent commands."}
#29
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashCheck clean_broad result
args
{
  "command": "sleep 95; cat /tmp/claude-0/-workspace/c504c517-4369-451f-ac2a-a4737203bba0/tasks/bjqnh94x8.output 2>/dev/null | grep -E \"HELDOUT_PPL|kept\"; cat /workspace/.timer/remaining_secs",
  "description": "Check clean_broad result"
}
Bash result
kept 115117 thr -0.679
HELDOUT_PPL 392.205  loss 5.9718  train_s 50  /tmp/dev_train.npy
4169

[stdout]
kept 115117 thr -0.679
HELDOUT_PPL 392.205  loss 5.9718  train_s 50  /tmp/dev_train.npy
4169
[metadata] {"noOutputExpected": false}
#30
narration

Big win: cleaned-broad = 392 (vs random 477, classifier-top 502). The recipe is: drop the junk tail, keep diversity. Now let me find the optimal drop threshold — sweep 50% and 65% (35% already = 392).

toolBashSweep drop thresholds 50,65
args
{
  "command": "cd /workspace; cat > /tmp/sweep.py <<'EOF'\nimport json,numpy as np,subprocess\nsc=np.load('/tmp/pool_scores.npy'); ids=np.load('/tmp/pool_ids.npy')\ntext={}\nfor line in open('data/pool.jsonl'):\n    r=json.loads(line); text[r['id']]=r['text']\ndef junk(t):\n    n=len(t)\n    if n<300: return True\n    if sum(c.isalpha() for c in t)/n<0.55: return True\n    return False\ngate=np.array([not junk(text[i]) for i in ids])\nfor pct in [50,65]:\n    thr=np.percentile(sc[gate],pct)\n    keep=gate&(sc>thr)\n    kids=np.random.default_rng(7).permutation(ids[keep])\n    fn=f'/tmp/drop{pct}.json'\n    json.dump([int(i) for i in kids],open(fn,'w'))\n    print(f'=== drop{pct}: kept {keep.sum()} ===',flush=True)\n    subprocess.run(f'bash dev_eval.sh {fn} 2>&1 | grep HELDOUT_PPL',shell=True)\nEOF\npython3 /tmp/sweep.py 2>&1 | grep -E \"drop|HELDOUT\" ",
  "description": "Sweep drop thresholds 50,65"
}
Bash result
Command did not complete within its 120s timeout and was moved to the background (ID: byr7ty0bs). Output is being written to: /tmp/claude-0/-workspace/c504c517-4369-451f-ac2a-a4737203bba0/tasks/byr7ty0bs.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": "byr7ty0bs", "timedOutAfterMs": 120000, "backgroundCwdHint": "Session cwd remains /workspace; directory changes made by the backgrounded command do not apply to subsequent commands."}
#31
narration

While that sweep runs, let me think about a complementary improvement: length-based filtering. Let me check the length distribution of very short vs the target.

toolBashCheck sweep progress
args
{
  "command": "sleep 100; cat /tmp/claude-0/-workspace/c504c517-4369-451f-ac2a-a4737203bba0/tasks/byr7ty0bs.output 2>/dev/null | grep -E \"drop|HELDOUT\"",
  "description": "Check sweep progress"
}
Bash result
=== drop50: kept 88550 ===
HELDOUT_PPL 388.883  loss 5.9633  train_s 50  /tmp/dev_train.npy
=== drop65: kept 61986 ===
HELDOUT_PPL 378.659  loss 5.9366  train_s 50  /tmp/dev_train.npy

[stdout]
=== drop50: kept 88550 ===
HELDOUT_PPL 388.883  loss 5.9633  train_s 50  /tmp/dev_train.npy
=== drop65: kept 61986 ===
HELDOUT_PPL 378.659  loss 5.9366  train_s 50  /tmp/dev_train.npy
[metadata] {"noOutputExpected": false}
#32
narration

Clear trend: drop35=392, drop50=389, drop65=379. Pushing quality harder helps (as long as we keep diversity via shuffle). Let me sweep further: 78% and 88%.

toolBashSweep drop 78,88
args
{
  "command": "cd /workspace; sed -i 's/\\[50,65\\]/[78,88]/' /tmp/sweep.py; python3 /tmp/sweep.py 2>&1 | grep -E \"drop|HELDOUT\"; echo REM $(cat /workspace/.timer/remaining_secs)",
  "description": "Sweep drop 78,88"
}
Bash result · error
Exit code 143
Command timed out after 2m 0s
=== drop78: kept 38963 ===

[error] tool reported failure
#33
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashRerun sweep 78,88 background
args
{
  "command": "cd /workspace; python3 /tmp/sweep.py 2>&1 | grep -E \"drop|HELDOUT\" &\nsleep 115; echo \"---\"; cat /workspace/.timer/remaining_secs",
  "description": "Rerun sweep 78,88 background"
}
Bash result
=== drop78: kept 38963 ===
---
3678

[stdout]
=== drop78: kept 38963 ===
---
3678
[metadata] {"noOutputExpected": false}
#34
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashRun sweep to logfile
args
{
  "command": "cd /workspace; python3 /tmp/sweep.py > /tmp/sweep2.log 2>&1 &\necho \"started pid $!\"",
  "description": "Run sweep to logfile"
}
Bash result
started pid 2847

[stdout]
started pid 2847
[metadata] {"noOutputExpected": false}
#35
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashCheck sweep2
args
{
  "command": "sleep 115; grep -E \"drop|HELDOUT\" /tmp/sweep2.log; echo REM $(cat /workspace/.timer/remaining_secs)",
  "description": "Check sweep2"
}
Bash result
=== drop78: kept 38963 ===
REM 3558

[stdout]
=== drop78: kept 38963 ===
REM 3558
[metadata] {"noOutputExpected": false}
#36
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashCheck sweep2 again
args
{
  "command": "sleep 110; grep -E \"drop|HELDOUT\" /tmp/sweep2.log; echo REM $(cat /workspace/.timer/remaining_secs)",
  "description": "Check sweep2 again"
}
Bash result
=== drop78: kept 38963 ===
HELDOUT_PPL 405.319  loss 6.0047  train_s 76  /tmp/dev_train.npy
=== drop88: kept 21253 ===
HELDOUT_PPL 443.470  loss 6.0946  train_s 50  /tmp/dev_train.npy
REM 3428

[stdout]
=== drop78: kept 38963 ===
HELDOUT_PPL 405.319  loss 6.0047  train_s 76  /tmp/dev_train.npy
=== drop88: kept 21253 ===
HELDOUT_PPL 443.470  loss 6.0946  train_s 50  /tmp/dev_train.npy
REM 3428
[metadata] {"noOutputExpected": false}
#37
narration

Full curve: drop35=392, drop50=389, drop65=379, drop78=405, drop88=443, top-only=502. Clear optimum near keeping the top ~35% by quality, shuffled. Let me refine around it (60, 70).

toolBashSweep 60,70
args
{
  "command": "cd /workspace; sed -i 's/\\[78,88\\]/[60,70]/' /tmp/sweep.py; python3 /tmp/sweep.py > /tmp/sweep3.log 2>&1 &\necho started",
  "description": "Sweep 60,70"
}
Bash result
started

[stdout]
started
[metadata] {"noOutputExpected": false}
#38
narration

While that runs, let me finalize curate.py with the winning criterion and write the deliverables.

toolEdit
args
{
  "replace_all": false,
  "file_path": "/workspace/submission/curate.py",
  "old_string": "Stated criterion\n----------------\nSelect the pool documents most *target-like* under a quality classifier, subject\nto light junk gating. The classifier is a feature-hashed bag-of-ngrams logistic\nregression (implemented as a torch EmbeddingBag on GPU, no sklearn dependency)\ntrained to separate:\n  positive := chunks of the disclosed target distribution (data/multi_dev.npy,\n              GPT-2 tokens decoded to text, split on <|endoftext|>)\n  negative := a random sample of the raw pool (the \"do-nothing\" distribution)\nEach pool doc is scored by the classifier logit (length-normalized via mean-pool);\nwe emit ids in descending score (best first). The frozen pipeline consumes them in\npriority order until the 12M-token budget is full. This is quality-classifier /\nDSIR-style curation: keep pool docs whose register + vocabulary match the target\nand that are clean enough to be low-entropy for the LM.\n\nReproducible: `python3 curate.py` -> writes submission/selection.json.\n\"\"\"",
  "new_string": "Stated criterion\n----------------\nQUALITY-FILTER, THEN PRESERVE DIVERSITY. The target is a *broad, 4-way* mixture\n(encyclopedic / web prose / news / technical Q&A). Empirically (dev-PPL sweep),\nhard-ranking the pool by a single \"target-likeness\" score and taking the extreme\ntop collapses register diversity and does *worse* than a random draw (top-only\ndev PPL 502 vs random 477). The minimum-perplexity recipe instead is:\n\n  1. Score each pool doc with a quality classifier (feature-hashed bag-of-ngrams\n     logistic regression, torch EmbeddingBag on GPU, no sklearn dependency),\n     trained: positive := chunks of the disclosed target (data/multi_dev.npy\n     decoded to text, split on <|endoftext|>); negative := random pool sample.\n  2. Apply a light junk gate (min length, min alphabetic fraction).\n  3. DROP the bottom KEEP_DROP_PCT of surviving docs by score (removes SEO/boiler-\n     plate/link-spam tail), then keep the *whole* top band and emit it in a\n     DIVERSITY-PRESERVING shuffled order — NOT sorted by score. The pipeline packs\n     ~12M tokens by randomly sampling this clean, broad top band, so all four\n     registers stay represented.\n\nThe drop percentile was tuned on dev PPL (drop 35%->392, 50%->389, 65%->379,\n78%->405, 88%->443): a clear optimum at keeping the top ~35% by quality while\nretaining diversity. Best dev PPL 379 vs random baseline 477.\n\nReproducible: `python3 curate.py` -> writes submission/selection.json.\n\"\"\""
}
Edit result
The file /workspace/submission/curate.py has been updated successfully. (file state is current in your context — no need to Read it back)

[metadata] {"filePath": "/workspace/submission/curate.py", "oldString": "Stated criterion\n----------------\nSelect the pool documents most *target-like* under a quality classifier, subject\nto light junk gating. The classifier is a feature-hashed bag-of-ngrams logistic\nregression (implemented as a torch EmbeddingBag on GPU, no sklearn dependency)\ntrained to separate:\n  positive := chunks of the disclosed target distribution (data/multi_dev.npy,\n              GPT-2 tokens decoded to text, split on <|endoftext|>)\n  negative := a random sample of the raw pool (the \"do-nothing\" distribution)\nEach pool doc is scored by the classifier logit (length-normalized via mean-pool);\nwe emit ids in descending score (best first). The frozen pipeline consumes them in\npriority order until the 12M-token budget is full. This is quality-classifier /\nDSIR-style curation: keep pool docs whose register + vocabulary match the target\nand that are clean enough to be low-entropy for the LM.\n\nReproducible: `python3 curate.py` -> writes submission/selection.json.\n\"\"\"", "newString": "Stated criterion\n----------------\nQUALITY-FILTER, THEN PRESERVE DIVERSITY. The target is a *broad, 4-way* mixture\n(encyclopedic / web prose / news / technical Q&A). Empirically (dev-PPL sweep),\nhard-ranking the pool by a single \"target-likeness\" score and taking the extreme\ntop collapses register diversity and does *worse* than a random draw (top-only\ndev PPL 502 vs random 477). The minimum-perplexity recipe instead is:\n\n  1. Score each pool doc with a quality classifier (feature-hashed bag-of-ngrams\n     logistic regression, torch EmbeddingBag on GPU, no sklearn dependency),\n     trained: positive := chunks of the disclosed target (data/multi_dev.npy\n     decoded to text, split on <|endoftext|>); negative := random pool sample.\n  2. Apply a light junk gate (min length, min alphabetic fraction).\n  3. DROP the bottom KEEP_DROP_PCT of surviving docs by score (removes SEO/boiler-\n     plate/link-spam tail), then keep the *whole* top band and emit it in a\n     DIVERSITY-PRESERVING shuffled order — NOT sorted by score. The pipeline packs\n     ~12M tokens by randomly sampling this clean, broad top band, so all four\n     registers stay represented.\n\nThe drop percentile was tuned on dev PPL (drop 35%->392, 50%->389, 65%->379,\n78%->405, 88%->443): a clear optimum at keeping the top ~35% by quality while\nretaining diversity. Best dev PPL 379 vs random baseline 477.\n\nReproducible: `python3 curate.py` -> writes submission/selection.json.\n\"\"\"", "originalFile": "\"\"\"Curate a raw web pool for small-LM pretraining toward a BROAD multi-domain\nhigh-quality English target (encyclopedic / web prose / news / technical Q&A).\n\nStated criterion\n----------------\nSelect the pool documents most *target-like* under a quality classifier, subject\nto light junk gating. The classifier is a feature-hashed bag-of-ngrams logistic\nregression (implemented as a torch EmbeddingBag on GPU, no sklearn dependency)\ntrained to separate:\n  positive := chunks of the disclosed target distribution (data/multi_dev.npy,\n              GPT-2 tokens decoded to text, split on <|endoftext|>)\n  negative := a random sample of the raw pool (the \"do-nothing\" distribution)\nEach pool doc is scored by the classifier logit (length-normalized via mean-pool);\nwe emit ids in descending score (best first). The frozen pipeline consumes them in\npriority order until the 12M-token budget is full. This is quality-classifier /\nDSIR-style curation: keep pool docs whose register + vocabulary match the target\nand that are clean enough to be low-entropy for the LM.\n\nReproducible: `python3 curate.py` -> writes submission/selection.json.\n\"\"\"\nimport json, re, sys, numpy as np, torch\nfrom transformers import AutoTokenizer\n\nPOOL = \"/workspace/data/pool.jsonl\"\nDEV  = \"/workspace/data/multi_dev.npy\"\nOUT  = \"/workspace/submission/selection.json\"\nSEED = 0\nN_NEG = 15000          # random pool docs as negatives\nN_EMIT = 60000         # emit far more ids than the 12M-token budget needs\nD = 1 << 20            # hashed feature buckets\nEPOCHS = 60\ndev_t = \"cuda\" if torch.cuda.is_available() else \"cpu\"\ntorch.manual_seed(SEED); rng = np.random.default_rng(SEED)\n\n_word = re.compile(r\"[a-z0-9']+\")\ndef ngram_buckets(text):\n    \"\"\"Hash word unigrams + bigrams to buckets in [0, D).\"\"\"\n    toks = _word.findall(text.lower())\n    if not toks:\n        return [0]\n    out = [(hash(t) & (D - 1)) for t in toks]\n    for i in range(len(toks) - 1):\n        out.append((hash(toks[i] + \" \" + toks[i + 1]) & (D - 1)))\n    return out\n\n# ---------- 1. positives: decode the disclosed target back to text ----------\ntok = AutoTokenizer.from_pretrained(\"gpt2\")\nEOS = tok.eos_token_id\ndev = np.load(DEV).astype(np.int64)\npos_texts, cur = [], []\nfor t in dev:\n    if t == EOS:\n        if cur: pos_texts.append(tok.decode(cur))\n        cur = []\n    else:\n        cur.append(int(t))\nif cur: pos_texts.append(tok.decode(cur))\n\ndef deartifact(s):  # strip WikiText tokenization quirks the pool can't contain\n    return s.replace(\" @,@ \", \",\").replace(\" @-@ \", \"-\").replace(\" @.@ \", \".\")\npos_texts = [deartifact(s) for s in pos_texts if len(s) > 200]\n\n# ---------- 2. 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\"])\nids = np.array(ids); N = len(ids)\nprint(f\"pool docs: {N}  positives: {len(pos_texts)}\", file=sys.stderr)\n\n# ---------- 3. negatives: random pool sample ----------\nneg_idx = rng.choice(N, size=min(N_NEG, N), replace=False)\nneg_texts = [texts[i] for i in neg_idx]\n\n# ---------- 4. featurize + train logistic regression (EmbeddingBag on GPU) ----------\ndef build_bag(text_list):\n    flat, offsets, off = [], [], 0\n    for t in text_list:\n        b = ngram_buckets(t)\n        offsets.append(off); flat.extend(b); off += len(b)\n    return (torch.tensor(flat, dtype=torch.long, device=dev_t),\n            torch.tensor(offsets, dtype=torch.long, device=dev_t))\n\ntrain_texts = pos_texts + neg_texts\ny = torch.tensor([1.0] * len(pos_texts) + [0.0] * len(neg_texts), device=dev_t)\ntin, toff = build_bag(train_texts)\n\nemb = torch.nn.EmbeddingBag(D, 1, mode=\"mean\").to(dev_t)\ntorch.nn.init.zeros_(emb.weight)\nbias = torch.zeros(1, requires_grad=True, device=dev_t)\nopt = torch.optim.Adam(list(emb.parameters()) + [bias], lr=0.05, weight_decay=1e-6)\n# class-balance weighting\nw_pos = len(train_texts) / (2 * len(pos_texts)); w_neg = len(train_texts) / (2 * len(neg_texts))\nwt = torch.where(y > 0.5, torch.tensor(w_pos, device=dev_t), torch.tensor(w_neg, device=dev_t))\nfor ep in range(EPOCHS):\n    opt.zero_grad()\n    logit = emb(tin, toff).squeeze(1) + bias\n    loss = torch.nn.functional.binary_cross_entropy_with_logits(logit, y, weight=wt)\n    loss.backward(); opt.step()\nwith torch.no_grad():\n    pred = (torch.sigmoid(emb(tin, toff).squeeze(1) + bias) > 0.5).float()\n    acc = (pred == y).float().mean().item()\nprint(f\"train loss {loss.item():.4f}  acc {acc:.3f}\", file=sys.stderr)\n\n# ---------- 5. score whole pool ----------\nscores = np.empty(N, dtype=np.float32)\nB = 8000\nemb.eval()\nwith torch.no_grad():\n    for s in range(0, N, B):\n        e = min(N, s + B)\n        bin_, boff = build_bag(texts[s:e])\n        scores[s:e] = (emb(bin_, boff).squeeze(1) + bias).cpu().numpy()\n\n# ---------- 6. light junk gating ----------\ndef junk(t):\n    n = len(t)\n    if n < 300: return True\n    if sum(c.isalpha() for c in t) / n < 0.55: return True\n    return False\ngate = np.array([not junk(t) for t in texts])\nscores_eff = np.where(gate, scores, -1e9)\n\n# ---------- 7. emit ids by descending score ----------\norder = np.argsort(-scores_eff)\nsel = [int(ids[i]) for i in order[:N_EMIT]]\njson.dump(sel, open(OUT, \"w\"))\nnp.save(\"/tmp/pool_scores.npy\", scores); np.save(\"/tmp/pool_ids.npy\", ids)\nprint(f\"wrote {len(sel)} ids -> {OUT}\", file=sys.stderr)\nprint(\"kept by gate:\", int(gate.sum()), \"/\", N, file=sys.stderr)\nprint(\"score pctiles:\", np.percentile(scores, [50, 90, 99, 99.9]), file=sys.stderr)\n", "structuredPatch": [{"oldStart": 3, "oldLines": 19, "newStart": 3, "newLines": 27, "lines": [" ", " Stated criterion", " ----------------", "-Select the pool documents most *target-like* under a quality classifier, subject", "-to light junk gating. The classifier is a feature-hashed bag-of-ngrams logistic", "-regression (implemented as a torch EmbeddingBag on GPU, no sklearn dependency)", "-trained to separate:", "-  positive := chunks of the disclosed target distribution (data/multi_dev.npy,", "-              GPT-2 tokens decoded to text, split on <|endoftext|>)", "-  negative := a random sample of the raw pool (the \"do-nothing\" distribution)", "-Each pool doc is scored by the classifier logit (length-normalized via mean-pool);", "-we emit ids in descending score (best first). The frozen pipeline consumes them in", "-priority order until the 12M-token budget is full. This is quality-classifier /", "-DSIR-style curation: keep pool docs whose register + vocabulary match the target", "-and that are clean enough to be low-entropy for the LM.", "+QUALITY-FILTER, THEN PRESERVE DIVERSITY. The target is a *broad, 4-way* mixture", "+(encyclopedic / web prose / news / technical Q&A). Empirically (dev-PPL sweep),", "+hard-ranking the pool by a single \"target-likeness\" score and taking the extreme", "+top collapses register diversity and does *worse* than a random draw (top-only", "+dev PPL 502 vs random 477). The minimum-perplexity recipe instead is:", " ", "+  1. Score each pool doc with a quality classifier (feature-hashed bag-of-ngrams", "+     logistic regression, torch EmbeddingBag on GPU, no sklearn dependency),", "+     trained: positive := chunks of the disclosed target (data/multi_dev.npy", "+     decoded to text, split on <|endoftext|>); negative := random pool sample.", "+  2. Apply a light junk gate (min length, min alphabetic fraction).", "+  3. DROP the bottom KEEP_DROP_PCT of surviving docs by score (removes SEO/boiler-", "+     plate/link-spam tail), then keep the *whole* top band and emit it in a", "+     DIVERSITY-PRESERVING shuffled order — NOT sorted by score. The pipeline packs", "+     ~12M tokens by randomly sampling this clean, broad top band, so all four", "+     registers stay represented.", "+", "+The drop percentile was tuned on dev PPL (drop 35%->392, 50%->389, 65%->379,", "+78%->405, 88%->443): a clear optimum at keeping the top ~35% by quality while", "+retaining diversity. Best dev PPL 379 vs random baseline 477.", "+", " Reproducible: `python3 curate.py` -> writes submission/selection.json.", " \"\"\"", " import json, re, sys, numpy as np, torch"]}], "userModified": false, "replaceAll": false}
#39
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolEdit
args
{
  "replace_all": false,
  "file_path": "/workspace/submission/curate.py",
  "old_string": "SEED = 0\nN_NEG = 15000          # random pool docs as negatives\nN_EMIT = 60000         # emit far more ids than the 12M-token budget needs",
  "new_string": "SEED = 0\nN_NEG = 15000          # random pool docs as negatives\nKEEP_DROP_PCT = 65     # drop bottom 65% by score among gated docs (tuned on dev PPL)\nSHUF_SEED = 7          # diversity-preserving shuffle of the kept top band"
}
Edit result
The file /workspace/submission/curate.py has been updated successfully. (file state is current in your context — no need to Read it back)

[metadata] {"filePath": "/workspace/submission/curate.py", "oldString": "SEED = 0\nN_NEG = 15000          # random pool docs as negatives\nN_EMIT = 60000         # emit far more ids than the 12M-token budget needs", "newString": "SEED = 0\nN_NEG = 15000          # random pool docs as negatives\nKEEP_DROP_PCT = 65     # drop bottom 65% by score among gated docs (tuned on dev PPL)\nSHUF_SEED = 7          # diversity-preserving shuffle of the kept top band", "originalFile": "\"\"\"Curate a raw web pool for small-LM pretraining toward a BROAD multi-domain\nhigh-quality English target (encyclopedic / web prose / news / technical Q&A).\n\nStated criterion\n----------------\nQUALITY-FILTER, THEN PRESERVE DIVERSITY. The target is a *broad, 4-way* mixture\n(encyclopedic / web prose / news / technical Q&A). Empirically (dev-PPL sweep),\nhard-ranking the pool by a single \"target-likeness\" score and taking the extreme\ntop collapses register diversity and does *worse* than a random draw (top-only\ndev PPL 502 vs random 477). The minimum-perplexity recipe instead is:\n\n  1. Score each pool doc with a quality classifier (feature-hashed bag-of-ngrams\n     logistic regression, torch EmbeddingBag on GPU, no sklearn dependency),\n     trained: positive := chunks of the disclosed target (data/multi_dev.npy\n     decoded to text, split on <|endoftext|>); negative := random pool sample.\n  2. Apply a light junk gate (min length, min alphabetic fraction).\n  3. DROP the bottom KEEP_DROP_PCT of surviving docs by score (removes SEO/boiler-\n     plate/link-spam tail), then keep the *whole* top band and emit it in a\n     DIVERSITY-PRESERVING shuffled order — NOT sorted by score. The pipeline packs\n     ~12M tokens by randomly sampling this clean, broad top band, so all four\n     registers stay represented.\n\nThe drop percentile was tuned on dev PPL (drop 35%->392, 50%->389, 65%->379,\n78%->405, 88%->443): a clear optimum at keeping the top ~35% by quality while\nretaining diversity. Best dev PPL 379 vs random baseline 477.\n\nReproducible: `python3 curate.py` -> writes submission/selection.json.\n\"\"\"\nimport json, re, sys, numpy as np, torch\nfrom transformers import AutoTokenizer\n\nPOOL = \"/workspace/data/pool.jsonl\"\nDEV  = \"/workspace/data/multi_dev.npy\"\nOUT  = \"/workspace/submission/selection.json\"\nSEED = 0\nN_NEG = 15000          # random pool docs as negatives\nN_EMIT = 60000         # emit far more ids than the 12M-token budget needs\nD = 1 << 20            # hashed feature buckets\nEPOCHS = 60\ndev_t = \"cuda\" if torch.cuda.is_available() else \"cpu\"\ntorch.manual_seed(SEED); rng = np.random.default_rng(SEED)\n\n_word = re.compile(r\"[a-z0-9']+\")\ndef ngram_buckets(text):\n    \"\"\"Hash word unigrams + bigrams to buckets in [0, D).\"\"\"\n    toks = _word.findall(text.lower())\n    if not toks:\n        return [0]\n    out = [(hash(t) & (D - 1)) for t in toks]\n    for i in range(len(toks) - 1):\n        out.append((hash(toks[i] + \" \" + toks[i + 1]) & (D - 1)))\n    return out\n\n# ---------- 1. positives: decode the disclosed target back to text ----------\ntok = AutoTokenizer.from_pretrained(\"gpt2\")\nEOS = tok.eos_token_id\ndev = np.load(DEV).astype(np.int64)\npos_texts, cur = [], []\nfor t in dev:\n    if t == EOS:\n        if cur: pos_texts.append(tok.decode(cur))\n        cur = []\n    else:\n        cur.append(int(t))\nif cur: pos_texts.append(tok.decode(cur))\n\ndef deartifact(s):  # strip WikiText tokenization quirks the pool can't contain\n    return s.replace(\" @,@ \", \",\").replace(\" @-@ \", \"-\").replace(\" @.@ \", \".\")\npos_texts = [deartifact(s) for s in pos_texts if len(s) > 200]\n\n# ---------- 2. 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\"])\nids = np.array(ids); N = len(ids)\nprint(f\"pool docs: {N}  positives: {len(pos_texts)}\", file=sys.stderr)\n\n# ---------- 3. negatives: random pool sample ----------\nneg_idx = rng.choice(N, size=min(N_NEG, N), replace=False)\nneg_texts = [texts[i] for i in neg_idx]\n\n# ---------- 4. featurize + train logistic regression (EmbeddingBag on GPU) ----------\ndef build_bag(text_list):\n    flat, offsets, off = [], [], 0\n    for t in text_list:\n        b = ngram_buckets(t)\n        offsets.append(off); flat.extend(b); off += len(b)\n    return (torch.tensor(flat, dtype=torch.long, device=dev_t),\n            torch.tensor(offsets, dtype=torch.long, device=dev_t))\n\ntrain_texts = pos_texts + neg_texts\ny = torch.tensor([1.0] * len(pos_texts) + [0.0] * len(neg_texts), device=dev_t)\ntin, toff = build_bag(train_texts)\n\nemb = torch.nn.EmbeddingBag(D, 1, mode=\"mean\").to(dev_t)\ntorch.nn.init.zeros_(emb.weight)\nbias = torch.zeros(1, requires_grad=True, device=dev_t)\nopt = torch.optim.Adam(list(emb.parameters()) + [bias], lr=0.05, weight_decay=1e-6)\n# class-balance weighting\nw_pos = len(train_texts) / (2 * len(pos_texts)); w_neg = len(train_texts) / (2 * len(neg_texts))\nwt = torch.where(y > 0.5, torch.tensor(w_pos, device=dev_t), torch.tensor(w_neg, device=dev_t))\nfor ep in range(EPOCHS):\n    opt.zero_grad()\n    logit = emb(tin, toff).squeeze(1) + bias\n    loss = torch.nn.functional.binary_cross_entropy_with_logits(logit, y, weight=wt)\n    loss.backward(); opt.step()\nwith torch.no_grad():\n    pred = (torch.sigmoid(emb(tin, toff).squeeze(1) + bias) > 0.5).float()\n    acc = (pred == y).float().mean().item()\nprint(f\"train loss {loss.item():.4f}  acc {acc:.3f}\", file=sys.stderr)\n\n# ---------- 5. score whole pool ----------\nscores = np.empty(N, dtype=np.float32)\nB = 8000\nemb.eval()\nwith torch.no_grad():\n    for s in range(0, N, B):\n        e = min(N, s + B)\n        bin_, boff = build_bag(texts[s:e])\n        scores[s:e] = (emb(bin_, boff).squeeze(1) + bias).cpu().numpy()\n\n# ---------- 6. light junk gating ----------\ndef junk(t):\n    n = len(t)\n    if n < 300: return True\n    if sum(c.isalpha() for c in t) / n < 0.55: return True\n    return False\ngate = np.array([not junk(t) for t in texts])\nscores_eff = np.where(gate, scores, -1e9)\n\n# ---------- 7. emit ids by descending score ----------\norder = np.argsort(-scores_eff)\nsel = [int(ids[i]) for i in order[:N_EMIT]]\njson.dump(sel, open(OUT, \"w\"))\nnp.save(\"/tmp/pool_scores.npy\", scores); np.save(\"/tmp/pool_ids.npy\", ids)\nprint(f\"wrote {len(sel)} ids -> {OUT}\", file=sys.stderr)\nprint(\"kept by gate:\", int(gate.sum()), \"/\", N, file=sys.stderr)\nprint(\"score pctiles:\", np.percentile(scores, [50, 90, 99, 99.9]), file=sys.stderr)\n", "structuredPatch": [{"oldStart": 34, "oldLines": 7, "newStart": 34, "newLines": 8, "lines": [" OUT  = \"/workspace/submission/selection.json\"", " SEED = 0", " N_NEG = 15000          # random pool docs as negatives", "-N_EMIT = 60000         # emit far more ids than the 12M-token budget needs", "+KEEP_DROP_PCT = 65     # drop bottom 65% by score among gated docs (tuned on dev PPL)", "+SHUF_SEED = 7          # diversity-preserving shuffle of the kept top band", " D = 1 << 20            # hashed feature buckets", " EPOCHS = 60", " dev_t = \"cuda\" if torch.cuda.is_available() else \"cpu\""]}], "userModified": false, "replaceAll": false}
#40
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolEdit
args
{
  "replace_all": false,
  "file_path": "/workspace/submission/curate.py",
  "old_string": "# ---------- 6. light junk gating ----------\ndef junk(t):\n    n = len(t)\n    if n < 300: return True\n    if sum(c.isalpha() for c in t) / n < 0.55: return True\n    return False\ngate = np.array([not junk(t) for t in texts])\nscores_eff = np.where(gate, scores, -1e9)\n\n# ---------- 7. emit ids by descending score ----------\norder = np.argsort(-scores_eff)\nsel = [int(ids[i]) for i in order[:N_EMIT]]\njson.dump(sel, open(OUT, \"w\"))\nnp.save(\"/tmp/pool_scores.npy\", scores); np.save(\"/tmp/pool_ids.npy\", ids)\nprint(f\"wrote {len(sel)} ids -> {OUT}\", file=sys.stderr)\nprint(\"kept by gate:\", int(gate.sum()), \"/\", N, file=sys.stderr)\nprint(\"score pctiles:\", np.percentile(scores, [50, 90, 99, 99.9]), file=sys.stderr)",
  "new_string": "# ---------- 6. light junk gating ----------\ndef junk(t):\n    n = len(t)\n    if n < 300: return True\n    if sum(c.isalpha() for c in t) / n < 0.55: return True\n    return False\ngate = np.array([not junk(t) for t in texts])\n\n# ---------- 7. drop bottom band, keep diverse top band, shuffle ----------\nthr = np.percentile(scores[gate], KEEP_DROP_PCT)\nkeep = gate & (scores > thr)\nkept_ids = ids[keep]\nkept_ids = np.random.default_rng(SHUF_SEED).permutation(kept_ids)  # diversity, not rank\nsel = [int(i) for i in kept_ids]\njson.dump(sel, open(OUT, \"w\"))\nnp.save(\"/tmp/pool_scores.npy\", scores); np.save(\"/tmp/pool_ids.npy\", ids)\nprint(f\"wrote {len(sel)} ids -> {OUT}\", file=sys.stderr)\nprint(f\"gate kept {int(gate.sum())}/{N}; after drop{KEEP_DROP_PCT}% kept {len(sel)}\", file=sys.stderr)\nprint(\"score pctiles:\", np.percentile(scores, [50, 90, 99, 99.9]), file=sys.stderr)"
}
Edit result
The file /workspace/submission/curate.py has been updated successfully. (file state is current in your context — no need to Read it back)

[metadata] {"filePath": "/workspace/submission/curate.py", "oldString": "# ---------- 6. light junk gating ----------\ndef junk(t):\n    n = len(t)\n    if n < 300: return True\n    if sum(c.isalpha() for c in t) / n < 0.55: return True\n    return False\ngate = np.array([not junk(t) for t in texts])\nscores_eff = np.where(gate, scores, -1e9)\n\n# ---------- 7. emit ids by descending score ----------\norder = np.argsort(-scores_eff)\nsel = [int(ids[i]) for i in order[:N_EMIT]]\njson.dump(sel, open(OUT, \"w\"))\nnp.save(\"/tmp/pool_scores.npy\", scores); np.save(\"/tmp/pool_ids.npy\", ids)\nprint(f\"wrote {len(sel)} ids -> {OUT}\", file=sys.stderr)\nprint(\"kept by gate:\", int(gate.sum()), \"/\", N, file=sys.stderr)\nprint(\"score pctiles:\", np.percentile(scores, [50, 90, 99, 99.9]), file=sys.stderr)", "newString": "# ---------- 6. light junk gating ----------\ndef junk(t):\n    n = len(t)\n    if n < 300: return True\n    if sum(c.isalpha() for c in t) / n < 0.55: return True\n    return False\ngate = np.array([not junk(t) for t in texts])\n\n# ---------- 7. drop bottom band, keep diverse top band, shuffle ----------\nthr = np.percentile(scores[gate], KEEP_DROP_PCT)\nkeep = gate & (scores > thr)\nkept_ids = ids[keep]\nkept_ids = np.random.default_rng(SHUF_SEED).permutation(kept_ids)  # diversity, not rank\nsel = [int(i) for i in kept_ids]\njson.dump(sel, open(OUT, \"w\"))\nnp.save(\"/tmp/pool_scores.npy\", scores); np.save(\"/tmp/pool_ids.npy\", ids)\nprint(f\"wrote {len(sel)} ids -> {OUT}\", file=sys.stderr)\nprint(f\"gate kept {int(gate.sum())}/{N}; after drop{KEEP_DROP_PCT}% kept {len(sel)}\", file=sys.stderr)\nprint(\"score pctiles:\", np.percentile(scores, [50, 90, 99, 99.9]), file=sys.stderr)", "originalFile": "\"\"\"Curate a raw web pool for small-LM pretraining toward a BROAD multi-domain\nhigh-quality English target (encyclopedic / web prose / news / technical Q&A).\n\nStated criterion\n----------------\nQUALITY-FILTER, THEN PRESERVE DIVERSITY. The target is a *broad, 4-way* mixture\n(encyclopedic / web prose / news / technical Q&A). Empirically (dev-PPL sweep),\nhard-ranking the pool by a single \"target-likeness\" score and taking the extreme\ntop collapses register diversity and does *worse* than a random draw (top-only\ndev PPL 502 vs random 477). The minimum-perplexity recipe instead is:\n\n  1. Score each pool doc with a quality classifier (feature-hashed bag-of-ngrams\n     logistic regression, torch EmbeddingBag on GPU, no sklearn dependency),\n     trained: positive := chunks of the disclosed target (data/multi_dev.npy\n     decoded to text, split on <|endoftext|>); negative := random pool sample.\n  2. Apply a light junk gate (min length, min alphabetic fraction).\n  3. DROP the bottom KEEP_DROP_PCT of surviving docs by score (removes SEO/boiler-\n     plate/link-spam tail), then keep the *whole* top band and emit it in a\n     DIVERSITY-PRESERVING shuffled order — NOT sorted by score. The pipeline packs\n     ~12M tokens by randomly sampling this clean, broad top band, so all four\n     registers stay represented.\n\nThe drop percentile was tuned on dev PPL (drop 35%->392, 50%->389, 65%->379,\n78%->405, 88%->443): a clear optimum at keeping the top ~35% by quality while\nretaining diversity. Best dev PPL 379 vs random baseline 477.\n\nReproducible: `python3 curate.py` -> writes submission/selection.json.\n\"\"\"\nimport json, re, sys, numpy as np, torch\nfrom transformers import AutoTokenizer\n\nPOOL = \"/workspace/data/pool.jsonl\"\nDEV  = \"/workspace/data/multi_dev.npy\"\nOUT  = \"/workspace/submission/selection.json\"\nSEED = 0\nN_NEG = 15000          # random pool docs as negatives\nKEEP_DROP_PCT = 65     # drop bottom 65% by score among gated docs (tuned on dev PPL)\nSHUF_SEED = 7          # diversity-preserving shuffle of the kept top band\nD = 1 << 20            # hashed feature buckets\nEPOCHS = 60\ndev_t = \"cuda\" if torch.cuda.is_available() else \"cpu\"\ntorch.manual_seed(SEED); rng = np.random.default_rng(SEED)\n\n_word = re.compile(r\"[a-z0-9']+\")\ndef ngram_buckets(text):\n    \"\"\"Hash word unigrams + bigrams to buckets in [0, D).\"\"\"\n    toks = _word.findall(text.lower())\n    if not toks:\n        return [0]\n    out = [(hash(t) & (D - 1)) for t in toks]\n    for i in range(len(toks) - 1):\n        out.append((hash(toks[i] + \" \" + toks[i + 1]) & (D - 1)))\n    return out\n\n# ---------- 1. positives: decode the disclosed target back to text ----------\ntok = AutoTokenizer.from_pretrained(\"gpt2\")\nEOS = tok.eos_token_id\ndev = np.load(DEV).astype(np.int64)\npos_texts, cur = [], []\nfor t in dev:\n    if t == EOS:\n        if cur: pos_texts.append(tok.decode(cur))\n        cur = []\n    else:\n        cur.append(int(t))\nif cur: pos_texts.append(tok.decode(cur))\n\ndef deartifact(s):  # strip WikiText tokenization quirks the pool can't contain\n    return s.replace(\" @,@ \", \",\").replace(\" @-@ \", \"-\").replace(\" @.@ \", \".\")\npos_texts = [deartifact(s) for s in pos_texts if len(s) > 200]\n\n# ---------- 2. 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\"])\nids = np.array(ids); N = len(ids)\nprint(f\"pool docs: {N}  positives: {len(pos_texts)}\", file=sys.stderr)\n\n# ---------- 3. negatives: random pool sample ----------\nneg_idx = rng.choice(N, size=min(N_NEG, N), replace=False)\nneg_texts = [texts[i] for i in neg_idx]\n\n# ---------- 4. featurize + train logistic regression (EmbeddingBag on GPU) ----------\ndef build_bag(text_list):\n    flat, offsets, off = [], [], 0\n    for t in text_list:\n        b = ngram_buckets(t)\n        offsets.append(off); flat.extend(b); off += len(b)\n    return (torch.tensor(flat, dtype=torch.long, device=dev_t),\n            torch.tensor(offsets, dtype=torch.long, device=dev_t))\n\ntrain_texts = pos_texts + neg_texts\ny = torch.tensor([1.0] * len(pos_texts) + [0.0] * len(neg_texts), device=dev_t)\ntin, toff = build_bag(train_texts)\n\nemb = torch.nn.EmbeddingBag(D, 1, mode=\"mean\").to(dev_t)\ntorch.nn.init.zeros_(emb.weight)\nbias = torch.zeros(1, requires_grad=True, device=dev_t)\nopt = torch.optim.Adam(list(emb.parameters()) + [bias], lr=0.05, weight_decay=1e-6)\n# class-balance weighting\nw_pos = len(train_texts) / (2 * len(pos_texts)); w_neg = len(train_texts) / (2 * len(neg_texts))\nwt = torch.where(y > 0.5, torch.tensor(w_pos, device=dev_t), torch.tensor(w_neg, device=dev_t))\nfor ep in range(EPOCHS):\n    opt.zero_grad()\n    logit = emb(tin, toff).squeeze(1) + bias\n    loss = torch.nn.functional.binary_cross_entropy_with_logits(logit, y, weight=wt)\n    loss.backward(); opt.step()\nwith torch.no_grad():\n    pred = (torch.sigmoid(emb(tin, toff).squeeze(1) + bias) > 0.5).float()\n    acc = (pred == y).float().mean().item()\nprint(f\"train loss {loss.item():.4f}  acc {acc:.3f}\", file=sys.stderr)\n\n# ---------- 5. score whole pool ----------\nscores = np.empty(N, dtype=np.float32)\nB = 8000\nemb.eval()\nwith torch.no_grad():\n    for s in range(0, N, B):\n        e = min(N, s + B)\n        bin_, boff = build_bag(texts[s:e])\n        scores[s:e] = (emb(bin_, boff).squeeze(1) + bias).cpu().numpy()\n\n# ---------- 6. light junk gating ----------\ndef junk(t):\n    n = len(t)\n    if n < 300: return True\n    if sum(c.isalpha() for c in t) / n < 0.55: return True\n    return False\ngate = np.array([not junk(t) for t in texts])\nscores_eff = np.where(gate, scores, -1e9)\n\n# ---------- 7. emit ids by descending score ----------\norder = np.argsort(-scores_eff)\nsel = [int(ids[i]) for i in order[:N_EMIT]]\njson.dump(sel, open(OUT, \"w\"))\nnp.save(\"/tmp/pool_scores.npy\", scores); np.save(\"/tmp/pool_ids.npy\", ids)\nprint(f\"wrote {len(sel)} ids -> {OUT}\", file=sys.stderr)\nprint(\"kept by gate:\", int(gate.sum()), \"/\", N, file=sys.stderr)\nprint(\"score pctiles:\", np.percentile(scores, [50, 90, 99, 99.9]), file=sys.stderr)\n", "structuredPatch": [{"oldStart": 129, "oldLines": 13, "newStart": 129, "newLines": 15, "lines": ["     if sum(c.isalpha() for c in t) / n < 0.55: return True", "     return False", " gate = np.array([not junk(t) for t in texts])", "-scores_eff = np.where(gate, scores, -1e9)", " ", "-# ---------- 7. emit ids by descending score ----------", "-order = np.argsort(-scores_eff)", "-sel = [int(ids[i]) for i in order[:N_EMIT]]", "+# ---------- 7. drop bottom band, keep diverse top band, shuffle ----------", "+thr = np.percentile(scores[gate], KEEP_DROP_PCT)", "+keep = gate & (scores > thr)", "+kept_ids = ids[keep]", "+kept_ids = np.random.default_rng(SHUF_SEED).permutation(kept_ids)  # diversity, not rank", "+sel = [int(i) for i in kept_ids]", " json.dump(sel, open(OUT, \"w\"))", " np.save(\"/tmp/pool_scores.npy\", scores); np.save(\"/tmp/pool_ids.npy\", ids)", " print(f\"wrote {len(sel)} ids -> {OUT}\", file=sys.stderr)", "-print(\"kept by gate:\", int(gate.sum()), \"/\", N, file=sys.stderr)", "+print(f\"gate kept {int(gate.sum())}/{N}; after drop{KEEP_DROP_PCT}% kept {len(sel)}\", file=sys.stderr)", " print(\"score pctiles:\", np.percentile(scores, [50, 90, 99, 99.9]), file=sys.stderr)"]}], "userModified": false, "replaceAll": false}
#41
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashCheck sweep3 60,70
args
{
  "command": "sleep 60; grep -E \"drop|HELDOUT\" /tmp/sweep3.log; echo REM $(cat /workspace/.timer/remaining_secs)",
  "description": "Check sweep3 60,70"
}
Bash result
=== drop60: kept 70841 ===
REM 3328

[stdout]
=== drop60: kept 70841 ===
REM 3328
[metadata] {"noOutputExpected": false}
#42
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashCheck sweep3 results
args
{
  "command": "sleep 105; grep -E \"drop|HELDOUT\" /tmp/sweep3.log; echo REM $(cat /workspace/.timer/remaining_secs)",
  "description": "Check sweep3 results"
}
Bash result
=== drop60: kept 70841 ===
HELDOUT_PPL 392.109  loss 5.9715  train_s 50  /tmp/dev_train.npy
=== drop70: kept 53131 ===
REM 3217

[stdout]
=== drop60: kept 70841 ===
HELDOUT_PPL 392.109  loss 5.9715  train_s 50  /tmp/dev_train.npy
=== drop70: kept 53131 ===
REM 3217
[metadata] {"noOutputExpected": false}
#43
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashCheck drop70
args
{
  "command": "sleep 70; grep -E \"drop|HELDOUT\" /tmp/sweep3.log; echo REM $(cat /workspace/.timer/remaining_secs)",
  "description": "Check drop70"
}
Bash result
=== drop60: kept 70841 ===
HELDOUT_PPL 392.109  loss 5.9715  train_s 50  /tmp/dev_train.npy
=== drop70: kept 53131 ===
HELDOUT_PPL 417.832  loss 6.0351  train_s 50  /tmp/dev_train.npy
REM 3137

[stdout]
=== drop60: kept 70841 ===
HELDOUT_PPL 392.109  loss 5.9715  train_s 50  /tmp/dev_train.npy
=== drop70: kept 53131 ===
HELDOUT_PPL 417.832  loss 6.0351  train_s 50  /tmp/dev_train.npy
REM 3137
[metadata] {"noOutputExpected": false}
#44
narration

Best is drop65 = 379 (drop50-65 all ~379-392; noise ~±10). Locking in KEEP_DROP_PCT=65. Now regenerate the official selection.json via curate.py and do a confirmatory eval.

toolTaskUpdate
args
{
  "taskId": "3",
  "status": "in_progress"
}
TaskUpdate result
Updated task #3 status

[metadata] {"success": true, "taskId": "3", "updatedFields": ["status"], "statusChange": {"from": "pending", "to": "in_progress"}}
#45
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashGenerate final selection.json
args
{
  "command": "cd /workspace; python3 submission/curate.py 2>&1 | grep -v -i warning | tail -6",
  "description": "Generate final selection.json"
}
Bash result
pool docs: 182016  positives: 2557
train loss 0.4718  acc 0.926
wrote 61986 ids -> /workspace/submission/selection.json
gate kept 177103/182016; after drop65% kept 61986
score pctiles: [-0.53728476 -0.03041644  0.30954432  0.5115409 ]

[stdout]
pool docs: 182016  positives: 2557
train loss 0.4718  acc 0.926
wrote 61986 ids -> /workspace/submission/selection.json
gate kept 177103/182016; after drop65% kept 61986
score pctiles: [-0.53728476 -0.03041644  0.30954432  0.5115409 ]
[metadata] {"noOutputExpected": false}
#46
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashValidate final selection
args
{
  "command": "cd /workspace; python3 -c \"\nimport json,numpy as np\na=json.load(open('submission/selection.json')); b=json.load(open('/tmp/drop65.json'))\nprint('matches drop65 (scored 379):', a==b)\nprint('n ids',len(a),'unique',len(set(a)),'min',min(a),'max',max(a))\npoolids=set()\nfor line in open('data/pool.jsonl'): poolids.add(json.loads(line)['id'])\nprint('all in pool:', set(a)<=poolids)\n# token coverage estimate\ntext={}\nfor line in open('data/pool.jsonl'):\n    r=json.loads(line); text[r['id']]=len(r['text'])\nprint('approx tokens available in selection:', int(sum(text[i] for i in a)/4/1e6),'M (need 12M)')\n\"",
  "description": "Validate final selection"
}
Bash result
matches drop65 (scored 379): True
n ids 61986 unique 61986 min 1 max 182015
all in pool: True
approx tokens available in selection: 69 M (need 12M)

[stdout]
matches drop65 (scored 379): True
n ids 61986 unique 61986 min 1 max 182015
all in pool: True
approx tokens available in selection: 69 M (need 12M)
[metadata] {"noOutputExpected": false}
#47
narration

Valid: 61,986 unique in-range ids, ~69M tokens available (5.7× budget), byte-identical to the drop65 config that scored 379 on dev (deterministic pipeline, so official reproduces it). Now the claim.

toolWrite
args
{
  "file_path": "/workspace/claim.md",
  "content": "# Curation claim\n\n## Hypothesis\nFor a **broad, multi-domain** perplexity target (encyclopedic / web prose / news /\ntechnical Q&A) under a *fixed small token budget*, the minimum-perplexity\nselection is **not** \"the documents that look most like the target.\" It is\n**\"drop the low-quality tail, then keep the widest possible diverse band.\"**\nConcretely: score every pool doc with a target-vs-random quality classifier,\nremove a light junk floor plus the bottom ~65% by score, and fill the budget by\n**randomly sampling the surviving top ~35% band** (diversity-preserving order),\n*not* by taking the highest-scoring documents. Quality filtering helps only up to\nthe point where it starts eroding register diversity; past that it hurts.\n\n## Mechanism (prediction of an observable other than final perplexity)\nThe controlling variable is **register/vocabulary coverage of the training set**,\nnot average document quality. Two falsifiable, non-perplexity observables:\n\n1. **Monotone-vs-U-shape.** As the drop percentile `p` increases from 0 to 100,\n   held-out loss is **U-shaped**, not monotone: a light filter helps, but an\n   aggressive filter that hard-ranks toward the target mode removes whole\n   registers and reverses the gain. Predicted minimum at \"keep top ~30–40%.\"\n   *Observed on dev:* drop 35%→392, 50%→389, **65%→379**, 78%→405, 88%→443, and\n   pure top-ranking (≈drop 93%)→**502, worse than random 477**. The reversal at\n   high `p` is the mechanism's fingerprint.\n2. **Diversity metric.** The rank-concentrated (top-only) 12M-token training set\n   has **measurably lower lexical/topic diversity** than the cleaned-broad set:\n   fewer distinct documents packed, lower unigram entropy, and a smaller share of\n   the rarer target registers (e.g. code/HTML technical Q&A). Prediction: sort the\n   candidate selections by that diversity metric and the ordering matches the\n   held-out-loss ordering — the broad set is both more diverse and lower-loss.\n\n## Falsification\nThe hypothesis is wrong if any of these hold:\n- Held-out loss is **monotonically decreasing** in the drop percentile `p` (i.e.\n  taking the strict top-k beats the shuffled broad band). Not observed — top-only\n  scored 502 > random 477.\n- A **random** 12M-token draw beats the cleaned-broad selection (would mean the\n  quality floor removes nothing useful). Not observed — 477 vs 379.\n- Held-out loss does **not** correlate with the training-set diversity metric\n  across the swept selections (would mean coverage is not the driver).\n\n## Transfer\nThe recipe transfers to any fixed-budget pretraining-data selection for a **broad\nor mixture** evaluation target: build a cheap target-vs-pool quality scorer, use\nit only to **excise the low-quality tail**, and preserve diversity by sampling the\nretained majority rather than ranking into it. It should **not** transfer to a\n**narrow, single-register** target (e.g. perplexity on code only): there,\nconcentrating on the highest-scoring in-domain documents is expected to win,\nbecause there is no cross-register diversity to protect. The optimal drop\npercentile is a tunable knob that moves toward 0 as the target broadens and toward\n100 as it narrows.\n"
}
Write result
File created successfully at: /workspace/claim.md (file state is current in your context — no need to Read it back)

[metadata] {"type": "create", "filePath": "/workspace/claim.md", "content": "# Curation claim\n\n## Hypothesis\nFor a **broad, multi-domain** perplexity target (encyclopedic / web prose / news /\ntechnical Q&A) under a *fixed small token budget*, the minimum-perplexity\nselection is **not** \"the documents that look most like the target.\" It is\n**\"drop the low-quality tail, then keep the widest possible diverse band.\"**\nConcretely: score every pool doc with a target-vs-random quality classifier,\nremove a light junk floor plus the bottom ~65% by score, and fill the budget by\n**randomly sampling the surviving top ~35% band** (diversity-preserving order),\n*not* by taking the highest-scoring documents. Quality filtering helps only up to\nthe point where it starts eroding register diversity; past that it hurts.\n\n## Mechanism (prediction of an observable other than final perplexity)\nThe controlling variable is **register/vocabulary coverage of the training set**,\nnot average document quality. Two falsifiable, non-perplexity observables:\n\n1. **Monotone-vs-U-shape.** As the drop percentile `p` increases from 0 to 100,\n   held-out loss is **U-shaped**, not monotone: a light filter helps, but an\n   aggressive filter that hard-ranks toward the target mode removes whole\n   registers and reverses the gain. Predicted minimum at \"keep top ~30–40%.\"\n   *Observed on dev:* drop 35%→392, 50%→389, **65%→379**, 78%→405, 88%→443, and\n   pure top-ranking (≈drop 93%)→**502, worse than random 477**. The reversal at\n   high `p` is the mechanism's fingerprint.\n2. **Diversity metric.** The rank-concentrated (top-only) 12M-token training set\n   has **measurably lower lexical/topic diversity** than the cleaned-broad set:\n   fewer distinct documents packed, lower unigram entropy, and a smaller share of\n   the rarer target registers (e.g. code/HTML technical Q&A). Prediction: sort the\n   candidate selections by that diversity metric and the ordering matches the\n   held-out-loss ordering — the broad set is both more diverse and lower-loss.\n\n## Falsification\nThe hypothesis is wrong if any of these hold:\n- Held-out loss is **monotonically decreasing** in the drop percentile `p` (i.e.\n  taking the strict top-k beats the shuffled broad band). Not observed — top-only\n  scored 502 > random 477.\n- A **random** 12M-token draw beats the cleaned-broad selection (would mean the\n  quality floor removes nothing useful). Not observed — 477 vs 379.\n- Held-out loss does **not** correlate with the training-set diversity metric\n  across the swept selections (would mean coverage is not the driver).\n\n## Transfer\nThe recipe transfers to any fixed-budget pretraining-data selection for a **broad\nor mixture** evaluation target: build a cheap target-vs-pool quality scorer, use\nit only to **excise the low-quality tail**, and preserve diversity by sampling the\nretained majority rather than ranking into it. It should **not** transfer to a\n**narrow, single-register** target (e.g. perplexity on code only): there,\nconcentrating on the highest-scoring in-domain documents is expected to win,\nbecause there is no cross-register diversity to protect. The optimal drop\npercentile is a tunable knob that moves toward 0 as the target broadens and toward\n100 as it narrows.\n", "structuredPatch": [], "originalFile": null, "userModified": false}
#48
narration

Let me run one final confirmatory eval on the official selection.json end-to-end.

toolBashFinal confirmatory eval
args
{
  "command": "cd /workspace; bash dev_eval.sh 2>&1 | grep -E \"HELDOUT_PPL|packed\"; echo REM $(cat /workspace/.timer/remaining_secs)",
  "description": "Final confirmatory eval"
}
Bash result
packed 12000000 tokens from 61986 ids -> /tmp/dev_train.npy
HELDOUT_PPL 378.659  loss 5.9366  train_s 50  /tmp/dev_train.npy
REM 2867

[stdout]
packed 12000000 tokens from 61986 ids -> /tmp/dev_train.npy
HELDOUT_PPL 378.659  loss 5.9366  train_s 50  /tmp/dev_train.npy
REM 2867
[metadata] {"noOutputExpected": false}
#49
narration

Confirmed end-to-end: official selection.jsondev PPL 378.7, a 20.6% improvement over the random baseline (476.7).

toolTaskUpdate
args
{
  "taskId": "2",
  "status": "completed"
}
TaskUpdate result
Updated task #2 status

[metadata] {"success": true, "taskId": "2", "updatedFields": ["status"], "statusChange": {"from": "in_progress", "to": "completed"}}
#50
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolTaskUpdate
args
{
  "taskId": "3",
  "status": "completed"
}
TaskUpdate result
Updated task #3 status

[metadata] {"success": true, "taskId": "3", "updatedFields": ["status"], "statusChange": {"from": "in_progress", "to": "completed"}}
final summary

All three deliverables are complete and verified.

Summary

Result: dev held-out perplexity 378.7 vs 476.7 random baseline — a 20.6% reduction.

What I found (the key insight): My first instinct — a quality classifier that ranks documents by target-likeness and takes the strict top — actually scored *502, worse than random (477)*. Decoding the dev target showed it's a genuine 4-way mixture (Wikipedia, web prose, news, StackOverflow Q&A). Hard-ranking toward "most target-like" collapses that register diversity and hurts a fixed-budget LM.

The winning recipe (in curate.py):

  1. Train a target-vs-random-pool quality classifier — feature-hashed bag-of-ngrams logistic regression as a torch EmbeddingBag on GPU (no sklearn; none was installable offline).
  2. Apply a light junk gate (min length, min alphabetic fraction).
  3. *Drop the bottom 65% by score, then keep the whole top band and emit it in a diversity-preserving shuffled order* — not sorted by score. The pipeline packs 12M tokens by broadly sampling this clean band, so all four registers stay represented.

The drop percentile was tuned on a dev-PPL sweep, which traced a clear U-shape (drop 35%→392, 50%→389, 65%→379, 78%→405, 88%→443, top-only→502) — the minimum sits at keeping the diverse top ~35% by quality.

Deliverables:

  • /workspace/submission/selection.json — 61,986 valid unique ids (~69M tokens, 5.7× budget), priority order = shuffled clean top band.
  • /workspace/submission/curate.py — reproducible, stated criterion.
  • /workspace/claim.md — hypothesis (filter-then-diversify beats rank-concentrate for broad targets), mechanism with a non-perplexity observable (the U-shape and a diversity metric that should track loss ordering), falsification conditions, and transfer (narrows toward strict top-k as the target narrows).