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
42
duration
39m
recorded
2026-08-04

run iddclm-nano-curation__RKmSGRj

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

download JSON ↗
claude-codeclaude-opus-4-842 turns28 narration42 action42 observation14 reasoning1 final
#1
narration

I'll start by surveying the environment, data, and constraints.

toolBashSurvey environment
args
{
  "command": "cat /workspace/.timer/remaining_secs; echo \"---\"; ls -la /workspace; echo \"---\"; ls -la /workspace/data; echo \"---\"; ls -la /workspace/submission 2>/dev/null; echo \"---nvidia---\"; nvidia-smi --query-gpu=name,memory.total,memory.used --format=csv 2>/dev/null",
  "description": "Survey environment"
}
Bash result
5400
---
total 14
drwxr-xr-x 1 root root  100 Aug  4 03:22 .
drwxr-xr-x 1 root root  160 Aug  4 03:22 ..
drwxr-xr-x 1 root root   60 Aug  4 03:22 .live
drwxr-xr-x 1 root root  100 Aug  4 03:22 .metrics
drwxr-xr-x 1 root root  140 Aug  4 03:22 .timer
drwxr-xr-x 1 root root   57 Aug  3 22:37 data
-rwxrwxr-x 1 root root  714 Aug  3 22:34 dev_eval.sh
-rw-rw-r-- 1 root root 5706 Aug  3 22:34 model.py
-rw-rw-r-- 1 root root  869 Aug  3 22:34 pack_selection.py
drwxr-xr-x 1 root root   10 Aug  3 22:37 submission
-rwxrwxr-x 1 root root 1509 Aug  3 22:34 timer.sh
-rw-rw-r-- 1 root root 2962 Aug  3 22:34 train_nano.py
---
total 792657
drwxr-xr-x 1 root root        57 Aug  3 22:37 .
drwxr-xr-x 1 root root       100 Aug  4 03:22 ..
-r--r--r-- 1 root root   2000128 Aug  3 22:34 multi_dev.npy
-r--r--r-- 1 root root 809679384 Aug  3 22:35 pool.jsonl
---
total 1
drwxr-xr-x 1 root root  10 Aug  3 22:37 .
drwxr-xr-x 1 root root 100 Aug  4 03:22 ..
---nvidia---
name, memory.total [MiB], memory.used [MiB]
NVIDIA H100 80GB HBM3, 81559 MiB, 4 MiB

[stdout]
5400
---
total 14
drwxr-xr-x 1 root root  100 Aug  4 03:22 .
drwxr-xr-x 1 root root  160 Aug  4 03:22 ..
drwxr-xr-x 1 root root   60 Aug  4 03:22 .live
drwxr-xr-x 1 root root  100 Aug  4 03:22 .metrics
drwxr-xr-x 1 root root  140 Aug  4 03:22 .timer
drwxr-xr-x 1 root root   57 Aug  3 22:37 data
-rwxrwxr-x 1 root root  714 Aug  3 22:34 dev_eval.sh
-rw-rw-r-- 1 root root 5706 Aug  3 22:34 model.py
-rw-rw-r-- 1 root root  869 Aug  3 22:34 pack_selection.py
drwxr-xr-x 1 root root   10 Aug  3 22:37 submission
-rwxrwxr-x 1 root root 1509 Aug  3 22:34 timer.sh
-rw-rw-r-- 1 root root 2962 Aug  3 22:34 train_nano.py
---
total 792657
drwxr-xr-x 1 root root        57 Aug  3 22:37 .
drwxr-xr-x 1 root root       100 Aug  4 03:22 ..
-r--r--r-- 1 root root   2000128 Aug  3 22:34 multi_dev.npy
-r--r--r-- 1 root root 809679384 Aug  3 22:35 pool.jsonl
---
total 1
drwxr-xr-x 1 root root  10 Aug  3 22:37 .
drwxr-xr-x 1 root root 100 Aug  4 03:22 ..
---nvidia---
name, memory.total [MiB], memory.used [MiB]
NVIDIA H100 80GB HBM3, 81559 MiB, 4 MiB
[metadata] {"noOutputExpected": false}
#2
narration

90 minutes total. Let me read the key files.

toolBashRead training scripts
args
{
  "command": "cd /workspace; echo \"=== train_nano.py ===\"; cat train_nano.py; echo \"=== pack_selection.py ===\"; cat pack_selection.py; echo \"=== dev_eval.sh ===\"; cat dev_eval.sh; echo \"=== timer.sh ===\"; cat timer.sh",
  "description": "Read training scripts"
}
Bash result
=== train_nano.py ===
"""From-scratch nano-GPT (30M) LM training on a 1D token .npy; report held-out perplexity.
Frozen recipe — the ONLY thing that varies between runs is the training token set."""
import argparse, math, time, numpy as np, torch
from model import GPT, GPTConfig

ap = argparse.ArgumentParser()
ap.add_argument("--train_npy", required=True)
ap.add_argument("--heldout_npy", required=True)
ap.add_argument("--out_json", required=True)
ap.add_argument("--max_iters", type=int, default=3000)
ap.add_argument("--warmup", type=int, default=150)
ap.add_argument("--batch", type=int, default=32)
ap.add_argument("--block", type=int, default=256)
ap.add_argument("--lr", type=float, default=6e-4)
ap.add_argument("--seed", type=int, default=1337)
a = ap.parse_args()

torch.manual_seed(a.seed); np.random.seed(a.seed)
dev = "cuda"
tr = np.load(a.train_npy); ho = np.load(a.heldout_npy)
tr = torch.from_numpy(tr.astype(np.int64)); ho = torch.from_numpy(ho.astype(np.int64))
rng = np.random.default_rng(a.seed)

model = GPT(GPTConfig(block_size=a.block, vocab_size=50257, n_layer=6, n_head=6,
                      n_embd=384, dropout=0.0, bias=False)).to(dev)
opt = model.configure_optimizers(0.1, a.lr, (0.9, 0.95), "cuda")

def lr_at(it):
    if it < a.warmup: return a.lr * (it + 1) / (a.warmup + 1)
    r = (it - a.warmup) / max(1, a.max_iters - a.warmup)
    return 0.1 * a.lr + 0.5 * (1 + math.cos(math.pi * r)) * (a.lr - 0.1 * a.lr)

def get_batch(src):
    ix = rng.integers(0, len(src) - a.block - 1, size=a.batch)
    x = torch.stack([src[i:i+a.block] for i in ix]).to(dev)
    y = torch.stack([src[i+1:i+1+a.block] for i in ix]).to(dev)
    return x, y

t0 = time.time()
model.train()
for it in range(a.max_iters):
    for g in opt.param_groups: g["lr"] = lr_at(it)
    x, y = get_batch(tr)
    with torch.autocast("cuda", dtype=torch.bfloat16):
        _, loss = model(x, y)
    opt.zero_grad(set_to_none=True); loss.backward()
    torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0); opt.step()
train_s = time.time() - t0

# held-out perplexity: mean next-token CE over non-overlapping windows (window-weighted)
import json
model.eval()
nwin = (len(ho) - 1) // a.block
starts = [j * a.block for j in range(nwin)]
sum_loss, nseen = 0.0, 0
with torch.no_grad():
    for i in range(0, nwin, a.batch):
        bs = starts[i:i+a.batch]
        x = torch.stack([ho[s:s+a.block] for s in bs]).to(dev)
        y = torch.stack([ho[s+1:s+1+a.block] for s in bs]).to(dev)
        with torch.autocast("cuda", dtype=torch.bfloat16):
            _, l = model(x, y)
        sum_loss += l.item() * len(bs); nseen += len(bs)
mean_loss = sum_loss / max(1, nseen)
ppl = math.exp(mean_loss)
out = {"train_npy": a.train_npy, "heldout_ppl": ppl, "mean_loss": mean_loss,
       "max_iters": a.max_iters, "seed": a.seed, "train_s": round(train_s, 1)}
json.dump(out, open(a.out_json, "w"))
print(f"HELDOUT_PPL {ppl:.3f}  loss {mean_loss:.4f}  train_s {train_s:.0f}  {a.train_npy}")
=== pack_selection.py ===
"""Solver-side: pack a selection.json (ordered ids) into a 12M-token train array from pool.jsonl."""
import json, sys, numpy as np
from transformers import AutoTokenizer

POOL = "/workspace/data/pool.jsonl"
SEL = sys.argv[1] if len(sys.argv) > 1 else "/workspace/submission/selection.json"
OUT = sys.argv[2] if len(sys.argv) > 2 else "/tmp/dev_train.npy"
BUDGET = 12_000_000

tok = AutoTokenizer.from_pretrained("gpt2"); EOS = tok.eos_token_id
text = {}
for line in open(POOL):
    r = json.loads(line); text[r["id"]] = r["text"]
sel = json.load(open(SEL))
parts, tot = [], 0
for i in sel:
    ids = tok(text[i], add_special_tokens=False).input_ids
    parts.extend(ids); parts.append(EOS); tot += len(ids) + 1
    if tot >= BUDGET: break
arr = np.array(parts[:BUDGET], dtype=np.uint16)
np.save(OUT, arr)
print(f"packed {len(arr)} tokens from {len(sel)} ids -> {OUT}")
=== dev_eval.sh ===
#!/bin/bash
# Diagnostic: train the frozen 30M GPT on your selection and report DEV perplexity.
# Usage: bash dev_eval.sh [selection.json]   (default /workspace/submission/selection.json)
# NOTE: the official target is a DIFFERENT (hidden) sample from the same HQ domain.
set -euo pipefail
export TOKENIZERS_PARALLELISM=false
SEL="${1:-/workspace/submission/selection.json}"
cd /workspace
python3 pack_selection.py "$SEL" /tmp/dev_train.npy
python3 train_nano.py --train_npy /tmp/dev_train.npy --heldout_npy /workspace/data/multi_dev.npy \
    --out_json /tmp/dev.json --max_iters 3000 --seed 1337 2>&1 | grep HELDOUT_PPL
echo "(dev PPL on multi_dev; official uses a hidden disjoint sample of the same HQ domain)"
=== timer.sh ===
#!/usr/bin/env bash
# Budget timer daemon — writes /workspace/.timer/remaining_secs every 10s so the
# solver can poll time left instead of doing wall-clock arithmetic.
#
# Budget (seconds) comes from $TASK_BUDGET_SECS (set by the runner to the agent
# timeout). Idempotent via a PID file. On ScaleTrain the trial runner starts its
# own timer; this file is the fallback for the Modal/local harbor backends, which
# bootstrap /workspace/timer.sh on environment start. The fallback default below
# (7200 = 2 h) matches this task's [agent] timeout_sec and is only used if
# $TASK_BUDGET_SECS is unset.
set -u
TIMER_DIR="/workspace/.timer"
PID_FILE="$TIMER_DIR/timer.pid"
mkdir -p "$TIMER_DIR"

if [ -s "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE" 2>/dev/null)" 2>/dev/null; then
    exit 0   # already running
fi
echo $$ > "$PID_FILE"
trap 'rm -f "$PID_FILE"' EXIT INT TERM

START=$(date +%s)
BUDGET="${TASK_BUDGET_SECS:-7200}"
echo "$START" > "$TIMER_DIR/start_epoch"
echo "$BUDGET" > "$TIMER_DIR/budget_secs"

while true; do
    NOW=$(date +%s)
    ELAPSED=$((NOW - START))
    REMAINING=$((BUDGET - ELAPSED))
    [ "$REMAINING" -lt 0 ] && REMAINING=0
    echo "$REMAINING" > "$TIMER_DIR/remaining_secs"
    echo "$ELAPSED" > "$TIMER_DIR/elapsed_secs"
    [ "$REMAINING" -le 1800 ] && [ ! -f "$TIMER_DIR/alert_30min" ] && touch "$TIMER_DIR/alert_30min"
    [ "$REMAINING" -le 600 ]  && [ ! -f "$TIMER_DIR/alert_10min" ] && touch "$TIMER_DIR/alert_10min"
    [ "$REMAINING" -le 0 ] && break
    sleep 10
done

[stdout]
=== train_nano.py ===
"""From-scratch nano-GPT (30M) LM training on a 1D token .npy; report held-out perplexity.
Frozen recipe — the ONLY thing that varies between runs is the training token set."""
import argparse, math, time, numpy as np, torch
from model import GPT, GPTConfig

ap = argparse.ArgumentParser()
ap.add_argument("--train_npy", required=True)
ap.add_argument("--heldout_npy", required=True)
ap.add_argument("--out_json", required=True)
ap.add_argument("--max_iters", type=int, default=3000)
ap.add_argument("--warmup", type=int, default=150)
ap.add_argument("--batch", type=int, default=32)
ap.add_argument("--block", type=int, default=256)
ap.add_argument("--lr", type=float, default=6e-4)
ap.add_argument("--seed", type=int, default=1337)
a = ap.parse_args()

torch.manual_seed(a.seed); np.random.seed(a.seed)
dev = "cuda"
tr = np.load(a.train_npy); ho = np.load(a.heldout_npy)
tr = torch.from_numpy(tr.astype(np.int64)); ho = torch.from_numpy(ho.astype(np.int64))
rng = np.random.default_rng(a.seed)

model = GPT(GPTConfig(block_size=a.block, vocab_size=50257, n_layer=6, n_head=6,
                      n_embd=384, dropout=0.0, bias=False)).to(dev)
opt = model.configure_optimizers(0.1, a.lr, (0.9, 0.95), "cuda")

def lr_at(it):
    if it < a.warmup: return a.lr * (it + 1) / (a.warmup + 1)
    r = (it - a.warmup) / max(1, a.max_iters - a.warmup)
    return 0.1 * a.lr + 0.5 * (1 + math.cos(math.pi * r)) * (a.lr - 0.1 * a.lr)

def get_batch(src):
    ix = rng.integers(0, len(src) - a.block - 1, size=a.batch)
    x = torch.stack([src[i:i+a.block] for i in ix]).to(dev)
    y = torch.stack([src[i+1:i+1+a.block] for i in ix]).to(dev)
    return x, y

t0 = time.time()
model.train()
for it in range(a.max_iters):
    for g in opt.param_groups: g["lr"] = lr_at(it)
    x, y = get_batch(tr)
    with torch.autocast("cuda", dtype=torch.bfloat16):
        _, loss = model(x, y)
    opt.zero_grad(set_to_none=True); loss.backward()
    torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0); opt.step()
train_s = time.time() - t0

# held-out perplexity: mean next-token CE over non-overlapping windows (window-weighted)
import json
model.eval()
nwin = (len(ho) - 1) // a.block
starts = [j * a.block for j in range(nwin)]
sum_loss, nseen = 0.0, 0
with torch.no_grad():
    for i in range(0, nwin, a.batch):
        bs = starts[i:i+a.batch]
        x = torch.stack([ho[s:s+a.block] for s in bs]).to(dev)
        y = torch.stack([ho[s+1:s+1+a.block] for s in bs]).to(dev)
        with torch.autocast("cuda", dtype=torch.bfloat16):
            _, l = model(x, y)
        sum_loss += l.item() * len(bs); nseen += len(bs)
mean_loss = sum_loss / max(1, nseen)
ppl = math.exp(mean_loss)
out = {"train_npy": a.train_npy, "heldout_ppl": ppl, "mean_loss": mean_loss,
       "max_iters": a.max_iters, "seed": a.seed, "train_s": round(train_s, 1)}
json.dump(out, open(a.out_json, "w"))
print(f"HELDOUT_PPL {ppl:.3f}  loss {mean_loss:.4f}  train_s {train_s:.0f}  {a.train_npy}")
=== pack_selection.py ===
"""Solver-side: pack a selection.json (ordered ids) into a 12M-token train array from pool.jsonl."""
import json, sys, numpy as np
from transformers import AutoTokenizer

POOL = "/workspace/data/pool.jsonl"
SEL = sys.argv[1] if len(sys.argv) > 1 else "/workspace/submission/selection.json"
OUT = sys.argv[2] if len(sys.argv) > 2 else "/tmp/dev_train.npy"
BUDGET = 12_000_000

tok = AutoTokenizer.from_pretrained("gpt2"); EOS = tok.eos_token_id
text = {}
for line in open(POOL):
    r = json.loads(line); text[r["id"]] = r["text"]
sel = json.load(open(SEL))
parts, tot = [], 0
for i in sel:
    ids = tok(text[i], add_special_tokens=False).input_ids
    parts.extend(ids); parts.append(EOS); tot += len(ids) + 1
    if tot >= BUDGET: break
arr = np.array(parts[:BUDGET], dtype=np.uint16)
np.save(OUT, arr)
print(f"packed {len(arr)} tokens from {len(sel)} ids -> {OUT}")
=== dev_eval.sh ===
#!/bin/bash
# Diagnostic: train the frozen 30M GPT on your selection and report DEV perplexity.
# Usage: bash dev_eval.sh [selection.json]   (default /workspace/submission/selection.json)
# NOTE: the official target is a DIFFERENT (hidden) sample from the same HQ domain.
set -euo pipefail
export TOKENIZERS_PARALLELISM=false
SEL="${1:-/workspace/submission/selection.json}"
cd /workspace
python3 pack_selection.py "$SEL" /tmp/dev_train.npy
python3 train_nano.py --train_npy /tmp/dev_train.npy --heldout_npy /workspace/data/multi_dev.npy \
    --out_json /tmp/dev.json --max_iters 3000 --seed 1337 2>&1 | grep HELDOUT_PPL
echo "(dev PPL on multi_dev; official uses a hidden disjoint sample of the same HQ domain)"
=== timer.sh ===
#!/usr/bin/env bash
# Budget timer daemon — writes /workspace/.timer/remaining_secs every 10s so the
# solver can poll time left instead of doing wall-clock arithmetic.
#
# Budget (seconds) comes from $TASK_BUDGET_SECS (set by the runner to the agent
# timeout). Idempotent via a PID file. On ScaleTrain the trial runner starts its
# own timer; this file is the fallback for the Modal/local harbor backends, which
# bootstrap /workspace/timer.sh on environment start. The fallback default below
# (7200 = 2 h) matches this task's [agent] timeout_sec and is only used if
# $TASK_BUDGET_SECS is unset.
set -u
TIMER_DIR="/workspace/.timer"
PID_FILE="$TIMER_DIR/timer.pid"
mkdir -p "$TIMER_DIR"

if [ -s "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE" 2>/dev/null)" 2>/dev/null; then
    exit 0   # already running
fi
echo $$ > "$PID_FILE"
trap 'rm -f "$PID_FILE"' EXIT INT TERM

START=$(date +%s)
BUDGET="${TASK_BUDGET_SECS:-7200}"
echo "$START" > "$TIMER_DIR/start_epoch"
echo "$BUDGET" > "$TIMER_DIR/budget_secs"

while true; do
    NOW=$(date +%s)
    ELAPSED=$((NOW - START))
    REMAINING=$((BUDGET - ELAPSED))
    [ "$REMAINING" -lt 0 ] && REMAINING=0
    echo "$REMAINING" > "$TIMER_DIR/remaining_secs"
    echo "$ELAPSED" > "$TIMER_DIR/elapsed_secs"
    [ "$REMAINING" -le 1800 ] && [ ! -f "$TIMER_DIR/alert_30min" ] && touch "$TIMER_DIR/alert_30min"
    [ "$REMAINING" -le 600 ]  && [ ! -f "$TIMER_DIR/alert_10min" ] && touch "$TIMER_DIR/alert_10min"
    [ "$REMAINING" -le 0 ] && break
    sleep 10
done
[metadata] {"noOutputExpected": false}
#3
narration

90 min budget (~5400s), each train run ~60s+packing. Key insight: multi_dev.npy is GPT-2 tokens — I can decode it to see actual target text and build a similarity classifier. Let me inspect both datasets.

toolBashDecode dev target
args
{
  "command": "cd /workspace; python3 -c \"\nimport numpy as np, json\nfrom transformers import AutoTokenizer\ntok = AutoTokenizer.from_pretrained('gpt2')\nd = np.load('data/multi_dev.npy')\nprint('dev dtype', d.dtype, 'len', len(d))\n# decode a few chunks\nprint('=== DEV SAMPLE 1 ===')\nprint(tok.decode(d[:400]))\nprint('=== DEV SAMPLE 2 (mid) ===')\nprint(tok.decode(d[500000:500400]))\n\" 2>&1 | grep -v Warning",
  "description": "Decode dev target"
}
Bash result
dev dtype uint16 len 1000000
=== DEV SAMPLE 1 ===
 Beardmore was sufficiently impressed with Shackleton to offer financial support , but other donations proved hard to come by . Nevertheless , in February 1907 , Shackleton presented to the Royal Geographic Society his plans for an Antarctic expedition , the details of which , under the name British Antarctic Expedition , were published in the Royal Society 's newsletter , Geographic Journal . The aim was the conquest of both the geographical South Pole and the South Magnetic Pole . Shackleton then worked hard to persuade others of his wealthy friends and acquaintances to contribute , including Sir Philip Lee Brocklehurst , who subscribed £ 2 @,@ 000 ( 2011 equivalent £ 157 @,@ 000 ) to secure a place on the expedition ; author Campbell Mackellar ; and Guinness baron Lord Iveagh , whose contribution was secured less than two weeks before the departure of the expedition ship Nimrod . On 4 August 1907 , Shackleton was appointed a Member of the Royal Victorian Order , 4th Class ( MVO ; the present @-@ day grade of Lieutenant ) . 
<|endoftext|> On 1 January 1908 , Nimrod sailed for the Antarctic from Lyttelton Harbour , New Zealand . Shackleton 's original plans had envisaged using the old Discovery base in McMurdo Sound to launch his attempts on the South Pole and South Magnetic Pole . However , before leaving England , he had been pressured to give an undertaking to Scott that he would not base himself in the McMurdo area , which Scott was claiming as his own field of work . Shackleton reluctantly agreed to look for winter quarters at either the Barrier Inlet ( which Discovery had briefly visited in 1902 ) or King Edward VII Land . 
<|endoftext|> To conserve coal , the ship was towed 1 @,@ 650 miles ( 2 @,@ 655 km ) by the steamer Koonya to the Antarctic ice , after Shackleton had persuaded the New Zealand government and the Union Steamship Company to share the cost . In accordance with Shackleton 's promise to Scott , the
=== DEV SAMPLE 2 (mid) ===
I fucking hate when he does this shit pic.twitter.com/kpmcHnW4Cz — Miley Ray Cyrus (@MileyCyrus) April 22, 2018
Singer-actress Miley Cyrus has shared a rare video of herself with her partner, actor Liam Hemsworth, on social media. She shared a video of them both in a car, which showed Hemsworth listening to rap music as he drove them to their destination.Cyrus captioned the image: "I f***ing hate it when he does this s**t."In the clip, she was sporting a top which had the word 'Sunday' emblazoned across the front as she sat in the passenger seat. With his music blaring out, she filmed herself dancing and bopping her head to the music. Then all of a sudden, he made Cyrus jump as he suddenly screamed at her, causing to drop her phone in panic.(With IANS inputs)<|endoftext|>Uttar Pradesh chief minister Yogi Adityanath has maintained that 63 children in government-run Gorakhpur hospital died because of their ailments and not oxygen shortage, but grieving parents say their children were fine till the oxygen supply was cut.Some like Mohd Zahid, father of a five-year-old, alleged that BRD hospital authorities refused to declare their children dead, even as their bodies had turned ice cold as this would have further taken up the death toll.Another father said that while his son started bleeding from the nose, the hospital staff dismissed it saying, “Kachra nikal raha hai” (It’s just body waste that is coming out).In a harrowing tragedy exposing the sorry state of medical facilities in Uttar Pradesh, at least 32 children perished between August 10 and 11, allegedly due to no oxygen supply. A total of 63 children died within a span of five days, even as different ministers here could be seen making repeated tours

[stdout]
dev dtype uint16 len 1000000
=== DEV SAMPLE 1 ===
 Beardmore was sufficiently impressed with Shackleton to offer financial support , but other donations proved hard to come by . Nevertheless , in February 1907 , Shackleton presented to the Royal Geographic Society his plans for an Antarctic expedition , the details of which , under the name British Antarctic Expedition , were published in the Royal Society 's newsletter , Geographic Journal . The aim was the conquest of both the geographical South Pole and the South Magnetic Pole . Shackleton then worked hard to persuade others of his wealthy friends and acquaintances to contribute , including Sir Philip Lee Brocklehurst , who subscribed £ 2 @,@ 000 ( 2011 equivalent £ 157 @,@ 000 ) to secure a place on the expedition ; author Campbell Mackellar ; and Guinness baron Lord Iveagh , whose contribution was secured less than two weeks before the departure of the expedition ship Nimrod . On 4 August 1907 , Shackleton was appointed a Member of the Royal Victorian Order , 4th Class ( MVO ; the present @-@ day grade of Lieutenant ) . 
<|endoftext|> On 1 January 1908 , Nimrod sailed for the Antarctic from Lyttelton Harbour , New Zealand . Shackleton 's original plans had envisaged using the old Discovery base in McMurdo Sound to launch his attempts on the South Pole and South Magnetic Pole . However , before leaving England , he had been pressured to give an undertaking to Scott that he would not base himself in the McMurdo area , which Scott was claiming as his own field of work . Shackleton reluctantly agreed to look for winter quarters at either the Barrier Inlet ( which Discovery had briefly visited in 1902 ) or King Edward VII Land . 
<|endoftext|> To conserve coal , the ship was towed 1 @,@ 650 miles ( 2 @,@ 655 km ) by the steamer Koonya to the Antarctic ice , after Shackleton had persuaded the New Zealand government and the Union Steamship Company to share the cost . In accordance with Shackleton 's promise to Scott , the
=== DEV SAMPLE 2 (mid) ===
I fucking hate when he does this shit pic.twitter.com/kpmcHnW4Cz — Miley Ray Cyrus (@MileyCyrus) April 22, 2018
Singer-actress Miley Cyrus has shared a rare video of herself with her partner, actor Liam Hemsworth, on social media. She shared a video of them both in a car, which showed Hemsworth listening to rap music as he drove them to their destination.Cyrus captioned the image: "I f***ing hate it when he does this s**t."In the clip, she was sporting a top which had the word 'Sunday' emblazoned across the front as she sat in the passenger seat. With his music blaring out, she filmed herself dancing and bopping her head to the music. Then all of a sudden, he made Cyrus jump as he suddenly screamed at her, causing to drop her phone in panic.(With IANS inputs)<|endoftext|>Uttar Pradesh chief minister Yogi Adityanath has maintained that 63 children in government-run Gorakhpur hospital died because of their ailments and not oxygen shortage, but grieving parents say their children were fine till the oxygen supply was cut.Some like Mohd Zahid, father of a five-year-old, alleged that BRD hospital authorities refused to declare their children dead, even as their bodies had turned ice cold as this would have further taken up the death toll.Another father said that while his son started bleeding from the nose, the hospital staff dismissed it saying, “Kachra nikal raha hai” (It’s just body waste that is coming out).In a harrowing tragedy exposing the sorry state of medical facilities in Uttar Pradesh, at least 32 children perished between August 10 and 11, allegedly due to no oxygen supply. A total of 63 children died within a span of five days, even as different ministers here could be seen making repeated tours
[metadata] {"noOutputExpected": false}
#4
narration

Dev target = Wikipedia + news + web prose. Now let me inspect the pool.

toolBashInspect pool docs
args
{
  "command": "cd /workspace; python3 -c \"\nimport json\nn=0\nlens=[]\nimport sys\nwith open('data/pool.jsonl') as f:\n    for i,line in enumerate(f):\n        r=json.loads(line)\n        if i<5:\n            print('=== id',r['id'],'len',len(r['text']),'===')\n            print(r['text'][:600].replace(chr(10),' '))\n            print()\n        n+=1\nprint('total docs', n)\n\"",
  "description": "Inspect pool docs"
}
Bash result
=== id 0 len 414 ===
|Viewing Single Post From: Spoilers for the Week of February 11th| |Lil||Feb 1 2013, 09:58 AM| Don't care about Chloe/Taniel/Jen-Jen. Don't care about Sami, really, but hoping that we get some good "SAMANTHA GENE!!" Marlena Death-Stares out of it. And "newfound" feelings. Please. If only. STEFANO!! STEFANO, STEFANO, STEFANO!!!! :cheer: |Spoilers for the Week of February 11th · DAYS: News, Spoilers & Discussion|

=== id 1 len 3018 ===
*sigh* Fundamentalist community, let me pass on some advice to you I learned from the atheistic community: If you have set yourself on fire, do not run. Okay? Okay?? Please? Look, D, you had two months to say to Harvard in private emails, "Im sorry, I shouldnt have been using that animation in my paid presentations. I wont use it again. I really do like 'Inner Life', though, and would love to use it in classroom presentations, from the BioVisions site, if that is acceptable." I sat here, for two months, waiting for that to happen, anything to happen, and it didnt. Two months, on your own terms

=== id 2 len 2825 ===
A novel two-step immunotherapy approach has shown clinically beneficial responses in patients with advanced ovarian cancer. Following Lifestyle Recommendations Reduces Risk of Cancer Death People who follow the diet and lifestyle recommendations laid out by the WCRF and the AICR have a 20 percent reduced risk of dying from cancer. UCSF Launches Social Networking Site for Patients and Families with Hereditary Cancers For Immediate Release May 14, 2013 UCSF Launches Social Networking Site for Patients and Families... Genomic Test May Help Guide Prostate Cancer Treatment The Oncotype DX® Prostate

=== id 3 len 2467 ===
Free the Cans! Working Together to Reduce Waste In a blog about how people share, it’s worth the occasional reference to the bizarre ways that people DON’T SHARE. Is it safe to say we live in a society that places great value on independence, private property, personal space, and privacy? Even sometimes extreme value? Is that why people at an 8-unit apartment building in Oakland, CA have separate caged stalls for eight separate trash cans? I know it’s not nice to stare, but I walked by these incarcerated cans and could not help myself. I returned with my camera, so that I could share my questi

=== id 4 len 3303 ===
ORLANDO, Fla. — While the Rapid Recall Exchange, the 2-year-old industry recall portal, has signed up more than 600 manufacturer and retailer subscribers, it still lacks the “critical mass” of suppliers that would make it a primary source of recall information, according to trade association officials and retailers. Manufacturers use the exchange to communicate timely and accurate product recall and withdrawal notifications to retailer and wholesaler headquarters, which in turn share the information with individual stores. The exchange's retail membership represents 85% of U.S. grocery volume 

total docs 182016

[stdout]
=== id 0 len 414 ===
|Viewing Single Post From: Spoilers for the Week of February 11th| |Lil||Feb 1 2013, 09:58 AM| Don't care about Chloe/Taniel/Jen-Jen. Don't care about Sami, really, but hoping that we get some good "SAMANTHA GENE!!" Marlena Death-Stares out of it. And "newfound" feelings. Please. If only. STEFANO!! STEFANO, STEFANO, STEFANO!!!! :cheer: |Spoilers for the Week of February 11th · DAYS: News, Spoilers & Discussion|

=== id 1 len 3018 ===
*sigh* Fundamentalist community, let me pass on some advice to you I learned from the atheistic community: If you have set yourself on fire, do not run. Okay? Okay?? Please? Look, D, you had two months to say to Harvard in private emails, "Im sorry, I shouldnt have been using that animation in my paid presentations. I wont use it again. I really do like 'Inner Life', though, and would love to use it in classroom presentations, from the BioVisions site, if that is acceptable." I sat here, for two months, waiting for that to happen, anything to happen, and it didnt. Two months, on your own terms

=== id 2 len 2825 ===
A novel two-step immunotherapy approach has shown clinically beneficial responses in patients with advanced ovarian cancer. Following Lifestyle Recommendations Reduces Risk of Cancer Death People who follow the diet and lifestyle recommendations laid out by the WCRF and the AICR have a 20 percent reduced risk of dying from cancer. UCSF Launches Social Networking Site for Patients and Families with Hereditary Cancers For Immediate Release May 14, 2013 UCSF Launches Social Networking Site for Patients and Families... Genomic Test May Help Guide Prostate Cancer Treatment The Oncotype DX® Prostate

=== id 3 len 2467 ===
Free the Cans! Working Together to Reduce Waste In a blog about how people share, it’s worth the occasional reference to the bizarre ways that people DON’T SHARE. Is it safe to say we live in a society that places great value on independence, private property, personal space, and privacy? Even sometimes extreme value? Is that why people at an 8-unit apartment building in Oakland, CA have separate caged stalls for eight separate trash cans? I know it’s not nice to stare, but I walked by these incarcerated cans and could not help myself. I returned with my camera, so that I could share my questi

=== id 4 len 3303 ===
ORLANDO, Fla. — While the Rapid Recall Exchange, the 2-year-old industry recall portal, has signed up more than 600 manufacturer and retailer subscribers, it still lacks the “critical mass” of suppliers that would make it a primary source of recall information, according to trade association officials and retailers. Manufacturers use the exchange to communicate timely and accurate product recall and withdrawal notifications to retailer and wholesaler headquarters, which in turn share the information with individual stores. The exchange's retail membership represents 85% of U.S. grocery volume 

total docs 182016
[metadata] {"noOutputExpected": false}
#5
narration

Pool is raw web (C4-like), mixed quality. Target is HQ multi-domain. Strategy: build a classifier scoring pool docs by similarity to the decoded target text, plus quality heuristics. Let me first cache GPT-2 token counts for the whole pool (one-time) and set up positives from the decoded dev target.

toolBashCheck model.py and sklearn
args
{
  "command": "cd /workspace; cat model.py | head -30; echo \"...\"; python3 -c \"import sklearn; print('sklearn', sklearn.__version__)\" 2>&1 | tail -1",
  "description": "Check model.py and sklearn"
}
Bash result
"""A small GPT (decoder-only Transformer) used as the distillation *student*.

Self-contained re-implementation in this repo's own idiom (no external GPT code).
GPT-2 byte-level BPE vocabulary (50257) so the student shares the teacher's
tokenizer. Standard pre-norm Transformer with weight-tied embeddings.
"""
from dataclasses import dataclass
import math
import torch
import torch.nn as nn
import torch.nn.functional as F


@dataclass
class GPTConfig:
    block_size: int = 256
    vocab_size: int = 50257
    n_layer: int = 6
    n_head: int = 6
    n_embd: int = 384
    dropout: float = 0.0
    bias: bool = False


class LayerNorm(nn.Module):
    def __init__(self, ndim, bias):
        super().__init__()
        self.weight = nn.Parameter(torch.ones(ndim))
        self.bias = nn.Parameter(torch.zeros(ndim)) if bias else None

...
ModuleNotFoundError: No module named 'sklearn'

[stdout]
"""A small GPT (decoder-only Transformer) used as the distillation *student*.

Self-contained re-implementation in this repo's own idiom (no external GPT code).
GPT-2 byte-level BPE vocabulary (50257) so the student shares the teacher's
tokenizer. Standard pre-norm Transformer with weight-tied embeddings.
"""
from dataclasses import dataclass
import math
import torch
import torch.nn as nn
import torch.nn.functional as F


@dataclass
class GPTConfig:
    block_size: int = 256
    vocab_size: int = 50257
    n_layer: int = 6
    n_head: int = 6
    n_embd: int = 384
    dropout: float = 0.0
    bias: bool = False


class LayerNorm(nn.Module):
    def __init__(self, ndim, bias):
        super().__init__()
        self.weight = nn.Parameter(torch.ones(ndim))
        self.bias = nn.Parameter(torch.zeros(ndim)) if bias else None

...
ModuleNotFoundError: No module named 'sklearn'
[metadata] {"noOutputExpected": false}
#6
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashCheck packages / try install sklearn
args
{
  "command": "cd /workspace; python3 -c \"import numpy,torch,transformers; print('ok')\"; pip install scikit-learn 2>&1 | tail -2",
  "description": "Check packages / try install sklearn"
}
Bash result
Command did not complete within its 120s timeout and was moved to the background (ID: bejz8rbgk). Output is being written to: /tmp/claude-0/-workspace/d93cbbfa-ea11-4ae4-a8a1-b7fde4c99f5b/tasks/bejz8rbgk.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": "bejz8rbgk", "timedOutAfterMs": 120000, "backgroundCwdHint": "Session cwd remains /workspace; directory changes made by the backgrounded command do not apply to subsequent commands."}
#7
narration

No internet — I'll implement the classifier in torch (GPU) myself. Let me kill the pip attempt and build the pipeline.

toolTaskStop
args
{
  "task_id": "bejz8rbgk"
}
TaskStop result
{"message":"Successfully stopped task: bejz8rbgk (cd /workspace; python3 -c \"import numpy,torch,transformers; print('ok')\"; pip install scikit-learn 2>&1 | tail -2)","task_id":"bejz8rbgk","task_type":"local_bash","command":"cd /workspace; python3 -c \"import numpy,torch,transformers; print('ok')\"; pip install scikit-learn 2>&1 | tail -2"}

[metadata] {"message": "Successfully stopped task: bejz8rbgk (cd /workspace; python3 -c \"import numpy,torch,transformers; print('ok')\"; pip install scikit-learn 2>&1 | tail -2)", "task_id": "bejz8rbgk", "task_type": "local_bash", "command": "cd /workspace; python3 -c \"import numpy,torch,transformers; print('ok')\"; pip install scikit-learn 2>&1 | tail -2"}
#8
narration

No sklearn — I'll build the classifier in torch. Let me launch pool tokenization (for fast packing + exact token counts) in the background while I build the classifier.

toolBashLaunch pool tokenization cache in background
args
{
  "command": "cd /workspace; cat > /workspace/cache_tokens.py <<'PY'\nimport json, numpy as np, time\nfrom transformers import AutoTokenizer\ntok = AutoTokenizer.from_pretrained(\"gpt2\")\nids_all=[]; counts=[]; docids=[]\ntexts=[]; batch=[]\nt0=time.time()\ndef flush(batch):\n    enc = tok(batch, add_special_tokens=False).input_ids\n    return enc\nbuf=[]; bufid=[]\nimport sys\nall_tokens=[]; offsets=[0]; docid_arr=[]\nN=0\nwith open(\"data/pool.jsonl\") as f:\n    for line in f:\n        r=json.loads(line); buf.append(r[\"text\"]); bufid.append(r[\"id\"])\n        if len(buf)>=2000:\n            enc=tok(buf, add_special_tokens=False).input_ids\n            for e in enc:\n                all_tokens.append(np.array(e,dtype=np.uint16))\n                offsets.append(offsets[-1]+len(e)); docid_arr.append(0)\n            docids.extend(bufid); buf=[]; bufid=[]\n            N+=2000\n            if N%20000==0: print(N, round(time.time()-t0,1), 'toks', offsets[-1], flush=True)\nif buf:\n    enc=tok(buf, add_special_tokens=False).input_ids\n    for e in enc:\n        all_tokens.append(np.array(e,dtype=np.uint16)); offsets.append(offsets[-1]+len(e))\n    docids.extend(bufid)\ntoks=np.concatenate(all_tokens); offsets=np.array(offsets,dtype=np.int64); docids=np.array(docids,dtype=np.int64)\nnp.save(\"/tmp/pool_tokens.npy\", toks)\nnp.save(\"/tmp/pool_offsets.npy\", offsets)\nnp.save(\"/tmp/pool_docids.npy\", docids)\nprint(\"DONE\", len(docids),\"docs\", len(toks),\"tokens\", round(time.time()-t0,1),\"s\")\nPY\npython3 cache_tokens.py > /tmp/cache_tokens.log 2>&1 &\necho \"launched pid $!\"",
  "description": "Launch pool tokenization cache in background"
}
Bash result
launched pid 571

[stdout]
launched pid 571
[metadata] {"noOutputExpected": false}
#9
narration

While tokenization runs, let me check its progress and plan the classifier. I'll reuse the cached GPT-2 tokens as classifier features (token unigram+bigram hashing, fully vectorizable in numpy/torch).

toolBashCheck tokenization progress
args
{
  "command": "sleep 20; cat /tmp/cache_tokens.log; echo \"---timer---\"; cat /workspace/.timer/remaining_secs",
  "description": "Check tokenization progress"
}
Bash result
Token indices sequence length is longer than the specified maximum sequence length for this model (1048 > 1024). Running this sequence through the model will result in indexing errors
20000 26.5 toks 14031662
---timer---
5100

[stdout]
Token indices sequence length is longer than the specified maximum sequence length for this model (1048 > 1024). Running this sequence through the model will result in indexing errors
20000 26.5 toks 14031662
---timer---
5100
[metadata] {"noOutputExpected": false}
#10
narration

Tokenization ~4 min. Let me write the full curate.py now — a logistic-regression quality classifier over GPT-2 token unigram+bigram hashed features (fully GPU-vectorized via EmbeddingBag), target text as positives.

toolWrite
args
{
  "file_path": "/workspace/curate.py",
  "content": "\"\"\"Curate the raw web pool for small-LM pretraining.\n\nCriterion (stated, reproducible): select the pool documents whose GPT-2 token\nn-gram distribution is most similar to the DISCLOSED high-quality multi-domain\ntarget (Wikipedia + general web prose + news + technical Q&A). We train a linear\n\"quality/domain\" classifier that separates target-domain text (positives, taken\nfrom the disclosed dev target) from generic pool text (negatives, a random pool\nsample), using hashed GPT-2 token unigram+bigram features. Every pool document is\nscored by the classifier; documents are emitted in descending score order (best\nfirst) after a light junk filter. The training pipeline consumes this ordered\nlist until the 12M-token budget is full.\n\nThis is a distribution-matching / quality-classifier approach (cf. GPT-3 / CCNet\n/ fastText-style data selection): training data closest to the target domain\nminimizes held-out perplexity on that domain.\n\"\"\"\nimport json, numpy as np, torch, time\n\nD_BIG = 1 << 20          # bigram hash buckets\nVOCAB = 50257\nEOS = 50256\nMIN_TOK = 64             # junk filter: drop very short docs\nNEG_SAMPLE = 40000       # random pool docs used as negatives\nEPOCHS = 60\nt0 = time.time()\ndev = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# ---- load cached pool tokens (produced by cache_tokens.py) ----\ntoks = np.load(\"/tmp/pool_tokens.npy\")          # uint16, all docs concatenated\noffs = np.load(\"/tmp/pool_offsets.npy\")         # int64, len = ndoc+1\ndocids = np.load(\"/tmp/pool_docids.npy\")        # int64, len = ndoc  (pool ids in order)\nndoc = len(docids)\ndoclen = (offs[1:] - offs[:-1]).astype(np.int64)\nprint(f\"pool: {ndoc} docs, {len(toks)} tokens, {time.time()-t0:.1f}s\")\n\ndef bigram_hash(a, b):\n    # a,b uint16 token ids -> bucket in [0,D_BIG)\n    key = a.astype(np.uint64) * np.uint64(VOCAB) + b.astype(np.uint64)\n    return ((key * np.uint64(2654435761)) & np.uint64(D_BIG - 1)).astype(np.int64)\n\n# precompute global bigram stream (cross-doc boundary bigrams are negligible noise)\nbg_all = bigram_hash(toks[:-1], toks[1:])\nuni_all = toks.astype(np.int64)\n\n# ---- positives: decode-free, use dev target token stream split on EOS ----\ndev_toks = np.load(\"/workspace/data/multi_dev.npy\")\n# split dev into pseudo-docs on EOS\ncut = np.where(dev_toks == EOS)[0]\nstarts = np.concatenate([[0], cut + 1])\nends = np.concatenate([cut, [len(dev_toks)]])\npos_docs = [dev_toks[s:e] for s, e in zip(starts, ends) if e - s >= MIN_TOK]\nprint(f\"positives: {len(pos_docs)} dev pseudo-docs\")\n\n# ---- build EmbeddingBag inputs for a set of docs given (uni_stream, bg_stream, per-doc slices) ----\ndef bags_from_slices(slices, uni_stream, bg_stream):\n    uni_in, bg_in, off_list, cur_u, cur_b = [], [], [0], 0, 0\n    for (s, e) in slices:\n        u = uni_stream[s:e]\n        b = bg_stream[s:max(s, e-1)] if e - 1 > s else bg_stream[s:e]\n        uni_in.append(u); bg_in.append(b)\n        cur_u += len(u)\n    # offsets computed below per-stream\n    return uni_in, bg_in\n\n# Simpler: build flat + offsets for uni and bg separately for an arbitrary list of token arrays\ndef make_bags(list_of_token_arrays):\n    uni_flat, bg_flat, uni_off, bg_off = [], [], [0], [0]\n    for t in list_of_token_arrays:\n        t = t.astype(np.int64)\n        uni_flat.append(t)\n        if len(t) >= 2:\n            bg = bigram_hash(t[:-1].astype(np.uint16), t[1:].astype(np.uint16))\n        else:\n            bg = np.array([0], dtype=np.int64)\n        bg_flat.append(bg)\n        uni_off.append(uni_off[-1] + len(t))\n        bg_off.append(bg_off[-1] + len(bg))\n    uni_flat = torch.from_numpy(np.concatenate(uni_flat)).to(dev)\n    bg_flat = torch.from_numpy(np.concatenate(bg_flat)).to(dev)\n    uni_off = torch.tensor(uni_off[:-1], dtype=torch.long, device=dev)\n    bg_off = torch.tensor(bg_off[:-1], dtype=torch.long, device=dev)\n    return uni_flat, uni_off, bg_flat, bg_off\n\n# ---- assemble training set ----\nrng = np.random.default_rng(0)\nneg_idx = rng.choice(ndoc, size=min(NEG_SAMPLE, ndoc), replace=False)\nneg_idx = neg_idx[doclen[neg_idx] >= MIN_TOK]\nneg_docs = [toks[offs[i]:offs[i+1]] for i in neg_idx]\n\ntrain_docs = pos_docs + neg_docs\nlabels = np.concatenate([np.ones(len(pos_docs)), np.zeros(len(neg_docs))]).astype(np.float32)\n# class weight to balance\nw_pos = len(neg_docs) / max(1, len(pos_docs))\nuni_flat, uni_off, bg_flat, bg_off = make_bags(train_docs)\ny = torch.from_numpy(labels).to(dev)\nsample_w = torch.where(y > 0.5, torch.tensor(w_pos, device=dev), torch.tensor(1.0, device=dev))\n\n# ---- model: two EmbeddingBags (unigram, bigram), mean-pooled, dim=1 => logistic regression ----\nuni_emb = torch.nn.EmbeddingBag(VOCAB, 1, mode=\"mean\").to(dev)\nbg_emb = torch.nn.EmbeddingBag(D_BIG, 1, mode=\"mean\").to(dev)\ntorch.nn.init.zeros_(uni_emb.weight); torch.nn.init.zeros_(bg_emb.weight)\nbias = torch.zeros(1, device=dev, requires_grad=True)\nparams = list(uni_emb.parameters()) + list(bg_emb.parameters()) + [bias]\nopt = torch.optim.Adam(params, lr=0.05, weight_decay=2e-6)\nbce = torch.nn.BCEWithLogitsLoss(reduction=\"none\")\n\nfor ep in range(EPOCHS):\n    opt.zero_grad()\n    logit = (uni_emb(uni_flat, uni_off).squeeze(1)\n             + bg_emb(bg_flat, bg_off).squeeze(1) + bias)\n    loss = (bce(logit, y) * sample_w).mean()\n    loss.backward(); opt.step()\nwith torch.no_grad():\n    pred = torch.sigmoid((uni_emb(uni_flat, uni_off).squeeze(1)\n                          + bg_emb(bg_flat, bg_off).squeeze(1) + bias))\n    acc = ((pred > 0.5).float() == y).float().mean().item()\nprint(f\"classifier trained: loss {loss.item():.4f} acc {acc:.3f} {time.time()-t0:.1f}s\")\n\n# ---- score ALL pool docs in batches using the global streams ----\nuni_w = uni_emb.weight.detach().squeeze(1)      # [VOCAB]\nbg_w = bg_emb.weight.detach().squeeze(1)        # [D_BIG]\nb = bias.item()\nuni_all_t = torch.from_numpy(uni_all).to(dev)\nbg_all_t = torch.from_numpy(bg_all).to(dev)\n# cumulative sums of per-token weight, so a doc mean = (cumsum[e]-cumsum[s])/len\nuni_wt_stream = uni_w[uni_all_t]                # weight per token position\nbg_wt_stream = bg_w[bg_all_t]\nuni_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(uni_wt_stream.double(), 0)])\nbg_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(bg_wt_stream.double(), 0)])\noffs_t = torch.from_numpy(offs).to(dev)\ns = offs_t[:-1]; e = offs_t[1:]\nuni_mean = (uni_cum[e] - uni_cum[s]) / (e - s).clamp(min=1)\nbe = (e - 1).clamp(min=s + 0 * s)  # bigram end index\n# bigram doc mean over stream positions [s, e-1); guard tiny docs\nbg_len = (e - 1 - s).clamp(min=1)\nbg_e = (e - 1).clamp(max=len(bg_all))\nbg_mean = (bg_cum[bg_e] - bg_cum[s]) / bg_len\nscore = (uni_mean + bg_mean + b).cpu().numpy()\n\n# ---- junk filter + order ----\nvalid = doclen >= MIN_TOK\norder = np.argsort(-score)\norder = order[valid[order]]\nsel_ids = docids[order].tolist()\n# cover comfortably beyond 12M tokens (emit top docs summing to ~40M tokens)\ncum = np.cumsum(doclen[order])\nkeep = int(np.searchsorted(cum, 40_000_000)) + 1\nsel_ids = sel_ids[:max(keep, 60000)]\njson.dump(sel_ids, open(\"/workspace/submission/selection.json\", \"w\"))\nprint(f\"selection: {len(sel_ids)} ids, top-doc tokens sum {cum[min(keep,len(cum)-1)]:.0f}, {time.time()-t0:.1f}s\")\nprint(\"score stats: min %.3f med %.3f max %.3f\" % (score.min(), np.median(score), score.max()))\n"
}
Write result
File created successfully at: /workspace/curate.py (file state is current in your context — no need to Read it back)

[metadata] {"type": "create", "filePath": "/workspace/curate.py", "content": "\"\"\"Curate the raw web pool for small-LM pretraining.\n\nCriterion (stated, reproducible): select the pool documents whose GPT-2 token\nn-gram distribution is most similar to the DISCLOSED high-quality multi-domain\ntarget (Wikipedia + general web prose + news + technical Q&A). We train a linear\n\"quality/domain\" classifier that separates target-domain text (positives, taken\nfrom the disclosed dev target) from generic pool text (negatives, a random pool\nsample), using hashed GPT-2 token unigram+bigram features. Every pool document is\nscored by the classifier; documents are emitted in descending score order (best\nfirst) after a light junk filter. The training pipeline consumes this ordered\nlist until the 12M-token budget is full.\n\nThis is a distribution-matching / quality-classifier approach (cf. GPT-3 / CCNet\n/ fastText-style data selection): training data closest to the target domain\nminimizes held-out perplexity on that domain.\n\"\"\"\nimport json, numpy as np, torch, time\n\nD_BIG = 1 << 20          # bigram hash buckets\nVOCAB = 50257\nEOS = 50256\nMIN_TOK = 64             # junk filter: drop very short docs\nNEG_SAMPLE = 40000       # random pool docs used as negatives\nEPOCHS = 60\nt0 = time.time()\ndev = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# ---- load cached pool tokens (produced by cache_tokens.py) ----\ntoks = np.load(\"/tmp/pool_tokens.npy\")          # uint16, all docs concatenated\noffs = np.load(\"/tmp/pool_offsets.npy\")         # int64, len = ndoc+1\ndocids = np.load(\"/tmp/pool_docids.npy\")        # int64, len = ndoc  (pool ids in order)\nndoc = len(docids)\ndoclen = (offs[1:] - offs[:-1]).astype(np.int64)\nprint(f\"pool: {ndoc} docs, {len(toks)} tokens, {time.time()-t0:.1f}s\")\n\ndef bigram_hash(a, b):\n    # a,b uint16 token ids -> bucket in [0,D_BIG)\n    key = a.astype(np.uint64) * np.uint64(VOCAB) + b.astype(np.uint64)\n    return ((key * np.uint64(2654435761)) & np.uint64(D_BIG - 1)).astype(np.int64)\n\n# precompute global bigram stream (cross-doc boundary bigrams are negligible noise)\nbg_all = bigram_hash(toks[:-1], toks[1:])\nuni_all = toks.astype(np.int64)\n\n# ---- positives: decode-free, use dev target token stream split on EOS ----\ndev_toks = np.load(\"/workspace/data/multi_dev.npy\")\n# split dev into pseudo-docs on EOS\ncut = np.where(dev_toks == EOS)[0]\nstarts = np.concatenate([[0], cut + 1])\nends = np.concatenate([cut, [len(dev_toks)]])\npos_docs = [dev_toks[s:e] for s, e in zip(starts, ends) if e - s >= MIN_TOK]\nprint(f\"positives: {len(pos_docs)} dev pseudo-docs\")\n\n# ---- build EmbeddingBag inputs for a set of docs given (uni_stream, bg_stream, per-doc slices) ----\ndef bags_from_slices(slices, uni_stream, bg_stream):\n    uni_in, bg_in, off_list, cur_u, cur_b = [], [], [0], 0, 0\n    for (s, e) in slices:\n        u = uni_stream[s:e]\n        b = bg_stream[s:max(s, e-1)] if e - 1 > s else bg_stream[s:e]\n        uni_in.append(u); bg_in.append(b)\n        cur_u += len(u)\n    # offsets computed below per-stream\n    return uni_in, bg_in\n\n# Simpler: build flat + offsets for uni and bg separately for an arbitrary list of token arrays\ndef make_bags(list_of_token_arrays):\n    uni_flat, bg_flat, uni_off, bg_off = [], [], [0], [0]\n    for t in list_of_token_arrays:\n        t = t.astype(np.int64)\n        uni_flat.append(t)\n        if len(t) >= 2:\n            bg = bigram_hash(t[:-1].astype(np.uint16), t[1:].astype(np.uint16))\n        else:\n            bg = np.array([0], dtype=np.int64)\n        bg_flat.append(bg)\n        uni_off.append(uni_off[-1] + len(t))\n        bg_off.append(bg_off[-1] + len(bg))\n    uni_flat = torch.from_numpy(np.concatenate(uni_flat)).to(dev)\n    bg_flat = torch.from_numpy(np.concatenate(bg_flat)).to(dev)\n    uni_off = torch.tensor(uni_off[:-1], dtype=torch.long, device=dev)\n    bg_off = torch.tensor(bg_off[:-1], dtype=torch.long, device=dev)\n    return uni_flat, uni_off, bg_flat, bg_off\n\n# ---- assemble training set ----\nrng = np.random.default_rng(0)\nneg_idx = rng.choice(ndoc, size=min(NEG_SAMPLE, ndoc), replace=False)\nneg_idx = neg_idx[doclen[neg_idx] >= MIN_TOK]\nneg_docs = [toks[offs[i]:offs[i+1]] for i in neg_idx]\n\ntrain_docs = pos_docs + neg_docs\nlabels = np.concatenate([np.ones(len(pos_docs)), np.zeros(len(neg_docs))]).astype(np.float32)\n# class weight to balance\nw_pos = len(neg_docs) / max(1, len(pos_docs))\nuni_flat, uni_off, bg_flat, bg_off = make_bags(train_docs)\ny = torch.from_numpy(labels).to(dev)\nsample_w = torch.where(y > 0.5, torch.tensor(w_pos, device=dev), torch.tensor(1.0, device=dev))\n\n# ---- model: two EmbeddingBags (unigram, bigram), mean-pooled, dim=1 => logistic regression ----\nuni_emb = torch.nn.EmbeddingBag(VOCAB, 1, mode=\"mean\").to(dev)\nbg_emb = torch.nn.EmbeddingBag(D_BIG, 1, mode=\"mean\").to(dev)\ntorch.nn.init.zeros_(uni_emb.weight); torch.nn.init.zeros_(bg_emb.weight)\nbias = torch.zeros(1, device=dev, requires_grad=True)\nparams = list(uni_emb.parameters()) + list(bg_emb.parameters()) + [bias]\nopt = torch.optim.Adam(params, lr=0.05, weight_decay=2e-6)\nbce = torch.nn.BCEWithLogitsLoss(reduction=\"none\")\n\nfor ep in range(EPOCHS):\n    opt.zero_grad()\n    logit = (uni_emb(uni_flat, uni_off).squeeze(1)\n             + bg_emb(bg_flat, bg_off).squeeze(1) + bias)\n    loss = (bce(logit, y) * sample_w).mean()\n    loss.backward(); opt.step()\nwith torch.no_grad():\n    pred = torch.sigmoid((uni_emb(uni_flat, uni_off).squeeze(1)\n                          + bg_emb(bg_flat, bg_off).squeeze(1) + bias))\n    acc = ((pred > 0.5).float() == y).float().mean().item()\nprint(f\"classifier trained: loss {loss.item():.4f} acc {acc:.3f} {time.time()-t0:.1f}s\")\n\n# ---- score ALL pool docs in batches using the global streams ----\nuni_w = uni_emb.weight.detach().squeeze(1)      # [VOCAB]\nbg_w = bg_emb.weight.detach().squeeze(1)        # [D_BIG]\nb = bias.item()\nuni_all_t = torch.from_numpy(uni_all).to(dev)\nbg_all_t = torch.from_numpy(bg_all).to(dev)\n# cumulative sums of per-token weight, so a doc mean = (cumsum[e]-cumsum[s])/len\nuni_wt_stream = uni_w[uni_all_t]                # weight per token position\nbg_wt_stream = bg_w[bg_all_t]\nuni_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(uni_wt_stream.double(), 0)])\nbg_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(bg_wt_stream.double(), 0)])\noffs_t = torch.from_numpy(offs).to(dev)\ns = offs_t[:-1]; e = offs_t[1:]\nuni_mean = (uni_cum[e] - uni_cum[s]) / (e - s).clamp(min=1)\nbe = (e - 1).clamp(min=s + 0 * s)  # bigram end index\n# bigram doc mean over stream positions [s, e-1); guard tiny docs\nbg_len = (e - 1 - s).clamp(min=1)\nbg_e = (e - 1).clamp(max=len(bg_all))\nbg_mean = (bg_cum[bg_e] - bg_cum[s]) / bg_len\nscore = (uni_mean + bg_mean + b).cpu().numpy()\n\n# ---- junk filter + order ----\nvalid = doclen >= MIN_TOK\norder = np.argsort(-score)\norder = order[valid[order]]\nsel_ids = docids[order].tolist()\n# cover comfortably beyond 12M tokens (emit top docs summing to ~40M tokens)\ncum = np.cumsum(doclen[order])\nkeep = int(np.searchsorted(cum, 40_000_000)) + 1\nsel_ids = sel_ids[:max(keep, 60000)]\njson.dump(sel_ids, open(\"/workspace/submission/selection.json\", \"w\"))\nprint(f\"selection: {len(sel_ids)} ids, top-doc tokens sum {cum[min(keep,len(cum)-1)]:.0f}, {time.time()-t0:.1f}s\")\nprint(\"score stats: min %.3f med %.3f max %.3f\" % (score.min(), np.median(score), score.max()))\n", "structuredPatch": [], "originalFile": null, "userModified": false}
#11
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashCheck tokenization progress
args
{
  "command": "cat /tmp/cache_tokens.log | tail -5; echo \"---timer---\"; cat /workspace/.timer/remaining_secs",
  "description": "Check tokenization progress"
}
Bash result
20000 26.5 toks 14031662
40000 54.5 toks 28060454
60000 81.4 toks 41945424
80000 107.8 toks 55605749
100000 134.1 toks 69447055
---timer---
5020

[stdout]
20000 26.5 toks 14031662
40000 54.5 toks 28060454
60000 81.4 toks 41945424
80000 107.8 toks 55605749
100000 134.1 toks 69447055
---timer---
5020
[metadata] {"noOutputExpected": false}
#12
narration

Good, ~2 min left on tokenization. Let me write a fast packer that uses the cached tokens (avoids re-reading the 800MB pool each eval), plus a random baseline selection.

toolBashWrite fast packer and random baseline selection
args
{
  "command": "cd /workspace; cat > /workspace/fast_pack.py <<'PY'\nimport json, sys, numpy as np\nsel_path = sys.argv[1]; out = sys.argv[2]\nBUDGET = 12_000_000; EOS = 50256\ntoks = np.load(\"/tmp/pool_tokens.npy\"); offs = np.load(\"/tmp/pool_offsets.npy\"); docids = np.load(\"/tmp/pool_docids.npy\")\nid2idx = {int(d): i for i, d in enumerate(docids)}\nsel = json.load(open(sel_path))\nparts = []; tot = 0\nfor i in sel:\n    idx = id2idx[i]; ids = toks[offs[idx]:offs[idx+1]]\n    parts.append(ids); parts.append(np.array([EOS], dtype=toks.dtype)); tot += len(ids) + 1\n    if tot >= BUDGET: break\narr = np.concatenate(parts)[:BUDGET].astype(np.uint16)\nnp.save(out, arr)\nprint(f\"packed {len(arr)} tokens from {len(sel)} ids -> {out}\")\nPY\n# random baseline selection (reproducible)\npython3 -c \"\nimport numpy as np, json\ndocids = np.load('/tmp/pool_docids.npy')\nrng = np.random.default_rng(1337)\norder = rng.permutation(len(docids))\njson.dump(docids[order][:60000].tolist(), open('/tmp/random_sel.json','w'))\nprint('random baseline sel written', 60000)\n\" 2>&1 | tail -2\necho \"done\"",
  "description": "Write fast packer and random baseline selection"
}
Bash result
fid = stack.enter_context(open(os_fspath(file), "rb"))
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/pool_docids.npy'
done

[stdout]
    fid = stack.enter_context(open(os_fspath(file), "rb"))
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/pool_docids.npy'
done
[metadata] {"noOutputExpected": false}
#13
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashWait for tokenization cache to finish
args
{
  "command": "while [ ! -f /tmp/pool_docids.npy ]; do sleep 8; done; tail -3 /tmp/cache_tokens.log; echo \"---\"; ls -la /tmp/pool_*.npy",
  "description": "Wait for tokenization cache to finish"
}
Bash result
Command did not complete within its 120s timeout and was moved to the background (ID: b79rygox0). Output is being written to: /tmp/claude-0/-workspace/d93cbbfa-ea11-4ae4-a8a1-b7fde4c99f5b/tasks/b79rygox0.output. You will be notified when it completes. To check interim output, use Read on that file path.

[metadata] {"noOutputExpected": false, "backgroundTaskId": "b79rygox0", "timedOutAfterMs": 120000}
#14
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashCheck cache status
args
{
  "command": "tail -3 /tmp/cache_tokens.log; echo \"---\"; ls -la /tmp/pool_*.npy 2>/dev/null; cat /workspace/.timer/remaining_secs",
  "description": "Check cache status"
}
Bash result
100000 134.1 toks 69447055
120000 171.6 toks 89821037
140000 235.5 toks 125103527
---
4880

[stdout]
100000 134.1 toks 69447055
120000 171.6 toks 89821037
140000 235.5 toks 125103527
---
4880
[metadata] {"noOutputExpected": false}
#15
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashWait more for cache
args
{
  "command": "sleep 60; tail -3 /tmp/cache_tokens.log; ls -la /tmp/pool_*.npy 2>/dev/null",
  "description": "Wait more for cache"
}
Bash result · error
Exit code 2
120000 171.6 toks 89821037
140000 235.5 toks 125103527
160000 299.0 toks 160255940

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

encrypted chain-of-thought — recorded, not exposed

toolBashWait for cache completion
args
{
  "command": "sleep 45; tail -2 /tmp/cache_tokens.log; ls -la /tmp/pool_docids.npy 2>/dev/null && echo READY",
  "description": "Wait for cache completion"
}
Bash result
180000 364.0 toks 196302411
DONE 182016 docs 199911772 tokens 370.7 s
-rw-r--r-- 1 root root 1456256 Aug  4 03:33 /tmp/pool_docids.npy
READY

[stdout]
180000 364.0 toks 196302411
DONE 182016 docs 199911772 tokens 370.7 s
-rw-r--r-- 1 root root 1456256 Aug  4 03:33 /tmp/pool_docids.npy
READY
[metadata] {"noOutputExpected": false}
#17
narration

Cache ready: 182,016 docs, ~200M tokens. Now let me run the classifier and generate the selection.

toolBashRun classifier curation
args
{
  "command": "cd /workspace; python3 -c \"import numpy as np; docids=np.load('/tmp/pool_docids.npy'); import json; rng=np.random.default_rng(1337); order=rng.permutation(len(docids)); json.dump(docids[order][:60000].tolist(), open('/tmp/random_sel.json','w')); print('random sel ok')\"; python3 curate.py 2>&1 | grep -v Warning",
  "description": "Run classifier curation"
}
Bash result
random sel ok
pool: 182016 docs, 199911772 tokens, 0.2s
positives: 2449 dev pseudo-docs
classifier trained: loss 0.5349 acc 0.982 12.8s
selection: 60000 ids, top-doc tokens sum 40007892, 13.3s
score stats: min -5.279 med -1.269 max 3.359

[stdout]
random sel ok
pool: 182016 docs, 199911772 tokens, 0.2s
positives: 2449 dev pseudo-docs
classifier trained: loss 0.5349 acc 0.982 12.8s
selection: 60000 ids, top-doc tokens sum 40007892, 13.3s
score stats: min -5.279 med -1.269 max 3.359
[metadata] {"noOutputExpected": false}
#18
narration

Classifier works (98% acc). Let me sanity-check what it selects vs rejects before spending a training run.

toolBashInspect selected vs rejected docs
args
{
  "command": "cd /workspace; python3 -c \"\nimport json, numpy as np\nfrom transformers import AutoTokenizer\ntok=AutoTokenizer.from_pretrained('gpt2')\ntoks=np.load('/tmp/pool_tokens.npy'); offs=np.load('/tmp/pool_offsets.npy'); docids=np.load('/tmp/pool_docids.npy')\nid2idx={int(d):i for i,d in enumerate(docids)}\nsel=json.load(open('submission/selection.json'))\nprint('=== TOP 3 selected ===')\nfor i in sel[:3]:\n    idx=id2idx[i]; print('id',i, tok.decode(toks[offs[idx]:offs[idx]+120]).replace(chr(10),' ')); print()\nprint('=== docs ranked ~middle (30000th) ===')\nfor i in sel[30000:30002]:\n    idx=id2idx[i]; print('id',i, tok.decode(toks[offs[idx]:offs[idx]+120]).replace(chr(10),' ')); print()\nprint('=== BOTTOM (rejected) ===')\nallset=set(docids.tolist()); selset=set(sel); rej=list(allset-selset)[:3]\nfor i in rej[:3]:\n    idx=id2idx[i]; print('id',i, tok.decode(toks[offs[idx]:offs[idx]+120]).replace(chr(10),' ')); print()\n\" 2>&1 | grep -v Warning",
  "description": "Inspect selected vs rejected docs"
}
Bash result
=== TOP 3 selected ===
id 131205 Index of /regional-patterns/assets/ Index of /regional-patterns/assets/ Name                                                                             Last modified         Size  Description Parent Directory   

id 153861 Index of /regional-patterns/assets/ Index of /regional-patterns/assets/ Name                                                                             Last modified         Size  Description Parent Directory   

id 181035 1865 Top<|endoftext|>Index of /_papuros.id/ Index of /_papuros.id/ Name                                                                             Last modified         Size  Description 

=== docs ranked ~middle (30000th) ===
id 30608 Getting the Cash Offers for Your House on Sale You might be in such a scenario where you want to sell house and at the same time want it sold fast and for cash and as such you only have this question as to what options you may have that you can probably pursue. The good news is that there are some three alternatives that you may go for as a homeowner with the intent to sell house fast and for cash which will by and large enable you maximize your proceeds from the sale. See more on the options that you may explore to enable you sell your house fast and for sale. 

id 85324  Major BFBS Edition of the Hebrew Bible, also known as C.D. Ginsburg's Massoretico-Critical Edition of the Hebrew Bible, is very hard to come across. I've made TIF scans of the Pentateuch available online. These scans were made almost a decade ago from my printed set (which was destroyed during the process). This particular set was printed in 1926 in Germany by the British & Foreign Bible Society. Here is a link: The .zip file is a bit over 30 megabytes but will grow to a bit over 180 megabytes in size. In the

=== BOTTOM (rejected) ===
id 0 |Viewing Single Post From: Spoilers for the Week of February 11th| |Lil||Feb 1 2013, 09:58 AM| Don't care about Chloe/Taniel/Jen-Jen. Don't care about Sami, really, but hoping that we get some good "SAMANTHA GENE!!" Marlena Death-Stares out of it. And "newfound" feelings. Please. If only. STEFANO!! STEFANO, STEFANO, STEFANO!!!! :cheer: |

id 2 A novel two-step immunotherapy approach has shown clinically beneficial responses in patients with advanced ovarian cancer. Following Lifestyle Recommendations Reduces Risk of Cancer Death People who follow the diet and lifestyle recommendations laid out by the WCRF and the AICR have a 20 percent reduced risk of dying from cancer. UCSF Launches Social Networking Site for Patients and Families with Hereditary Cancers For Immediate Release May 14, 2013 UCSF Launches Social Networking Site for Patients and Families... Genomic Test May Help Guide Prostate Cancer Treatment The Oncotype DX® Pro

id 3 Free the Cans! Working Together to Reduce Waste In a blog about how people share, it’s worth the occasional reference to the bizarre ways that people DON’T SHARE. Is it safe to say we live in a society that places great value on independence, private property, personal space, and privacy? Even sometimes extreme value? Is that why people at an 8-unit apartment building in Oakland, CA have separate caged stalls for eight separate trash cans? I know it’s not nice to stare, but I walked by these incarcerated cans and could not help myself

[stdout]
=== TOP 3 selected ===
id 131205 Index of /regional-patterns/assets/ Index of /regional-patterns/assets/ Name                                                                             Last modified         Size  Description Parent Directory   

id 153861 Index of /regional-patterns/assets/ Index of /regional-patterns/assets/ Name                                                                             Last modified         Size  Description Parent Directory   

id 181035 1865 Top<|endoftext|>Index of /_papuros.id/ Index of /_papuros.id/ Name                                                                             Last modified         Size  Description 

=== docs ranked ~middle (30000th) ===
id 30608 Getting the Cash Offers for Your House on Sale You might be in such a scenario where you want to sell house and at the same time want it sold fast and for cash and as such you only have this question as to what options you may have that you can probably pursue. The good news is that there are some three alternatives that you may go for as a homeowner with the intent to sell house fast and for cash which will by and large enable you maximize your proceeds from the sale. See more on the options that you may explore to enable you sell your house fast and for sale. 

id 85324  Major BFBS Edition of the Hebrew Bible, also known as C.D. Ginsburg's Massoretico-Critical Edition of the Hebrew Bible, is very hard to come across. I've made TIF scans of the Pentateuch available online. These scans were made almost a decade ago from my printed set (which was destroyed during the process). This particular set was printed in 1926 in Germany by the British & Foreign Bible Society. Here is a link: The .zip file is a bit over 30 megabytes but will grow to a bit over 180 megabytes in size. In the

=== BOTTOM (rejected) ===
id 0 |Viewing Single Post From: Spoilers for the Week of February 11th| |Lil||Feb 1 2013, 09:58 AM| Don't care about Chloe/Taniel/Jen-Jen. Don't care about Sami, really, but hoping that we get some good "SAMANTHA GENE!!" Marlena Death-Stares out of it. And "newfound" feelings. Please. If only. STEFANO!! STEFANO, STEFANO, STEFANO!!!! :cheer: |

id 2 A novel two-step immunotherapy approach has shown clinically beneficial responses in patients with advanced ovarian cancer. Following Lifestyle Recommendations Reduces Risk of Cancer Death People who follow the diet and lifestyle recommendations laid out by the WCRF and the AICR have a 20 percent reduced risk of dying from cancer. UCSF Launches Social Networking Site for Patients and Families with Hereditary Cancers For Immediate Release May 14, 2013 UCSF Launches Social Networking Site for Patients and Families... Genomic Test May Help Guide Prostate Cancer Treatment The Oncotype DX® Pro

id 3 Free the Cans! Working Together to Reduce Waste In a blog about how people share, it’s worth the occasional reference to the bizarre ways that people DON’T SHARE. Is it safe to say we live in a society that places great value on independence, private property, personal space, and privacy? Even sometimes extreme value? Is that why people at an 8-unit apartment building in Oakland, CA have separate caged stalls for eight separate trash cans? I know it’s not nice to stare, but I walked by these incarcerated cans and could not help myself
[metadata] {"noOutputExpected": false}
#19
narration

The classifier is fooled by whitespace/boilerplate junk ("Index of /..." repeated docs score highest — they're whitespace-heavy near-duplicates). I need quality prefilters. Let me compute per-doc repetition/quality stats from the cached tokens and cache them.

toolBashCompute repetition stats per doc
args
{
  "command": "cd /workspace; python3 -c \"\nimport numpy as np, time\ntoks=np.load('/tmp/pool_tokens.npy'); offs=np.load('/tmp/pool_offsets.npy')\nndoc=len(offs)-1; t0=time.time()\nuniq_ratio=np.zeros(ndoc,dtype=np.float32)\nmaxfreq=np.zeros(ndoc,dtype=np.float32)\nfor i in range(ndoc):\n    d=toks[offs[i]:offs[i+1]]\n    if len(d)==0: uniq_ratio[i]=0; maxfreq[i]=1; continue\n    c=np.bincount(d)\n    nz=c[c>0]\n    uniq_ratio[i]=len(nz)/len(d)\n    maxfreq[i]=nz.max()/len(d)\n    if i%40000==0: print(i, round(time.time()-t0,1))\nnp.save('/tmp/pool_uniqratio.npy',uniq_ratio)\nnp.save('/tmp/pool_maxfreq.npy',maxfreq)\nprint('done', round(time.time()-t0,1),'s')\nprint('uniq_ratio pctiles', np.percentile(uniq_ratio,[1,5,25,50,75,95]))\nprint('maxfreq pctiles', np.percentile(maxfreq,[5,25,50,75,95,99]))\n\"",
  "description": "Compute repetition stats per doc"
}
Bash result
0 0.0
40000 4.2
80000 6.0
120000 8.0
160000 10.3
done 11.5 s
uniq_ratio pctiles [0.14184034 0.26518562 0.41295494 0.50379267 0.60434979 0.75      ]
maxfreq pctiles [0.03672316 0.04629629 0.05828221 0.09304447 0.18024927 0.23307347]

[stdout]
0 0.0
40000 4.2
80000 6.0
120000 8.0
160000 10.3
done 11.5 s
uniq_ratio pctiles [0.14184034 0.26518562 0.41295494 0.50379267 0.60434979 0.75      ]
maxfreq pctiles [0.03672316 0.04629629 0.05828221 0.09304447 0.18024927 0.23307347]
[metadata] {"noOutputExpected": false}
#20
narration

Now let me integrate quality filters into curate.py and verify the junk gets removed.

toolEdit
args
{
  "replace_all": false,
  "file_path": "/workspace/curate.py",
  "old_string": "# ---- junk filter + order ----\nvalid = doclen >= MIN_TOK\norder = np.argsort(-score)",
  "new_string": "# ---- quality prefilter (Gopher/CCNet-style, computed from tokens) ----\n# repetition stats: unique-token ratio (low => boilerplate/repetitive),\n# max-token-frequency ratio (high => whitespace/char-run junk like \"Index of /...\").\nuniq_ratio = np.load(\"/tmp/pool_uniqratio.npy\")\nmaxfreq = np.load(\"/tmp/pool_maxfreq.npy\")\nvalid = (doclen >= MIN_TOK) & (doclen <= 20000) & (uniq_ratio >= 0.35) & (maxfreq <= 0.18)\nprint(f\"quality prefilter keeps {valid.sum()}/{ndoc} docs\")\n\n# ---- order ----\norder = np.argsort(-score)"
}
Edit result
The file /workspace/curate.py has been updated successfully. (file state is current in your context — no need to Read it back)

[metadata] {"filePath": "/workspace/curate.py", "oldString": "# ---- junk filter + order ----\nvalid = doclen >= MIN_TOK\norder = np.argsort(-score)", "newString": "# ---- quality prefilter (Gopher/CCNet-style, computed from tokens) ----\n# repetition stats: unique-token ratio (low => boilerplate/repetitive),\n# max-token-frequency ratio (high => whitespace/char-run junk like \"Index of /...\").\nuniq_ratio = np.load(\"/tmp/pool_uniqratio.npy\")\nmaxfreq = np.load(\"/tmp/pool_maxfreq.npy\")\nvalid = (doclen >= MIN_TOK) & (doclen <= 20000) & (uniq_ratio >= 0.35) & (maxfreq <= 0.18)\nprint(f\"quality prefilter keeps {valid.sum()}/{ndoc} docs\")\n\n# ---- order ----\norder = np.argsort(-score)", "originalFile": "\"\"\"Curate the raw web pool for small-LM pretraining.\n\nCriterion (stated, reproducible): select the pool documents whose GPT-2 token\nn-gram distribution is most similar to the DISCLOSED high-quality multi-domain\ntarget (Wikipedia + general web prose + news + technical Q&A). We train a linear\n\"quality/domain\" classifier that separates target-domain text (positives, taken\nfrom the disclosed dev target) from generic pool text (negatives, a random pool\nsample), using hashed GPT-2 token unigram+bigram features. Every pool document is\nscored by the classifier; documents are emitted in descending score order (best\nfirst) after a light junk filter. The training pipeline consumes this ordered\nlist until the 12M-token budget is full.\n\nThis is a distribution-matching / quality-classifier approach (cf. GPT-3 / CCNet\n/ fastText-style data selection): training data closest to the target domain\nminimizes held-out perplexity on that domain.\n\"\"\"\nimport json, numpy as np, torch, time\n\nD_BIG = 1 << 20          # bigram hash buckets\nVOCAB = 50257\nEOS = 50256\nMIN_TOK = 64             # junk filter: drop very short docs\nNEG_SAMPLE = 40000       # random pool docs used as negatives\nEPOCHS = 60\nt0 = time.time()\ndev = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# ---- load cached pool tokens (produced by cache_tokens.py) ----\ntoks = np.load(\"/tmp/pool_tokens.npy\")          # uint16, all docs concatenated\noffs = np.load(\"/tmp/pool_offsets.npy\")         # int64, len = ndoc+1\ndocids = np.load(\"/tmp/pool_docids.npy\")        # int64, len = ndoc  (pool ids in order)\nndoc = len(docids)\ndoclen = (offs[1:] - offs[:-1]).astype(np.int64)\nprint(f\"pool: {ndoc} docs, {len(toks)} tokens, {time.time()-t0:.1f}s\")\n\ndef bigram_hash(a, b):\n    # a,b uint16 token ids -> bucket in [0,D_BIG)\n    key = a.astype(np.uint64) * np.uint64(VOCAB) + b.astype(np.uint64)\n    return ((key * np.uint64(2654435761)) & np.uint64(D_BIG - 1)).astype(np.int64)\n\n# precompute global bigram stream (cross-doc boundary bigrams are negligible noise)\nbg_all = bigram_hash(toks[:-1], toks[1:])\nuni_all = toks.astype(np.int64)\n\n# ---- positives: decode-free, use dev target token stream split on EOS ----\ndev_toks = np.load(\"/workspace/data/multi_dev.npy\")\n# split dev into pseudo-docs on EOS\ncut = np.where(dev_toks == EOS)[0]\nstarts = np.concatenate([[0], cut + 1])\nends = np.concatenate([cut, [len(dev_toks)]])\npos_docs = [dev_toks[s:e] for s, e in zip(starts, ends) if e - s >= MIN_TOK]\nprint(f\"positives: {len(pos_docs)} dev pseudo-docs\")\n\n# ---- build EmbeddingBag inputs for a set of docs given (uni_stream, bg_stream, per-doc slices) ----\ndef bags_from_slices(slices, uni_stream, bg_stream):\n    uni_in, bg_in, off_list, cur_u, cur_b = [], [], [0], 0, 0\n    for (s, e) in slices:\n        u = uni_stream[s:e]\n        b = bg_stream[s:max(s, e-1)] if e - 1 > s else bg_stream[s:e]\n        uni_in.append(u); bg_in.append(b)\n        cur_u += len(u)\n    # offsets computed below per-stream\n    return uni_in, bg_in\n\n# Simpler: build flat + offsets for uni and bg separately for an arbitrary list of token arrays\ndef make_bags(list_of_token_arrays):\n    uni_flat, bg_flat, uni_off, bg_off = [], [], [0], [0]\n    for t in list_of_token_arrays:\n        t = t.astype(np.int64)\n        uni_flat.append(t)\n        if len(t) >= 2:\n            bg = bigram_hash(t[:-1].astype(np.uint16), t[1:].astype(np.uint16))\n        else:\n            bg = np.array([0], dtype=np.int64)\n        bg_flat.append(bg)\n        uni_off.append(uni_off[-1] + len(t))\n        bg_off.append(bg_off[-1] + len(bg))\n    uni_flat = torch.from_numpy(np.concatenate(uni_flat)).to(dev)\n    bg_flat = torch.from_numpy(np.concatenate(bg_flat)).to(dev)\n    uni_off = torch.tensor(uni_off[:-1], dtype=torch.long, device=dev)\n    bg_off = torch.tensor(bg_off[:-1], dtype=torch.long, device=dev)\n    return uni_flat, uni_off, bg_flat, bg_off\n\n# ---- assemble training set ----\nrng = np.random.default_rng(0)\nneg_idx = rng.choice(ndoc, size=min(NEG_SAMPLE, ndoc), replace=False)\nneg_idx = neg_idx[doclen[neg_idx] >= MIN_TOK]\nneg_docs = [toks[offs[i]:offs[i+1]] for i in neg_idx]\n\ntrain_docs = pos_docs + neg_docs\nlabels = np.concatenate([np.ones(len(pos_docs)), np.zeros(len(neg_docs))]).astype(np.float32)\n# class weight to balance\nw_pos = len(neg_docs) / max(1, len(pos_docs))\nuni_flat, uni_off, bg_flat, bg_off = make_bags(train_docs)\ny = torch.from_numpy(labels).to(dev)\nsample_w = torch.where(y > 0.5, torch.tensor(w_pos, device=dev), torch.tensor(1.0, device=dev))\n\n# ---- model: two EmbeddingBags (unigram, bigram), mean-pooled, dim=1 => logistic regression ----\nuni_emb = torch.nn.EmbeddingBag(VOCAB, 1, mode=\"mean\").to(dev)\nbg_emb = torch.nn.EmbeddingBag(D_BIG, 1, mode=\"mean\").to(dev)\ntorch.nn.init.zeros_(uni_emb.weight); torch.nn.init.zeros_(bg_emb.weight)\nbias = torch.zeros(1, device=dev, requires_grad=True)\nparams = list(uni_emb.parameters()) + list(bg_emb.parameters()) + [bias]\nopt = torch.optim.Adam(params, lr=0.05, weight_decay=2e-6)\nbce = torch.nn.BCEWithLogitsLoss(reduction=\"none\")\n\nfor ep in range(EPOCHS):\n    opt.zero_grad()\n    logit = (uni_emb(uni_flat, uni_off).squeeze(1)\n             + bg_emb(bg_flat, bg_off).squeeze(1) + bias)\n    loss = (bce(logit, y) * sample_w).mean()\n    loss.backward(); opt.step()\nwith torch.no_grad():\n    pred = torch.sigmoid((uni_emb(uni_flat, uni_off).squeeze(1)\n                          + bg_emb(bg_flat, bg_off).squeeze(1) + bias))\n    acc = ((pred > 0.5).float() == y).float().mean().item()\nprint(f\"classifier trained: loss {loss.item():.4f} acc {acc:.3f} {time.time()-t0:.1f}s\")\n\n# ---- score ALL pool docs in batches using the global streams ----\nuni_w = uni_emb.weight.detach().squeeze(1)      # [VOCAB]\nbg_w = bg_emb.weight.detach().squeeze(1)        # [D_BIG]\nb = bias.item()\nuni_all_t = torch.from_numpy(uni_all).to(dev)\nbg_all_t = torch.from_numpy(bg_all).to(dev)\n# cumulative sums of per-token weight, so a doc mean = (cumsum[e]-cumsum[s])/len\nuni_wt_stream = uni_w[uni_all_t]                # weight per token position\nbg_wt_stream = bg_w[bg_all_t]\nuni_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(uni_wt_stream.double(), 0)])\nbg_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(bg_wt_stream.double(), 0)])\noffs_t = torch.from_numpy(offs).to(dev)\ns = offs_t[:-1]; e = offs_t[1:]\nuni_mean = (uni_cum[e] - uni_cum[s]) / (e - s).clamp(min=1)\nbe = (e - 1).clamp(min=s + 0 * s)  # bigram end index\n# bigram doc mean over stream positions [s, e-1); guard tiny docs\nbg_len = (e - 1 - s).clamp(min=1)\nbg_e = (e - 1).clamp(max=len(bg_all))\nbg_mean = (bg_cum[bg_e] - bg_cum[s]) / bg_len\nscore = (uni_mean + bg_mean + b).cpu().numpy()\n\n# ---- junk filter + order ----\nvalid = doclen >= MIN_TOK\norder = np.argsort(-score)\norder = order[valid[order]]\nsel_ids = docids[order].tolist()\n# cover comfortably beyond 12M tokens (emit top docs summing to ~40M tokens)\ncum = np.cumsum(doclen[order])\nkeep = int(np.searchsorted(cum, 40_000_000)) + 1\nsel_ids = sel_ids[:max(keep, 60000)]\njson.dump(sel_ids, open(\"/workspace/submission/selection.json\", \"w\"))\nprint(f\"selection: {len(sel_ids)} ids, top-doc tokens sum {cum[min(keep,len(cum)-1)]:.0f}, {time.time()-t0:.1f}s\")\nprint(\"score stats: min %.3f med %.3f max %.3f\" % (score.min(), np.median(score), score.max()))\n", "structuredPatch": [{"oldStart": 137, "oldLines": 8, "newStart": 137, "newLines": 15, "lines": [" bg_mean = (bg_cum[bg_e] - bg_cum[s]) / bg_len", " score = (uni_mean + bg_mean + b).cpu().numpy()", " ", "-# ---- junk filter + order ----", "-valid = doclen >= MIN_TOK", "+# ---- quality prefilter (Gopher/CCNet-style, computed from tokens) ----", "+# repetition stats: unique-token ratio (low => boilerplate/repetitive),", "+# max-token-frequency ratio (high => whitespace/char-run junk like \"Index of /...\").", "+uniq_ratio = np.load(\"/tmp/pool_uniqratio.npy\")", "+maxfreq = np.load(\"/tmp/pool_maxfreq.npy\")", "+valid = (doclen >= MIN_TOK) & (doclen <= 20000) & (uniq_ratio >= 0.35) & (maxfreq <= 0.18)", "+print(f\"quality prefilter keeps {valid.sum()}/{ndoc} docs\")", "+", "+# ---- order ----", " order = np.argsort(-score)", " order = order[valid[order]]", " sel_ids = docids[order].tolist()"]}], "userModified": false, "replaceAll": false}
#21
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashRe-run with filters, inspect top
args
{
  "command": "cd /workspace; python3 curate.py 2>&1 | grep -v Warning; echo \"=== inspect new top ===\"; python3 -c \"\nimport json, numpy as np\nfrom transformers import AutoTokenizer\ntok=AutoTokenizer.from_pretrained('gpt2')\ntoks=np.load('/tmp/pool_tokens.npy'); offs=np.load('/tmp/pool_offsets.npy'); docids=np.load('/tmp/pool_docids.npy')\nid2idx={int(d):i for i,d in enumerate(docids)}\nsel=json.load(open('submission/selection.json'))\nfor rank in [0,1,2,5,10,100]:\n    i=sel[rank]; idx=id2idx[i]\n    print(f'--- rank {rank} id {i} ---'); print(tok.decode(toks[offs[idx]:offs[idx]+90]).replace(chr(10),' ')); print()\n\" 2>&1 | grep -v Warning",
  "description": "Re-run with filters, inspect top"
}
Bash result
pool: 182016 docs, 199911772 tokens, 0.2s
positives: 2449 dev pseudo-docs
classifier trained: loss 0.5349 acc 0.982 13.0s
quality prefilter keeps 148920/182016 docs
selection: 61845 ids, top-doc tokens sum 40002926, 13.5s
score stats: min -5.279 med -1.269 max 3.359
=== inspect new top ===
--- rank 0 id 68804 ---
This article analyses the historical evolution of the migration relations between France and Italy from the aftermath of the Second World War to the outbreak of the European Migrant Crisis. Adopting a long-term perspective, it shows that migration has been more a source of tension than convergence between the two countries. Despite an apparent complementarity of interests, between the mid- 1940s and the early 1970s the governments in Paris and Rome disagreed over size and patterns of

--- rank 1 id 67417 ---
<|endoftext|>Referring to her remarks in a press conference in New Delhi [ Images ] on the issue, he said, "She knows that her candidate Rajakannappan has filed an election petition in the Madras high court and that is pending since September 2009. Her statement is therefore in gross contempt of court." The home minister said that Jayalalithaa [ Images ] has the habit of "starting on the wrong foot" and "

--- rank 2 id 26420 ---
At least 12, including two US troops, die in Iraq attacks Gunmen shot dead an Iraqi journalist for a US-funded Arabic television station and his young son in one of a number of attacks across the country that left at least nine people dead. The US military also reported the death of two more US soldiers. Abdel Hussein Khazaal, a correspondent for the Al-Hurra pan-Arab television station was shot dead as he

--- rank 5 id 8524 ---
GAZA STRIP, Palestinian territories - A Hamas commander trying to stop two youths from approaching the border fence in the northern Gaza strip was shot dead by the Israeli army on Thursday. According to The Jerusalem Post the Israeli army has confirmed the death occurred, but says it was a misunderstanding. The death of Hamas operative Mahmoud Ahmad Sabri al-Adham, 28, was criticized by the Hamas organization, which in a statement, carried by the

--- rank 10 id 51268 ---
omaya Hendousa founded dumped in slums east of Khartoum , severely tortured and her hair shaved confirmed resources close to her family . Hendousa family are in shock after what happened to her and starting police report against NISS member had been threatening Hendousa after she returned to Sudan last week. The resources confirmed that Somaya subjected to racial insults and torture because she is Darfurian , and her hair shaved , as the

--- rank 100 id 84947 ---
 Italy<|endoftext|>A 12-year-old girl and a 15-year-old boy have been killed in an acid attack in Afghanistan, an official said Saturday, with witnesses claiming it was because of their friendship in what is an ultra-conservative country. The bodies were discovered on Friday in wasteland in the Esfandi area of Ghazni province in south central Afghanistan, provincial police chief Zorawar Zahid told AFP. “Their

[stdout]
pool: 182016 docs, 199911772 tokens, 0.2s
positives: 2449 dev pseudo-docs
classifier trained: loss 0.5349 acc 0.982 13.0s
quality prefilter keeps 148920/182016 docs
selection: 61845 ids, top-doc tokens sum 40002926, 13.5s
score stats: min -5.279 med -1.269 max 3.359
=== inspect new top ===
--- rank 0 id 68804 ---
This article analyses the historical evolution of the migration relations between France and Italy from the aftermath of the Second World War to the outbreak of the European Migrant Crisis. Adopting a long-term perspective, it shows that migration has been more a source of tension than convergence between the two countries. Despite an apparent complementarity of interests, between the mid- 1940s and the early 1970s the governments in Paris and Rome disagreed over size and patterns of

--- rank 1 id 67417 ---
<|endoftext|>Referring to her remarks in a press conference in New Delhi [ Images ] on the issue, he said, "She knows that her candidate Rajakannappan has filed an election petition in the Madras high court and that is pending since September 2009. Her statement is therefore in gross contempt of court." The home minister said that Jayalalithaa [ Images ] has the habit of "starting on the wrong foot" and "

--- rank 2 id 26420 ---
At least 12, including two US troops, die in Iraq attacks Gunmen shot dead an Iraqi journalist for a US-funded Arabic television station and his young son in one of a number of attacks across the country that left at least nine people dead. The US military also reported the death of two more US soldiers. Abdel Hussein Khazaal, a correspondent for the Al-Hurra pan-Arab television station was shot dead as he

--- rank 5 id 8524 ---
GAZA STRIP, Palestinian territories - A Hamas commander trying to stop two youths from approaching the border fence in the northern Gaza strip was shot dead by the Israeli army on Thursday. According to The Jerusalem Post the Israeli army has confirmed the death occurred, but says it was a misunderstanding. The death of Hamas operative Mahmoud Ahmad Sabri al-Adham, 28, was criticized by the Hamas organization, which in a statement, carried by the

--- rank 10 id 51268 ---
omaya Hendousa founded dumped in slums east of Khartoum , severely tortured and her hair shaved confirmed resources close to her family . Hendousa family are in shock after what happened to her and starting police report against NISS member had been threatening Hendousa after she returned to Sudan last week. The resources confirmed that Somaya subjected to racial insults and torture because she is Darfurian , and her hair shaved , as the

--- rank 100 id 84947 ---
 Italy<|endoftext|>A 12-year-old girl and a 15-year-old boy have been killed in an acid attack in Afghanistan, an official said Saturday, with witnesses claiming it was because of their friendship in what is an ultra-conservative country. The bodies were discovered on Friday in wasteland in the Esfandi area of Ghazni province in south central Afghanistan, provincial police chief Zorawar Zahid told AFP. “Their
[metadata] {"noOutputExpected": false}
#22
narration

Now the top docs are exactly the target register (news/encyclopedic prose). Let me evaluate: baseline (random) vs classifier selection via the frozen trainer.

toolBashEval random baseline vs classifier selection
args
{
  "command": "cd /workspace; export TOKENIZERS_PARALLELISM=false\necho \"=== RANDOM BASELINE ===\"; python3 fast_pack.py /tmp/random_sel.json /tmp/rand_train.npy\npython3 train_nano.py --train_npy /tmp/rand_train.npy --heldout_npy data/multi_dev.npy --out_json /tmp/rand.json --max_iters 3000 --seed 1337 2>&1 | grep HELDOUT_PPL\necho \"=== CLASSIFIER SELECTION ===\"; python3 fast_pack.py submission/selection.json /tmp/cls_train.npy\npython3 train_nano.py --train_npy /tmp/cls_train.npy --heldout_npy data/multi_dev.npy --out_json /tmp/cls.json --max_iters 3000 --seed 1337 2>&1 | grep HELDOUT_PPL\ncat /workspace/.timer/remaining_secs",
  "description": "Eval random baseline vs classifier selection"
}
Bash result
Command did not complete within its 120s timeout and was moved to the background (ID: b3tpkjv38). Output is being written to: /tmp/claude-0/-workspace/d93cbbfa-ea11-4ae4-a8a1-b7fde4c99f5b/tasks/b3tpkjv38.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": "b3tpkjv38", "timedOutAfterMs": 120000, "backgroundCwdHint": "Session cwd remains /workspace; directory changes made by the backgrounded command do not apply to subsequent commands."}
#23
reasoning (redacted)

encrypted chain-of-thought — recorded, not exposed

toolBashCheck eval results
args
{
  "command": "sleep 90; cat /tmp/claude-0/-workspace/d93cbbfa-ea11-4ae4-a8a1-b7fde4c99f5b/tasks/b3tpkjv38.output; echo \"---timer---\"; cat /workspace/.timer/remaining_secs",
  "description": "Check eval results"
}
Bash result
=== RANDOM BASELINE ===
packed 12000000 tokens from 60000 ids -> /tmp/rand_train.npy
HELDOUT_PPL 457.226  loss 6.1252  train_s 56  /tmp/rand_train.npy
=== CLASSIFIER SELECTION ===
packed 12000000 tokens from 61845 ids -> /tmp/cls_train.npy
HELDOUT_PPL 405.331  loss 6.0047  train_s 56  /tmp/cls_train.npy
4509
---timer---
4419

[stdout]
=== RANDOM BASELINE ===
packed 12000000 tokens from 60000 ids -> /tmp/rand_train.npy
HELDOUT_PPL 457.226  loss 6.1252  train_s 56  /tmp/rand_train.npy
=== CLASSIFIER SELECTION ===
packed 12000000 tokens from 61845 ids -> /tmp/cls_train.npy
HELDOUT_PPL 405.331  loss 6.0047  train_s 56  /tmp/cls_train.npy
4509
---timer---
4419
[metadata] {"noOutputExpected": false}
#24
narration

Classifier beats random (405 vs 457, ~11%). But the top ranks look news-heavy, while the target is equal parts wiki/web/news/techQA — imbalance and topical overfit likely cap the gain. Let me diagnose register composition and build a distribution-matched selection: cluster docs, then fill the budget matching the target's cluster proportions while taking highest-quality within each.

toolBashCluster pool and dev, compute target proportions
args
{
  "command": "cd /workspace; cat > /workspace/cluster_balance.py <<'PY'\nimport json, numpy as np, torch, time\nt0=time.time(); dev=\"cuda\"\ntoks=np.load('/tmp/pool_tokens.npy'); offs=np.load('/tmp/pool_offsets.npy'); docids=np.load('/tmp/pool_docids.npy')\nndoc=len(docids); doclen=(offs[1:]-offs[:-1]).astype(np.int64)\nVOCAB=50257; EOS=50256; TOPN=3000; K=8\n\n# global token frequency -> pick top-N tokens as clustering vocab\ngc=np.bincount(toks.astype(np.int64), minlength=VOCAB)\ntop=np.argsort(-gc)[:TOPN]\nremap=-np.ones(VOCAB,dtype=np.int64); remap[top]=np.arange(TOPN)\n\ndef docvecs(tokarr, off_pairs):\n    # build normalized TF vectors over TOPN vocab for given doc slices\n    V=np.zeros((len(off_pairs),TOPN),dtype=np.float32)\n    for k,(s,e) in enumerate(off_pairs):\n        d=remap[tokarr[s:e].astype(np.int64)]; d=d[d>=0]\n        if len(d)==0: continue\n        c=np.bincount(d,minlength=TOPN).astype(np.float32); V[k]=c/ c.sum()\n    return V\n\n# pool vectors\npairs=[(offs[i],offs[i+1]) for i in range(ndoc)]\nVp=docvecs(toks,pairs); print('pool vecs',Vp.shape,round(time.time()-t0,1))\n# tfidf-ish weight by idf\ndf=(Vp>0).sum(0)+1; idf=np.log(ndoc/df).astype(np.float32)\nVp*=idf\n# normalize rows\nnrm=np.linalg.norm(Vp,axis=1,keepdims=True); nrm[nrm==0]=1; Vp/=nrm\nVp_t=torch.from_numpy(Vp).to(dev)\n\n# kmeans on pool (spherical)\ng=torch.Generator(device=dev); g.manual_seed(0)\ncent=Vp_t[torch.randperm(ndoc,generator=g,device=dev)[:K]].clone()\nfor it in range(25):\n    sim=Vp_t@cent.T; assign=sim.argmax(1)\n    for c in range(K):\n        m=assign==c\n        if m.any(): v=Vp_t[m].mean(0); cent[c]=v/ (v.norm()+1e-8)\npool_assign=assign.cpu().numpy()\nprint('pool kmeans done',round(time.time()-t0,1))\n\n# dev vectors + assign\ndevtoks=np.load('/workspace/data/multi_dev.npy')\ncut=np.where(devtoks==EOS)[0]; ds=np.concatenate([[0],cut+1]); de=np.concatenate([cut,[len(devtoks)]])\ndpairs=[(s,e) for s,e in zip(ds,de) if e-s>=64]\nVd=docvecs(devtoks,dpairs); Vd*=idf; n=np.linalg.norm(Vd,axis=1,keepdims=True); n[n==0]=1; Vd/=n\ndev_assign=(torch.from_numpy(Vd).to(dev)@cent.T).argmax(1).cpu().numpy()\n# target proportion per cluster (by dev tokens)\ndevlens=np.array([e-s for s,e in dpairs])\ntarget_prop=np.array([devlens[dev_assign==c].sum() for c in range(K)],dtype=np.float64)\ntarget_prop/=target_prop.sum()\nprint('target cluster proportions:', np.round(target_prop,3))\npoolshare=np.array([(pool_assign==c).sum() for c in range(K)])\nprint('pool cluster counts:', poolshare)\nnp.save('/tmp/pool_assign.npy',pool_assign); np.save('/tmp/target_prop.npy',target_prop)\nprint('done',round(time.time()-t0,1))\nPY\npython3 cluster_balance.py 2>&1 | grep -v Warning; cat /workspace/.timer/remaining_secs",
  "description": "Cluster pool and dev, compute target proportions"
}
Bash result
pool vecs (182016, 3000) 5.1
pool kmeans done 8.7
target cluster proportions: [0.107 0.137 0.346 0.002 0.004 0.259 0.145 0.   ]
pool cluster counts: [45690 27746 30391 29068  7933  5506 31581  4101]
done 8.8
4329

[stdout]
pool vecs (182016, 3000) 5.1
pool kmeans done 8.7
target cluster proportions: [0.107 0.137 0.346 0.002 0.004 0.259 0.145 0.   ]
pool cluster counts: [45690 27746 30391 29068  7933  5506 31581  4101]
done 8.8
4329
[metadata] {"noOutputExpected": false}
#25
narration

The target concentrates in a few clusters (2, 5, 6, 1, 0) while the pool has many docs in clusters the target barely uses. Distribution-matching will help. Let me write the final integrated curate.py: classifier score + cluster assignment + proportional priority fill (matches target register mix while taking highest-quality within each cluster).

toolWrite
args
{
  "file_path": "/workspace/curate.py",
  "content": "\"\"\"Curate the raw web pool for small-LM pretraining (reproducible).\n\nCriterion (stated): select pool documents so the training mix (a) is high quality\n(junk/boilerplate removed) and (b) MATCHES THE DISCLOSED TARGET DISTRIBUTION —\nbroad multi-domain HQ English (Wikipedia + general web prose + news + technical\nQ&A). Two ingredients:\n\n  1. QUALITY / DOMAIN classifier. A linear model over hashed GPT-2 token\n     unigram+bigram features is trained to separate target-domain text (positives\n     = the disclosed dev target, split on EOS into pseudo-docs) from generic pool\n     text (negatives = random pool sample). Every pool doc gets a \"target-likeness\"\n     score. A Gopher/CCNet-style repetition prefilter drops boilerplate first.\n\n  2. DISTRIBUTION MATCHING. Pool docs are clustered (spherical k-means on tf-idf\n     token histograms). The dev target is assigned to the same clusters, giving a\n     target token-proportion per cluster. The final priority order is a greedy\n     proportional interleave: at every prefix the cluster token-mix tracks the\n     target's, and within each cluster docs are ordered by classifier score. This\n     yields a budget-filling set that is balanced across the target's registers\n     (not collapsed onto whichever single register is easiest to match) while\n     still preferring the cleanest, most target-like documents.\n\nRationale: held-out perplexity on a broad target is minimized by training data\nwhose DISTRIBUTION matches that target, at the highest quality the pool allows.\n\nProduces /workspace/submission/selection.json (ordered pool ids, best first).\nRequires the GPT-2 token cache (built by cache_tokens.py from pool.jsonl); this\nscript rebuilds it if absent, so it is reproducible from pool.jsonl alone.\n\"\"\"\nimport json, os, numpy as np, torch, time\n\nPOOL = \"/workspace/data/pool.jsonl\"\nDEV = \"/workspace/data/multi_dev.npy\"\nOUT = \"/workspace/submission/selection.json\"\nVOCAB, EOS = 50257, 50256\nD_BIG = 1 << 20\nMIN_TOK = 64\nNEG_SAMPLE = 40000\nEPOCHS = 60\nK = 8              # register clusters\nTOPN = 3000        # vocab size for clustering histograms\nFILL_TOKENS = 24_000_000   # emit priority order covering ~2x the 12M budget\nt0 = time.time()\ndev = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# ---------------- token cache (rebuild from pool.jsonl if missing) ----------------\nCT, CO, CD = \"/tmp/pool_tokens.npy\", \"/tmp/pool_offsets.npy\", \"/tmp/pool_docids.npy\"\nif not all(os.path.exists(p) for p in (CT, CO, CD)):\n    from transformers import AutoTokenizer\n    tk = AutoTokenizer.from_pretrained(\"gpt2\")\n    chunks, off, ids, buf, bid = [], [0], [], [], []\n    def flush():\n        enc = tk(buf, add_special_tokens=False).input_ids\n        for e in enc:\n            chunks.append(np.array(e, dtype=np.uint16)); off.append(off[-1] + len(e))\n        ids.extend(bid)\n    for line in open(POOL):\n        r = json.loads(line); buf.append(r[\"text\"]); bid.append(r[\"id\"])\n        if len(buf) >= 2000:\n            flush(); buf, bid = [], []\n    if buf: flush()\n    np.save(CT, np.concatenate(chunks)); np.save(CO, np.array(off, dtype=np.int64))\n    np.save(CD, np.array(ids, dtype=np.int64))\n\ntoks = np.load(CT); offs = np.load(CO); docids = np.load(CD)\nndoc = len(docids); doclen = (offs[1:] - offs[:-1]).astype(np.int64)\nprint(f\"pool: {ndoc} docs, {len(toks)} tokens, {time.time()-t0:.1f}s\")\n\ndef bigram_hash(a, b):\n    key = a.astype(np.uint64) * np.uint64(VOCAB) + b.astype(np.uint64)\n    return ((key * np.uint64(2654435761)) & np.uint64(D_BIG - 1)).astype(np.int64)\n\nuni_all = toks.astype(np.int64)\nbg_all = bigram_hash(toks[:-1], toks[1:])\n\n# ---------------- positives from disclosed dev target ----------------\ndev_toks = np.load(DEV)\ncut = np.where(dev_toks == EOS)[0]\nds = np.concatenate([[0], cut + 1]); de = np.concatenate([cut, [len(dev_toks)]])\npos_docs = [dev_toks[s:e] for s, e in zip(ds, de) if e - s >= MIN_TOK]\nprint(f\"positives: {len(pos_docs)} dev pseudo-docs\")\n\ndef make_bags(list_of_token_arrays):\n    uf, bf, uo, bo = [], [], [0], [0]\n    for t in list_of_token_arrays:\n        t = t.astype(np.int64); uf.append(t)\n        bg = bigram_hash(t[:-1].astype(np.uint16), t[1:].astype(np.uint16)) if len(t) >= 2 else np.array([0], np.int64)\n        bf.append(bg); uo.append(uo[-1] + len(t)); bo.append(bo[-1] + len(bg))\n    return (torch.from_numpy(np.concatenate(uf)).to(dev),\n            torch.tensor(uo[:-1], dtype=torch.long, device=dev),\n            torch.from_numpy(np.concatenate(bf)).to(dev),\n            torch.tensor(bo[:-1], dtype=torch.long, device=dev))\n\n# ---------------- train linear quality/domain classifier ----------------\nrng = np.random.default_rng(0)\nneg_idx = rng.choice(ndoc, size=min(NEG_SAMPLE, ndoc), replace=False)\nneg_idx = neg_idx[doclen[neg_idx] >= MIN_TOK]\nneg_docs = [toks[offs[i]:offs[i+1]] for i in neg_idx]\ntrain_docs = pos_docs + neg_docs\nlabels = np.concatenate([np.ones(len(pos_docs)), np.zeros(len(neg_docs))]).astype(np.float32)\nw_pos = len(neg_docs) / max(1, len(pos_docs))\nuf, uo, bf, bo = make_bags(train_docs)\ny = torch.from_numpy(labels).to(dev)\nsw = torch.where(y > 0.5, torch.tensor(w_pos, device=dev), torch.tensor(1.0, device=dev))\nuni_emb = torch.nn.EmbeddingBag(VOCAB, 1, mode=\"mean\").to(dev)\nbg_emb = torch.nn.EmbeddingBag(D_BIG, 1, mode=\"mean\").to(dev)\ntorch.nn.init.zeros_(uni_emb.weight); torch.nn.init.zeros_(bg_emb.weight)\nbias = torch.zeros(1, device=dev, requires_grad=True)\nopt = torch.optim.Adam(list(uni_emb.parameters()) + list(bg_emb.parameters()) + [bias], lr=0.05, weight_decay=2e-6)\nbce = torch.nn.BCEWithLogitsLoss(reduction=\"none\")\nfor ep in range(EPOCHS):\n    opt.zero_grad()\n    logit = uni_emb(uf, uo).squeeze(1) + bg_emb(bf, bo).squeeze(1) + bias\n    (bce(logit, y) * sw).mean().backward(); opt.step()\nprint(f\"classifier trained {time.time()-t0:.1f}s\")\n\n# score all pool docs via cumulative-sum trick (mean per-token weight per doc)\nuni_w = uni_emb.weight.detach().squeeze(1); bg_w = bg_emb.weight.detach().squeeze(1); b = bias.item()\nuni_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(uni_w[torch.from_numpy(uni_all).to(dev)].double(), 0)])\nbg_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(bg_w[torch.from_numpy(bg_all).to(dev)].double(), 0)])\not = torch.from_numpy(offs).to(dev); s = ot[:-1]; e = ot[1:]\nuni_mean = (uni_cum[e] - uni_cum[s]) / (e - s).clamp(min=1)\nbg_e = (e - 1).clamp(max=len(bg_all)); bg_mean = (bg_cum[bg_e] - bg_cum[s]) / (e - 1 - s).clamp(min=1)\nscore = (uni_mean + bg_mean + b).cpu().numpy()\n\n# ---------------- quality prefilter (repetition/boilerplate) ----------------\ndef rep_stats():\n    ur = np.zeros(ndoc, np.float32); mf = np.zeros(ndoc, np.float32)\n    for i in range(ndoc):\n        d = toks[offs[i]:offs[i+1]]\n        if len(d) == 0: mf[i] = 1; continue\n        c = np.bincount(d); nz = c[c > 0]\n        ur[i] = len(nz) / len(d); mf[i] = nz.max() / len(d)\n    return ur, mf\nif os.path.exists(\"/tmp/pool_uniqratio.npy\"):\n    uniq_ratio = np.load(\"/tmp/pool_uniqratio.npy\"); maxfreq = np.load(\"/tmp/pool_maxfreq.npy\")\nelse:\n    uniq_ratio, maxfreq = rep_stats(); np.save(\"/tmp/pool_uniqratio.npy\", uniq_ratio); np.save(\"/tmp/pool_maxfreq.npy\", maxfreq)\nvalid = (doclen >= MIN_TOK) & (doclen <= 20000) & (uniq_ratio >= 0.35) & (maxfreq <= 0.18)\nprint(f\"quality prefilter keeps {int(valid.sum())}/{ndoc}\")\n\n# ---------------- cluster into registers, get target proportions ----------------\ngc = np.bincount(uni_all, minlength=VOCAB); top = np.argsort(-gc)[:TOPN]\nremap = -np.ones(VOCAB, np.int64); remap[top] = np.arange(TOPN)\ndef docvecs(tokarr, pairs):\n    V = np.zeros((len(pairs), TOPN), np.float32)\n    for k, (a, c) in enumerate(pairs):\n        d = remap[tokarr[a:c].astype(np.int64)]; d = d[d >= 0]\n        if len(d): cc = np.bincount(d, minlength=TOPN).astype(np.float32); V[k] = cc / cc.sum()\n    return V\nVp = docvecs(toks, [(offs[i], offs[i+1]) for i in range(ndoc)])\ndf = (Vp > 0).sum(0) + 1; idf = np.log(ndoc / df).astype(np.float32); Vp *= idf\nnrm = np.linalg.norm(Vp, axis=1, keepdims=True); nrm[nrm == 0] = 1; Vp /= nrm\nVpt = torch.from_numpy(Vp).to(dev)\ng = torch.Generator(device=dev); g.manual_seed(0)\ncent = Vpt[torch.randperm(ndoc, generator=g, device=dev)[:K]].clone()\nfor _ in range(25):\n    assign = (Vpt @ cent.T).argmax(1)\n    for c in range(K):\n        m = assign == c\n        if m.any(): v = Vpt[m].mean(0); cent[c] = v / (v.norm() + 1e-8)\npool_assign = assign.cpu().numpy()\ndpairs = [(s, e_) for s, e_ in zip(ds, de) if e_ - s >= MIN_TOK]\nVd = docvecs(dev_toks, dpairs); Vd *= idf; n = np.linalg.norm(Vd, axis=1, keepdims=True); n[n == 0] = 1; Vd /= n\ndev_assign = (torch.from_numpy(Vd).to(dev) @ cent.T).argmax(1).cpu().numpy()\ndevlens = np.array([e_ - s for s, e_ in dpairs])\ntprop = np.array([devlens[dev_assign == c].sum() for c in range(K)], np.float64)\ntprop = tprop / tprop.sum()\nprint(\"target cluster proportions:\", np.round(tprop, 3))\n\n# ---------------- greedy proportional interleave ----------------\n# within each cluster: valid docs ordered by classifier score (desc)\ncl_order = {}\nfor c in range(K):\n    idx = np.where((pool_assign == c) & valid)[0]\n    idx = idx[np.argsort(-score[idx])]\n    cl_order[c] = (idx, doclen[idx], 0)  # (indices, lens, pointer)\nemit = []; emit_tok = np.zeros(K); total = 0.0\nptr = {c: 0 for c in range(K)}\nactive = [c for c in range(K) if tprop[c] > 0 and len(cl_order[c][0]) > 0]\nwhile total < FILL_TOKENS and active:\n    # pick cluster with largest deficit relative to target share\n    best, bestdef = None, -1e18\n    for c in active:\n        deficit = tprop[c] * (total + 1) - emit_tok[c]\n        if deficit > bestdef: bestdef, best = deficit, c\n    idx, lens, _ = cl_order[best]; p = ptr[best]\n    emit.append(int(docids[idx[p]])); emit_tok[best] += lens[p]; total += lens[p]; ptr[best] += 1\n    if ptr[best] >= len(idx): active.remove(best)\nprint(f\"emitted {len(emit)} ids, {total:.0f} tokens across clusters; per-cluster tokens:\",\n      np.round(emit_tok / max(1, emit_tok.sum()), 3))\njson.dump(emit, open(OUT, \"w\"))\nprint(f\"wrote {OUT}  ({time.time()-t0:.1f}s)\")\n"
}
Write result
The file /workspace/curate.py has been updated successfully. (file state is current in your context — no need to Read it back)

[metadata] {"type": "update", "filePath": "/workspace/curate.py", "content": "\"\"\"Curate the raw web pool for small-LM pretraining (reproducible).\n\nCriterion (stated): select pool documents so the training mix (a) is high quality\n(junk/boilerplate removed) and (b) MATCHES THE DISCLOSED TARGET DISTRIBUTION —\nbroad multi-domain HQ English (Wikipedia + general web prose + news + technical\nQ&A). Two ingredients:\n\n  1. QUALITY / DOMAIN classifier. A linear model over hashed GPT-2 token\n     unigram+bigram features is trained to separate target-domain text (positives\n     = the disclosed dev target, split on EOS into pseudo-docs) from generic pool\n     text (negatives = random pool sample). Every pool doc gets a \"target-likeness\"\n     score. A Gopher/CCNet-style repetition prefilter drops boilerplate first.\n\n  2. DISTRIBUTION MATCHING. Pool docs are clustered (spherical k-means on tf-idf\n     token histograms). The dev target is assigned to the same clusters, giving a\n     target token-proportion per cluster. The final priority order is a greedy\n     proportional interleave: at every prefix the cluster token-mix tracks the\n     target's, and within each cluster docs are ordered by classifier score. This\n     yields a budget-filling set that is balanced across the target's registers\n     (not collapsed onto whichever single register is easiest to match) while\n     still preferring the cleanest, most target-like documents.\n\nRationale: held-out perplexity on a broad target is minimized by training data\nwhose DISTRIBUTION matches that target, at the highest quality the pool allows.\n\nProduces /workspace/submission/selection.json (ordered pool ids, best first).\nRequires the GPT-2 token cache (built by cache_tokens.py from pool.jsonl); this\nscript rebuilds it if absent, so it is reproducible from pool.jsonl alone.\n\"\"\"\nimport json, os, numpy as np, torch, time\n\nPOOL = \"/workspace/data/pool.jsonl\"\nDEV = \"/workspace/data/multi_dev.npy\"\nOUT = \"/workspace/submission/selection.json\"\nVOCAB, EOS = 50257, 50256\nD_BIG = 1 << 20\nMIN_TOK = 64\nNEG_SAMPLE = 40000\nEPOCHS = 60\nK = 8              # register clusters\nTOPN = 3000        # vocab size for clustering histograms\nFILL_TOKENS = 24_000_000   # emit priority order covering ~2x the 12M budget\nt0 = time.time()\ndev = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# ---------------- token cache (rebuild from pool.jsonl if missing) ----------------\nCT, CO, CD = \"/tmp/pool_tokens.npy\", \"/tmp/pool_offsets.npy\", \"/tmp/pool_docids.npy\"\nif not all(os.path.exists(p) for p in (CT, CO, CD)):\n    from transformers import AutoTokenizer\n    tk = AutoTokenizer.from_pretrained(\"gpt2\")\n    chunks, off, ids, buf, bid = [], [0], [], [], []\n    def flush():\n        enc = tk(buf, add_special_tokens=False).input_ids\n        for e in enc:\n            chunks.append(np.array(e, dtype=np.uint16)); off.append(off[-1] + len(e))\n        ids.extend(bid)\n    for line in open(POOL):\n        r = json.loads(line); buf.append(r[\"text\"]); bid.append(r[\"id\"])\n        if len(buf) >= 2000:\n            flush(); buf, bid = [], []\n    if buf: flush()\n    np.save(CT, np.concatenate(chunks)); np.save(CO, np.array(off, dtype=np.int64))\n    np.save(CD, np.array(ids, dtype=np.int64))\n\ntoks = np.load(CT); offs = np.load(CO); docids = np.load(CD)\nndoc = len(docids); doclen = (offs[1:] - offs[:-1]).astype(np.int64)\nprint(f\"pool: {ndoc} docs, {len(toks)} tokens, {time.time()-t0:.1f}s\")\n\ndef bigram_hash(a, b):\n    key = a.astype(np.uint64) * np.uint64(VOCAB) + b.astype(np.uint64)\n    return ((key * np.uint64(2654435761)) & np.uint64(D_BIG - 1)).astype(np.int64)\n\nuni_all = toks.astype(np.int64)\nbg_all = bigram_hash(toks[:-1], toks[1:])\n\n# ---------------- positives from disclosed dev target ----------------\ndev_toks = np.load(DEV)\ncut = np.where(dev_toks == EOS)[0]\nds = np.concatenate([[0], cut + 1]); de = np.concatenate([cut, [len(dev_toks)]])\npos_docs = [dev_toks[s:e] for s, e in zip(ds, de) if e - s >= MIN_TOK]\nprint(f\"positives: {len(pos_docs)} dev pseudo-docs\")\n\ndef make_bags(list_of_token_arrays):\n    uf, bf, uo, bo = [], [], [0], [0]\n    for t in list_of_token_arrays:\n        t = t.astype(np.int64); uf.append(t)\n        bg = bigram_hash(t[:-1].astype(np.uint16), t[1:].astype(np.uint16)) if len(t) >= 2 else np.array([0], np.int64)\n        bf.append(bg); uo.append(uo[-1] + len(t)); bo.append(bo[-1] + len(bg))\n    return (torch.from_numpy(np.concatenate(uf)).to(dev),\n            torch.tensor(uo[:-1], dtype=torch.long, device=dev),\n            torch.from_numpy(np.concatenate(bf)).to(dev),\n            torch.tensor(bo[:-1], dtype=torch.long, device=dev))\n\n# ---------------- train linear quality/domain classifier ----------------\nrng = np.random.default_rng(0)\nneg_idx = rng.choice(ndoc, size=min(NEG_SAMPLE, ndoc), replace=False)\nneg_idx = neg_idx[doclen[neg_idx] >= MIN_TOK]\nneg_docs = [toks[offs[i]:offs[i+1]] for i in neg_idx]\ntrain_docs = pos_docs + neg_docs\nlabels = np.concatenate([np.ones(len(pos_docs)), np.zeros(len(neg_docs))]).astype(np.float32)\nw_pos = len(neg_docs) / max(1, len(pos_docs))\nuf, uo, bf, bo = make_bags(train_docs)\ny = torch.from_numpy(labels).to(dev)\nsw = torch.where(y > 0.5, torch.tensor(w_pos, device=dev), torch.tensor(1.0, device=dev))\nuni_emb = torch.nn.EmbeddingBag(VOCAB, 1, mode=\"mean\").to(dev)\nbg_emb = torch.nn.EmbeddingBag(D_BIG, 1, mode=\"mean\").to(dev)\ntorch.nn.init.zeros_(uni_emb.weight); torch.nn.init.zeros_(bg_emb.weight)\nbias = torch.zeros(1, device=dev, requires_grad=True)\nopt = torch.optim.Adam(list(uni_emb.parameters()) + list(bg_emb.parameters()) + [bias], lr=0.05, weight_decay=2e-6)\nbce = torch.nn.BCEWithLogitsLoss(reduction=\"none\")\nfor ep in range(EPOCHS):\n    opt.zero_grad()\n    logit = uni_emb(uf, uo).squeeze(1) + bg_emb(bf, bo).squeeze(1) + bias\n    (bce(logit, y) * sw).mean().backward(); opt.step()\nprint(f\"classifier trained {time.time()-t0:.1f}s\")\n\n# score all pool docs via cumulative-sum trick (mean per-token weight per doc)\nuni_w = uni_emb.weight.detach().squeeze(1); bg_w = bg_emb.weight.detach().squeeze(1); b = bias.item()\nuni_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(uni_w[torch.from_numpy(uni_all).to(dev)].double(), 0)])\nbg_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(bg_w[torch.from_numpy(bg_all).to(dev)].double(), 0)])\not = torch.from_numpy(offs).to(dev); s = ot[:-1]; e = ot[1:]\nuni_mean = (uni_cum[e] - uni_cum[s]) / (e - s).clamp(min=1)\nbg_e = (e - 1).clamp(max=len(bg_all)); bg_mean = (bg_cum[bg_e] - bg_cum[s]) / (e - 1 - s).clamp(min=1)\nscore = (uni_mean + bg_mean + b).cpu().numpy()\n\n# ---------------- quality prefilter (repetition/boilerplate) ----------------\ndef rep_stats():\n    ur = np.zeros(ndoc, np.float32); mf = np.zeros(ndoc, np.float32)\n    for i in range(ndoc):\n        d = toks[offs[i]:offs[i+1]]\n        if len(d) == 0: mf[i] = 1; continue\n        c = np.bincount(d); nz = c[c > 0]\n        ur[i] = len(nz) / len(d); mf[i] = nz.max() / len(d)\n    return ur, mf\nif os.path.exists(\"/tmp/pool_uniqratio.npy\"):\n    uniq_ratio = np.load(\"/tmp/pool_uniqratio.npy\"); maxfreq = np.load(\"/tmp/pool_maxfreq.npy\")\nelse:\n    uniq_ratio, maxfreq = rep_stats(); np.save(\"/tmp/pool_uniqratio.npy\", uniq_ratio); np.save(\"/tmp/pool_maxfreq.npy\", maxfreq)\nvalid = (doclen >= MIN_TOK) & (doclen <= 20000) & (uniq_ratio >= 0.35) & (maxfreq <= 0.18)\nprint(f\"quality prefilter keeps {int(valid.sum())}/{ndoc}\")\n\n# ---------------- cluster into registers, get target proportions ----------------\ngc = np.bincount(uni_all, minlength=VOCAB); top = np.argsort(-gc)[:TOPN]\nremap = -np.ones(VOCAB, np.int64); remap[top] = np.arange(TOPN)\ndef docvecs(tokarr, pairs):\n    V = np.zeros((len(pairs), TOPN), np.float32)\n    for k, (a, c) in enumerate(pairs):\n        d = remap[tokarr[a:c].astype(np.int64)]; d = d[d >= 0]\n        if len(d): cc = np.bincount(d, minlength=TOPN).astype(np.float32); V[k] = cc / cc.sum()\n    return V\nVp = docvecs(toks, [(offs[i], offs[i+1]) for i in range(ndoc)])\ndf = (Vp > 0).sum(0) + 1; idf = np.log(ndoc / df).astype(np.float32); Vp *= idf\nnrm = np.linalg.norm(Vp, axis=1, keepdims=True); nrm[nrm == 0] = 1; Vp /= nrm\nVpt = torch.from_numpy(Vp).to(dev)\ng = torch.Generator(device=dev); g.manual_seed(0)\ncent = Vpt[torch.randperm(ndoc, generator=g, device=dev)[:K]].clone()\nfor _ in range(25):\n    assign = (Vpt @ cent.T).argmax(1)\n    for c in range(K):\n        m = assign == c\n        if m.any(): v = Vpt[m].mean(0); cent[c] = v / (v.norm() + 1e-8)\npool_assign = assign.cpu().numpy()\ndpairs = [(s, e_) for s, e_ in zip(ds, de) if e_ - s >= MIN_TOK]\nVd = docvecs(dev_toks, dpairs); Vd *= idf; n = np.linalg.norm(Vd, axis=1, keepdims=True); n[n == 0] = 1; Vd /= n\ndev_assign = (torch.from_numpy(Vd).to(dev) @ cent.T).argmax(1).cpu().numpy()\ndevlens = np.array([e_ - s for s, e_ in dpairs])\ntprop = np.array([devlens[dev_assign == c].sum() for c in range(K)], np.float64)\ntprop = tprop / tprop.sum()\nprint(\"target cluster proportions:\", np.round(tprop, 3))\n\n# ---------------- greedy proportional interleave ----------------\n# within each cluster: valid docs ordered by classifier score (desc)\ncl_order = {}\nfor c in range(K):\n    idx = np.where((pool_assign == c) & valid)[0]\n    idx = idx[np.argsort(-score[idx])]\n    cl_order[c] = (idx, doclen[idx], 0)  # (indices, lens, pointer)\nemit = []; emit_tok = np.zeros(K); total = 0.0\nptr = {c: 0 for c in range(K)}\nactive = [c for c in range(K) if tprop[c] > 0 and len(cl_order[c][0]) > 0]\nwhile total < FILL_TOKENS and active:\n    # pick cluster with largest deficit relative to target share\n    best, bestdef = None, -1e18\n    for c in active:\n        deficit = tprop[c] * (total + 1) - emit_tok[c]\n        if deficit > bestdef: bestdef, best = deficit, c\n    idx, lens, _ = cl_order[best]; p = ptr[best]\n    emit.append(int(docids[idx[p]])); emit_tok[best] += lens[p]; total += lens[p]; ptr[best] += 1\n    if ptr[best] >= len(idx): active.remove(best)\nprint(f\"emitted {len(emit)} ids, {total:.0f} tokens across clusters; per-cluster tokens:\",\n      np.round(emit_tok / max(1, emit_tok.sum()), 3))\njson.dump(emit, open(OUT, \"w\"))\nprint(f\"wrote {OUT}  ({time.time()-t0:.1f}s)\")\n", "structuredPatch": [{"oldStart": 1, "oldLines": 158, "newStart": 1, "newLines": 193, "lines": ["-\"\"\"Curate the raw web pool for small-LM pretraining.", "+\"\"\"Curate the raw web pool for small-LM pretraining (reproducible).", " ", "-Criterion (stated, reproducible): select the pool documents whose GPT-2 token", "-n-gram distribution is most similar to the DISCLOSED high-quality multi-domain", "-target (Wikipedia + general web prose + news + technical Q&A). We train a linear", "-\"quality/domain\" classifier that separates target-domain text (positives, taken", "-from the disclosed dev target) from generic pool text (negatives, a random pool", "-sample), using hashed GPT-2 token unigram+bigram features. Every pool document is", "-scored by the classifier; documents are emitted in descending score order (best", "-first) after a light junk filter. The training pipeline consumes this ordered", "-list until the 12M-token budget is full.", "+Criterion (stated): select pool documents so the training mix (a) is high quality", "+(junk/boilerplate removed) and (b) MATCHES THE DISCLOSED TARGET DISTRIBUTION —", "+broad multi-domain HQ English (Wikipedia + general web prose + news + technical", "+Q&A). Two ingredients:", " ", "-This is a distribution-matching / quality-classifier approach (cf. GPT-3 / CCNet", "-/ fastText-style data selection): training data closest to the target domain", "-minimizes held-out perplexity on that domain.", "+  1. QUALITY / DOMAIN classifier. A linear model over hashed GPT-2 token", "+     unigram+bigram features is trained to separate target-domain text (positives", "+     = the disclosed dev target, split on EOS into pseudo-docs) from generic pool", "+     text (negatives = random pool sample). Every pool doc gets a \"target-likeness\"", "+     score. A Gopher/CCNet-style repetition prefilter drops boilerplate first.", "+", "+  2. DISTRIBUTION MATCHING. Pool docs are clustered (spherical k-means on tf-idf", "+     token histograms). The dev target is assigned to the same clusters, giving a", "+     target token-proportion per cluster. The final priority order is a greedy", "+     proportional interleave: at every prefix the cluster token-mix tracks the", "+     target's, and within each cluster docs are ordered by classifier score. This", "+     yields a budget-filling set that is balanced across the target's registers", "+     (not collapsed onto whichever single register is easiest to match) while", "+     still preferring the cleanest, most target-like documents.", "+", "+Rationale: held-out perplexity on a broad target is minimized by training data", "+whose DISTRIBUTION matches that target, at the highest quality the pool allows.", "+", "+Produces /workspace/submission/selection.json (ordered pool ids, best first).", "+Requires the GPT-2 token cache (built by cache_tokens.py from pool.jsonl); this", "+script rebuilds it if absent, so it is reproducible from pool.jsonl alone.", " \"\"\"", "-import json, numpy as np, torch, time", "+import json, os, numpy as np, torch, time", " ", "-D_BIG = 1 << 20          # bigram hash buckets", "-VOCAB = 50257", "-EOS = 50256", "-MIN_TOK = 64             # junk filter: drop very short docs", "-NEG_SAMPLE = 40000       # random pool docs used as negatives", "+POOL = \"/workspace/data/pool.jsonl\"", "+DEV = \"/workspace/data/multi_dev.npy\"", "+OUT = \"/workspace/submission/selection.json\"", "+VOCAB, EOS = 50257, 50256", "+D_BIG = 1 << 20", "+MIN_TOK = 64", "+NEG_SAMPLE = 40000", " EPOCHS = 60", "+K = 8              # register clusters", "+TOPN = 3000        # vocab size for clustering histograms", "+FILL_TOKENS = 24_000_000   # emit priority order covering ~2x the 12M budget", " t0 = time.time()", " dev = \"cuda\" if torch.cuda.is_available() else \"cpu\"", " ", "-# ---- load cached pool tokens (produced by cache_tokens.py) ----", "-toks = np.load(\"/tmp/pool_tokens.npy\")          # uint16, all docs concatenated", "-offs = np.load(\"/tmp/pool_offsets.npy\")         # int64, len = ndoc+1", "-docids = np.load(\"/tmp/pool_docids.npy\")        # int64, len = ndoc  (pool ids in order)", "-ndoc = len(docids)", "-doclen = (offs[1:] - offs[:-1]).astype(np.int64)", "+# ---------------- token cache (rebuild from pool.jsonl if missing) ----------------", "+CT, CO, CD = \"/tmp/pool_tokens.npy\", \"/tmp/pool_offsets.npy\", \"/tmp/pool_docids.npy\"", "+if not all(os.path.exists(p) for p in (CT, CO, CD)):", "+    from transformers import AutoTokenizer", "+    tk = AutoTokenizer.from_pretrained(\"gpt2\")", "+    chunks, off, ids, buf, bid = [], [0], [], [], []", "+    def flush():", "+        enc = tk(buf, add_special_tokens=False).input_ids", "+        for e in enc:", "+            chunks.append(np.array(e, dtype=np.uint16)); off.append(off[-1] + len(e))", "+        ids.extend(bid)", "+    for line in open(POOL):", "+        r = json.loads(line); buf.append(r[\"text\"]); bid.append(r[\"id\"])", "+        if len(buf) >= 2000:", "+            flush(); buf, bid = [], []", "+    if buf: flush()", "+    np.save(CT, np.concatenate(chunks)); np.save(CO, np.array(off, dtype=np.int64))", "+    np.save(CD, np.array(ids, dtype=np.int64))", "+", "+toks = np.load(CT); offs = np.load(CO); docids = np.load(CD)", "+ndoc = len(docids); doclen = (offs[1:] - offs[:-1]).astype(np.int64)", " print(f\"pool: {ndoc} docs, {len(toks)} tokens, {time.time()-t0:.1f}s\")", " ", " def bigram_hash(a, b):", "-    # a,b uint16 token ids -> bucket in [0,D_BIG)", "     key = a.astype(np.uint64) * np.uint64(VOCAB) + b.astype(np.uint64)", "     return ((key * np.uint64(2654435761)) & np.uint64(D_BIG - 1)).astype(np.int64)", " ", "-# precompute global bigram stream (cross-doc boundary bigrams are negligible noise)", "-bg_all = bigram_hash(toks[:-1], toks[1:])", " uni_all = toks.astype(np.int64)", "+bg_all = bigram_hash(toks[:-1], toks[1:])", " ", "-# ---- positives: decode-free, use dev target token stream split on EOS ----", "-dev_toks = np.load(\"/workspace/data/multi_dev.npy\")", "-# split dev into pseudo-docs on EOS", "+# ---------------- positives from disclosed dev target ----------------", "+dev_toks = np.load(DEV)", " cut = np.where(dev_toks == EOS)[0]", "-starts = np.concatenate([[0], cut + 1])", "-ends = np.concatenate([cut, [len(dev_toks)]])", "-pos_docs = [dev_toks[s:e] for s, e in zip(starts, ends) if e - s >= MIN_TOK]", "+ds = np.concatenate([[0], cut + 1]); de = np.concatenate([cut, [len(dev_toks)]])", "+pos_docs = [dev_toks[s:e] for s, e in zip(ds, de) if e - s >= MIN_TOK]", " print(f\"positives: {len(pos_docs)} dev pseudo-docs\")", " ", "-# ---- build EmbeddingBag inputs for a set of docs given (uni_stream, bg_stream, per-doc slices) ----", "-def bags_from_slices(slices, uni_stream, bg_stream):", "-    uni_in, bg_in, off_list, cur_u, cur_b = [], [], [0], 0, 0", "-    for (s, e) in slices:", "-        u = uni_stream[s:e]", "-        b = bg_stream[s:max(s, e-1)] if e - 1 > s else bg_stream[s:e]", "-        uni_in.append(u); bg_in.append(b)", "-        cur_u += len(u)", "-    # offsets computed below per-stream", "-    return uni_in, bg_in", "-", "-# Simpler: build flat + offsets for uni and bg separately for an arbitrary list of token arrays", " def make_bags(list_of_token_arrays):", "-    uni_flat, bg_flat, uni_off, bg_off = [], [], [0], [0]", "+    uf, bf, uo, bo = [], [], [0], [0]", "     for t in list_of_token_arrays:", "-        t = t.astype(np.int64)", "-        uni_flat.append(t)", "-        if len(t) >= 2:", "-            bg = bigram_hash(t[:-1].astype(np.uint16), t[1:].astype(np.uint16))", "-        else:", "-            bg = np.array([0], dtype=np.int64)", "-        bg_flat.append(bg)", "-        uni_off.append(uni_off[-1] + len(t))", "-        bg_off.append(bg_off[-1] + len(bg))", "-    uni_flat = torch.from_numpy(np.concatenate(uni_flat)).to(dev)", "-    bg_flat = torch.from_numpy(np.concatenate(bg_flat)).to(dev)", "-    uni_off = torch.tensor(uni_off[:-1], dtype=torch.long, device=dev)", "-    bg_off = torch.tensor(bg_off[:-1], dtype=torch.long, device=dev)", "-    return uni_flat, uni_off, bg_flat, bg_off", "+        t = t.astype(np.int64); uf.append(t)", "+        bg = bigram_hash(t[:-1].astype(np.uint16), t[1:].astype(np.uint16)) if len(t) >= 2 else np.array([0], np.int64)", "+        bf.append(bg); uo.append(uo[-1] + len(t)); bo.append(bo[-1] + len(bg))", "+    return (torch.from_numpy(np.concatenate(uf)).to(dev),", "+            torch.tensor(uo[:-1], dtype=torch.long, device=dev),", "+            torch.from_numpy(np.concatenate(bf)).to(dev),", "+            torch.tensor(bo[:-1], dtype=torch.long, device=dev))", " ", "-# ---- assemble training set ----", "+# ---------------- train linear quality/domain classifier ----------------", " rng = np.random.default_rng(0)", " neg_idx = rng.choice(ndoc, size=min(NEG_SAMPLE, ndoc), replace=False)", " neg_idx = neg_idx[doclen[neg_idx] >= MIN_TOK]", " neg_docs = [toks[offs[i]:offs[i+1]] for i in neg_idx]", "-", " train_docs = pos_docs + neg_docs", " labels = np.concatenate([np.ones(len(pos_docs)), np.zeros(len(neg_docs))]).astype(np.float32)", "-# class weight to balance", " w_pos = len(neg_docs) / max(1, len(pos_docs))", "-uni_flat, uni_off, bg_flat, bg_off = make_bags(train_docs)", "+uf, uo, bf, bo = make_bags(train_docs)", " y = torch.from_numpy(labels).to(dev)", "-sample_w = torch.where(y > 0.5, torch.tensor(w_pos, device=dev), torch.tensor(1.0, device=dev))", "-", "-# ---- model: two EmbeddingBags (unigram, bigram), mean-pooled, dim=1 => logistic regression ----", "+sw = torch.where(y > 0.5, torch.tensor(w_pos, device=dev), torch.tensor(1.0, device=dev))", " uni_emb = torch.nn.EmbeddingBag(VOCAB, 1, mode=\"mean\").to(dev)", " bg_emb = torch.nn.EmbeddingBag(D_BIG, 1, mode=\"mean\").to(dev)", " torch.nn.init.zeros_(uni_emb.weight); torch.nn.init.zeros_(bg_emb.weight)", " bias = torch.zeros(1, device=dev, requires_grad=True)", "-params = list(uni_emb.parameters()) + list(bg_emb.parameters()) + [bias]", "-opt = torch.optim.Adam(params, lr=0.05, weight_decay=2e-6)", "+opt = torch.optim.Adam(list(uni_emb.parameters()) + list(bg_emb.parameters()) + [bias], lr=0.05, weight_decay=2e-6)", " bce = torch.nn.BCEWithLogitsLoss(reduction=\"none\")", "-", " for ep in range(EPOCHS):", "     opt.zero_grad()", "-    logit = (uni_emb(uni_flat, uni_off).squeeze(1)", "-             + bg_emb(bg_flat, bg_off).squeeze(1) + bias)", "-    loss = (bce(logit, y) * sample_w).mean()", "-    loss.backward(); opt.step()", "-with torch.no_grad():", "-    pred = torch.sigmoid((uni_emb(uni_flat, uni_off).squeeze(1)", "-                          + bg_emb(bg_flat, bg_off).squeeze(1) + bias))", "-    acc = ((pred > 0.5).float() == y).float().mean().item()", "-print(f\"classifier trained: loss {loss.item():.4f} acc {acc:.3f} {time.time()-t0:.1f}s\")", "+    logit = uni_emb(uf, uo).squeeze(1) + bg_emb(bf, bo).squeeze(1) + bias", "+    (bce(logit, y) * sw).mean().backward(); opt.step()", "+print(f\"classifier trained {time.time()-t0:.1f}s\")", " ", "-# ---- score ALL pool docs in batches using the global streams ----", "-uni_w = uni_emb.weight.detach().squeeze(1)      # [VOCAB]", "-bg_w = bg_emb.weight.detach().squeeze(1)        # [D_BIG]", "-b = bias.item()", "-uni_all_t = torch.from_numpy(uni_all).to(dev)", "-bg_all_t = torch.from_numpy(bg_all).to(dev)", "-# cumulative sums of per-token weight, so a doc mean = (cumsum[e]-cumsum[s])/len", "-uni_wt_stream = uni_w[uni_all_t]                # weight per token position", "-bg_wt_stream = bg_w[bg_all_t]", "-uni_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(uni_wt_stream.double(), 0)])", "-bg_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(bg_wt_stream.double(), 0)])", "-offs_t = torch.from_numpy(offs).to(dev)", "-s = offs_t[:-1]; e = offs_t[1:]", "+# score all pool docs via cumulative-sum trick (mean per-token weight per doc)", "+uni_w = uni_emb.weight.detach().squeeze(1); bg_w = bg_emb.weight.detach().squeeze(1); b = bias.item()", "+uni_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(uni_w[torch.from_numpy(uni_all).to(dev)].double(), 0)])", "+bg_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(bg_w[torch.from_numpy(bg_all).to(dev)].double(), 0)])", "+ot = torch.from_numpy(offs).to(dev); s = ot[:-1]; e = ot[1:]", " uni_mean = (uni_cum[e] - uni_cum[s]) / (e - s).clamp(min=1)", "-be = (e - 1).clamp(min=s + 0 * s)  # bigram end index", "-# bigram doc mean over stream positions [s, e-1); guard tiny docs", "-bg_len = (e - 1 - s).clamp(min=1)", "-bg_e = (e - 1).clamp(max=len(bg_all))", "-bg_mean = (bg_cum[bg_e] - bg_cum[s]) / bg_len", "+bg_e = (e - 1).clamp(max=len(bg_all)); bg_mean = (bg_cum[bg_e] - bg_cum[s]) / (e - 1 - s).clamp(min=1)", " score = (uni_mean + bg_mean + b).cpu().numpy()", " ", "-# ---- quality prefilter (Gopher/CCNet-style, computed from tokens) ----", "-# repetition stats: unique-token ratio (low => boilerplate/repetitive),", "-# max-token-frequency ratio (high => whitespace/char-run junk like \"Index of /...\").", "-uniq_ratio = np.load(\"/tmp/pool_uniqratio.npy\")", "-maxfreq = np.load(\"/tmp/pool_maxfreq.npy\")", "+# ---------------- quality prefilter (repetition/boilerplate) ----------------", "+def rep_stats():", "+    ur = np.zeros(ndoc, np.float32); mf = np.zeros(ndoc, np.float32)", "+    for i in range(ndoc):", "+        d = toks[offs[i]:offs[i+1]]", "+        if len(d) == 0: mf[i] = 1; continue", "+        c = np.bincount(d); nz = c[c > 0]", "+        ur[i] = len(nz) / len(d); mf[i] = nz.max() / len(d)", "+    return ur, mf", "+if os.path.exists(\"/tmp/pool_uniqratio.npy\"):", "+    uniq_ratio = np.load(\"/tmp/pool_uniqratio.npy\"); maxfreq = np.load(\"/tmp/pool_maxfreq.npy\")", "+else:", "+    uniq_ratio, maxfreq = rep_stats(); np.save(\"/tmp/pool_uniqratio.npy\", uniq_ratio); np.save(\"/tmp/pool_maxfreq.npy\", maxfreq)", " valid = (doclen >= MIN_TOK) & (doclen <= 20000) & (uniq_ratio >= 0.35) & (maxfreq <= 0.18)", "-print(f\"quality prefilter keeps {valid.sum()}/{ndoc} docs\")", "+print(f\"quality prefilter keeps {int(valid.sum())}/{ndoc}\")", " ", "-# ---- order ----", "-order = np.argsort(-score)", "-order = order[valid[order]]", "-sel_ids = docids[order].tolist()", "-# cover comfortably beyond 12M tokens (emit top docs summing to ~40M tokens)", "-cum = np.cumsum(doclen[order])", "-keep = int(np.searchsorted(cum, 40_000_000)) + 1", "-sel_ids = sel_ids[:max(keep, 60000)]", "-json.dump(sel_ids, open(\"/workspace/submission/selection.json\", \"w\"))", "-print(f\"selection: {len(sel_ids)} ids, top-doc tokens sum {cum[min(keep,len(cum)-1)]:.0f}, {time.time()-t0:.1f}s\")", "-print(\"score stats: min %.3f med %.3f max %.3f\" % (score.min(), np.median(score), score.max()))", "+# ---------------- cluster into registers, get target proportions ----------------", "+gc = np.bincount(uni_all, minlength=VOCAB); top = np.argsort(-gc)[:TOPN]", "+remap = -np.ones(VOCAB, np.int64); remap[top] = np.arange(TOPN)", "+def docvecs(tokarr, pairs):", "+    V = np.zeros((len(pairs), TOPN), np.float32)", "+    for k, (a, c) in enumerate(pairs):", "+        d = remap[tokarr[a:c].astype(np.int64)]; d = d[d >= 0]", "+        if len(d): cc = np.bincount(d, minlength=TOPN).astype(np.float32); V[k] = cc / cc.sum()", "+    return V", "+Vp = docvecs(toks, [(offs[i], offs[i+1]) for i in range(ndoc)])", "+df = (Vp > 0).sum(0) + 1; idf = np.log(ndoc / df).astype(np.float32); Vp *= idf", "+nrm = np.linalg.norm(Vp, axis=1, keepdims=True); nrm[nrm == 0] = 1; Vp /= nrm", "+Vpt = torch.from_numpy(Vp).to(dev)", "+g = torch.Generator(device=dev); g.manual_seed(0)", "+cent = Vpt[torch.randperm(ndoc, generator=g, device=dev)[:K]].clone()", "+for _ in range(25):", "+    assign = (Vpt @ cent.T).argmax(1)", "+    for c in range(K):", "+        m = assign == c", "+        if m.any(): v = Vpt[m].mean(0); cent[c] = v / (v.norm() + 1e-8)", "+pool_assign = assign.cpu().numpy()", "+dpairs = [(s, e_) for s, e_ in zip(ds, de) if e_ - s >= MIN_TOK]", "+Vd = docvecs(dev_toks, dpairs); Vd *= idf; n = np.linalg.norm(Vd, axis=1, keepdims=True); n[n == 0] = 1; Vd /= n", "+dev_assign = (torch.from_numpy(Vd).to(dev) @ cent.T).argmax(1).cpu().numpy()", "+devlens = np.array([e_ - s for s, e_ in dpairs])", "+tprop = np.array([devlens[dev_assign == c].sum() for c in range(K)], np.float64)", "+tprop = tprop / tprop.sum()", "+print(\"target cluster proportions:\", np.round(tprop, 3))", "+", "+# ---------------- greedy proportional interleave ----------------", "+# within each cluster: valid docs ordered by classifier score (desc)", "+cl_order = {}", "+for c in range(K):", "+    idx = np.where((pool_assign == c) & valid)[0]", "+    idx = idx[np.argsort(-score[idx])]", "+    cl_order[c] = (idx, doclen[idx], 0)  # (indices, lens, pointer)", "+emit = []; emit_tok = np.zeros(K); total = 0.0", "+ptr = {c: 0 for c in range(K)}", "+active = [c for c in range(K) if tprop[c] > 0 and len(cl_order[c][0]) > 0]", "+while total < FILL_TOKENS and active:", "+    # pick cluster with largest deficit relative to target share", "+    best, bestdef = None, -1e18", "+    for c in active:", "+        deficit = tprop[c] * (total + 1) - emit_tok[c]", "+        if deficit > bestdef: bestdef, best = deficit, c", "+    idx, lens, _ = cl_order[best]; p = ptr[best]", "+    emit.append(int(docids[idx[p]])); emit_tok[best] += lens[p]; total += lens[p]; ptr[best] += 1", "+    if ptr[best] >= len(idx): active.remove(best)", "+print(f\"emitted {len(emit)} ids, {total:.0f} tokens across clusters; per-cluster tokens:\",", "+      np.round(emit_tok / max(1, emit_tok.sum()), 3))", "+json.dump(emit, open(OUT, \"w\"))", "+print(f\"wrote {OUT}  ({time.time()-t0:.1f}s)\")"]}], "originalFile": "\"\"\"Curate the raw web pool for small-LM pretraining.\n\nCriterion (stated, reproducible): select the pool documents whose GPT-2 token\nn-gram distribution is most similar to the DISCLOSED high-quality multi-domain\ntarget (Wikipedia + general web prose + news + technical Q&A). We train a linear\n\"quality/domain\" classifier that separates target-domain text (positives, taken\nfrom the disclosed dev target) from generic pool text (negatives, a random pool\nsample), using hashed GPT-2 token unigram+bigram features. Every pool document is\nscored by the classifier; documents are emitted in descending score order (best\nfirst) after a light junk filter. The training pipeline consumes this ordered\nlist until the 12M-token budget is full.\n\nThis is a distribution-matching / quality-classifier approach (cf. GPT-3 / CCNet\n/ fastText-style data selection): training data closest to the target domain\nminimizes held-out perplexity on that domain.\n\"\"\"\nimport json, numpy as np, torch, time\n\nD_BIG = 1 << 20          # bigram hash buckets\nVOCAB = 50257\nEOS = 50256\nMIN_TOK = 64             # junk filter: drop very short docs\nNEG_SAMPLE = 40000       # random pool docs used as negatives\nEPOCHS = 60\nt0 = time.time()\ndev = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# ---- load cached pool tokens (produced by cache_tokens.py) ----\ntoks = np.load(\"/tmp/pool_tokens.npy\")          # uint16, all docs concatenated\noffs = np.load(\"/tmp/pool_offsets.npy\")         # int64, len = ndoc+1\ndocids = np.load(\"/tmp/pool_docids.npy\")        # int64, len = ndoc  (pool ids in order)\nndoc = len(docids)\ndoclen = (offs[1:] - offs[:-1]).astype(np.int64)\nprint(f\"pool: {ndoc} docs, {len(toks)} tokens, {time.time()-t0:.1f}s\")\n\ndef bigram_hash(a, b):\n    # a,b uint16 token ids -> bucket in [0,D_BIG)\n    key = a.astype(np.uint64) * np.uint64(VOCAB) + b.astype(np.uint64)\n    return ((key * np.uint64(2654435761)) & np.uint64(D_BIG - 1)).astype(np.int64)\n\n# precompute global bigram stream (cross-doc boundary bigrams are negligible noise)\nbg_all = bigram_hash(toks[:-1], toks[1:])\nuni_all = toks.astype(np.int64)\n\n# ---- positives: decode-free, use dev target token stream split on EOS ----\ndev_toks = np.load(\"/workspace/data/multi_dev.npy\")\n# split dev into pseudo-docs on EOS\ncut = np.where(dev_toks == EOS)[0]\nstarts = np.concatenate([[0], cut + 1])\nends = np.concatenate([cut, [len(dev_toks)]])\npos_docs = [dev_toks[s:e] for s, e in zip(starts, ends) if e - s >= MIN_TOK]\nprint(f\"positives: {len(pos_docs)} dev pseudo-docs\")\n\n# ---- build EmbeddingBag inputs for a set of docs given (uni_stream, bg_stream, per-doc slices) ----\ndef bags_from_slices(slices, uni_stream, bg_stream):\n    uni_in, bg_in, off_list, cur_u, cur_b = [], [], [0], 0, 0\n    for (s, e) in slices:\n        u = uni_stream[s:e]\n        b = bg_stream[s:max(s, e-1)] if e - 1 > s else bg_stream[s:e]\n        uni_in.append(u); bg_in.append(b)\n        cur_u += len(u)\n    # offsets computed below per-stream\n    return uni_in, bg_in\n\n# Simpler: build flat + offsets for uni and bg separately for an arbitrary list of token arrays\ndef make_bags(list_of_token_arrays):\n    uni_flat, bg_flat, uni_off, bg_off = [], [], [0], [0]\n    for t in list_of_token_arrays:\n        t = t.astype(np.int64)\n        uni_flat.append(t)\n        if len(t) >= 2:\n            bg = bigram_hash(t[:-1].astype(np.uint16), t[1:].astype(np.uint16))\n        else:\n            bg = np.array([0], dtype=np.int64)\n        bg_flat.append(bg)\n        uni_off.append(uni_off[-1] + len(t))\n        bg_off.append(bg_off[-1] + len(bg))\n    uni_flat = torch.from_numpy(np.concatenate(uni_flat)).to(dev)\n    bg_flat = torch.from_numpy(np.concatenate(bg_flat)).to(dev)\n    uni_off = torch.tensor(uni_off[:-1], dtype=torch.long, device=dev)\n    bg_off = torch.tensor(bg_off[:-1], dtype=torch.long, device=dev)\n    return uni_flat, uni_off, bg_flat, bg_off\n\n# ---- assemble training set ----\nrng = np.random.default_rng(0)\nneg_idx = rng.choice(ndoc, size=min(NEG_SAMPLE, ndoc), replace=False)\nneg_idx = neg_idx[doclen[neg_idx] >= MIN_TOK]\nneg_docs = [toks[offs[i]:offs[i+1]] for i in neg_idx]\n\ntrain_docs = pos_docs + neg_docs\nlabels = np.concatenate([np.ones(len(pos_docs)), np.zeros(len(neg_docs))]).astype(np.float32)\n# class weight to balance\nw_pos = len(neg_docs) / max(1, len(pos_docs))\nuni_flat, uni_off, bg_flat, bg_off = make_bags(train_docs)\ny = torch.from_numpy(labels).to(dev)\nsample_w = torch.where(y > 0.5, torch.tensor(w_pos, device=dev), torch.tensor(1.0, device=dev))\n\n# ---- model: two EmbeddingBags (unigram, bigram), mean-pooled, dim=1 => logistic regression ----\nuni_emb = torch.nn.EmbeddingBag(VOCAB, 1, mode=\"mean\").to(dev)\nbg_emb = torch.nn.EmbeddingBag(D_BIG, 1, mode=\"mean\").to(dev)\ntorch.nn.init.zeros_(uni_emb.weight); torch.nn.init.zeros_(bg_emb.weight)\nbias = torch.zeros(1, device=dev, requires_grad=True)\nparams = list(uni_emb.parameters()) + list(bg_emb.parameters()) + [bias]\nopt = torch.optim.Adam(params, lr=0.05, weight_decay=2e-6)\nbce = torch.nn.BCEWithLogitsLoss(reduction=\"none\")\n\nfor ep in range(EPOCHS):\n    opt.zero_grad()\n    logit = (uni_emb(uni_flat, uni_off).squeeze(1)\n             + bg_emb(bg_flat, bg_off).squeeze(1) + bias)\n    loss = (bce(logit, y) * sample_w).mean()\n    loss.backward(); opt.step()\nwith torch.no_grad():\n    pred = torch.sigmoid((uni_emb(uni_flat, uni_off).squeeze(1)\n                          + bg_emb(bg_flat, bg_off).squeeze(1) + bias))\n    acc = ((pred > 0.5).float() == y).float().mean().item()\nprint(f\"classifier trained: loss {loss.item():.4f} acc {acc:.3f} {time.time()-t0:.1f}s\")\n\n# ---- score ALL pool docs in batches using the global streams ----\nuni_w = uni_emb.weight.detach().squeeze(1)      # [VOCAB]\nbg_w = bg_emb.weight.detach().squeeze(1)        # [D_BIG]\nb = bias.item()\nuni_all_t = torch.from_numpy(uni_all).to(dev)\nbg_all_t = torch.from_numpy(bg_all).to(dev)\n# cumulative sums of per-token weight, so a doc mean = (cumsum[e]-cumsum[s])/len\nuni_wt_stream = uni_w[uni_all_t]                # weight per token position\nbg_wt_stream = bg_w[bg_all_t]\nuni_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(uni_wt_stream.double(), 0)])\nbg_cum = torch.cat([torch.zeros(1, device=dev), torch.cumsum(bg_wt_stream.double(), 0)])\noffs_t = torch.from_numpy(offs).to(dev)\ns = offs_t[:-1]; e = offs_t[1:]\nuni_mean = (uni_cum[e] - uni_cum[s]) / (e - s).clamp(min=1)\nbe = (e - 1).clamp(min=s + 0 * s)  # bigram end index\n# bigram doc mean over stream positions [s, e-1); guard tiny docs\nbg_len = (e - 1 - s).clamp(min=1)\nbg_e = (e - 1).clamp(max=len(bg_all))\nbg_mean = (bg_cum[bg_e] - bg_cum[s]) / bg_len\nscore = (uni_mean + bg_mean + b).cpu().numpy()\n\n# ---- quality prefilter (Gopher/CCNet-style, computed from tokens) ----\n# repetition stats: unique-token ratio (low => boilerplate/repetitive),\n# max-token-frequency ratio (high => whitespace/char-run junk like \"Index of /...\").\nuniq_ratio = np.load(\"/tmp/pool_uniqratio.npy\")\nmaxfreq = np.load(\"/tmp/pool_maxfreq.npy\")\nvalid = (doclen >= MIN_TOK) & (doclen <= 20000) & (uniq_ratio >= 0.35) & (maxfreq <= 0.18)\nprint(f\"quality prefilter keeps {valid.sum()}/{ndoc} docs\")\n\n# ---- order ----\norder = np.argsort(-score)\norder = order[valid[order]]\nsel_ids = docids[order].tolist()\n# cover comfortably beyond 12M tokens (emit top docs summing to ~40M tokens)\ncum = np.cumsum(doclen[order])\nkeep = int(np.searchsorted(cum, 40_000_000)) + 1\nsel_ids = sel_ids[:max(keep, 60000)]\njson.dump(sel_ids, open(\"/workspace/submission/selection.json\", \"w\"))\nprint(f\"selection: {len(sel_ids)} ids, top-doc tokens sum {cum[min(keep,len(cum)-1)]:.0f}, {time.time()-t0:.1f}s\")\nprint(\"score stats: min %.3f med %.3f max %.3f\" % (score.min(), np.median(score), score.max()))\n", "userModified": false}