File size: 7,264 Bytes
6a8043b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | #!/usr/bin/env python3
"""Build the SN3 *true-objective* eval mix.
SN3 duel score is mu_hat = sum_i w_i * mu_i over 10 fixed corpora with FIXED
sampling weights (from upstream unarbos/teutonic npy_sources.py, reproduced
exactly by live dashboard n_sequences/25000). Corpora are PUBLIC pretokenized
uint32 .npy shards on Hippius, tokenizer silx-ai/Quasar-10B, seq_len 2048, so
the real eval distribution can be fetched directly via HTTP range reads.
"""
from __future__ import annotations
import hashlib
import json
import pathlib
import sys
import time
import urllib.error
import urllib.request
import numpy as np
OUT = pathlib.Path("/var/lib/mining/work/trainmix")
SEQ_LEN = 2048
ITEM = 4
VOCAB_SIZE = 248320
SEQS_PER_CORPUS = 6000
CHUNKS = 8
SEED = 20260727
SOURCES = {
"finewebedu": ("https://eu-central-1.hippius.com/teutonic-sn3/dataset/finewebedu/manifest.json", 0.27),
"automathtext-v2-quasar-10b": ("https://eu-central-1.hippius.com/teutonic-sn3/dataset/automathtext-v2-quasar-10b/manifest.json", 0.22),
"openthoughts3-1.2m-quasar-10b": ("https://eu-central-1.hippius.com/teutonic-sn3/dataset/openthoughts3-1.2m-quasar-10b/manifest.json", 0.10),
"ultradata-math-l3-quasar-10b": ("https://us-east-1.hippius.com/teutonic-sn3/dataset/ultradata-math-l3-quasar-10b/manifest.json", 0.09),
"pes2o-v3": ("https://s3.hippius.com/teutonic-sn3/dataset/pes2o-v3/manifest.json", 0.08),
"openmathreasoning-quasar-10b": ("https://eu-central-1.hippius.com/teutonic-sn3/dataset/openmathreasoning-quasar-10b/manifest.json", 0.06),
"nemotron-cc-math-v1-4plus-mind-quasar-10b": ("https://eu-central-1.hippius.com/teutonic-sn3/dataset/nemotron-cc-math-v1-4plus-mind-quasar-10b/manifest.json", 0.05),
"cosmopedia-wikihow-stories-quasar-10b": ("https://eu-central-1.hippius.com/teutonic-sn3/dataset/cosmopedia-wikihow-stories-quasar-10b/manifest.json", 0.05),
"nemotron-specialized-v1.2-quasar-10b": ("https://eu-central-1.hippius.com/teutonic-sn3/dataset/nemotron-specialized-v1.2-quasar-10b/manifest.json", 0.04),
"dendrite-synth-run": ("https://us-east-1.hippius.com/dendrite-teutonic/dataset/dendrite-synth-run/manifest.json", 0.04),
}
def log(msg):
print("[%s] %s" % (time.strftime("%H:%M:%S"), msg), flush=True)
def fetch(url, rng=None, tries=4):
last = None
for attempt in range(tries):
req = urllib.request.Request(url)
if rng is not None:
req.add_header("Range", "bytes=%d-%d" % rng)
try:
with urllib.request.urlopen(req, timeout=180) as resp:
return resp.read()
except Exception as exc:
last = exc
time.sleep(2 * (attempt + 1))
raise RuntimeError("fetch failed %s rng=%s: %s" % (url, rng, last))
def bucket_root(manifest_url, corpus):
tail = "dataset/%s/manifest.json" % corpus
if not manifest_url.endswith(tail):
return manifest_url[: manifest_url.rfind("dataset/")]
return manifest_url[: -len(tail)]
def npy_header_len(head):
if head[:6] != b"\x93NUMPY":
raise RuntimeError("not a .npy stream")
if head[6] == 1:
return 10 + int.from_bytes(head[8:10], "little")
return 12 + int.from_bytes(head[8:12], "little")
def build_corpus(corpus, manifest_url, weight, rs):
log("--- %s (w=%s)" % (corpus, weight))
man = json.loads(fetch(manifest_url))
shards = [s for s in (man.get("shards") or man.get("files") or []) if str(s.get("key", "")).endswith(".npy")]
if not shards:
raise RuntimeError("%s: manifest has no .npy shards" % corpus)
root = bucket_root(manifest_url, corpus)
per_chunk = SEQS_PER_CORPUS // CHUNKS
pick = min(CHUNKS, len(shards))
idxs = rs.choice(len(shards), size=pick, replace=False)
plan = [(int(idxs[j % pick]), per_chunk) for j in range(CHUNKS)]
blocks, prov = [], []
for shard_i, want in plan:
entry = shards[shard_i]
key = entry["key"]
url = root + key
head = fetch(url, (0, 255))
hlen = npy_header_len(head)
size_bytes = int(entry.get("size_bytes") or 0) or (hlen + int(entry.get("n_tokens", 0)) * ITEM)
total_seqs = (size_bytes - hlen) // (SEQ_LEN * ITEM)
if total_seqs < want:
want = total_seqs
if want <= 0:
continue
start_seq = int(rs.integers(0, max(0, total_seqs - want) + 1))
b0 = hlen + start_seq * SEQ_LEN * ITEM
b1 = b0 + want * SEQ_LEN * ITEM - 1
raw = fetch(url, (b0, b1))
if len(raw) != (b1 - b0 + 1):
raise RuntimeError("%s: short read %d != %d on %s" % (corpus, len(raw), b1 - b0 + 1, key))
blocks.append(np.frombuffer(raw, dtype="<u4").reshape(want, SEQ_LEN))
prov.append({
"shard_key": key, "url": url, "byte_start": b0, "byte_end": b1,
"start_seq_in_shard": start_seq, "n_seqs": int(want),
"slice_sha256": hashlib.sha256(raw).hexdigest(),
"shard_sha256_manifest": entry.get("sha256"),
})
log(" %s seqs[%d:%d] %.1fMB" % (key, start_seq, start_seq + want, len(raw) / 1e6))
tok = np.concatenate(blocks, axis=0)
n_raw = int(tok.shape[0])
keep = ~(tok >= VOCAB_SIZE).any(axis=1)
n_drop = int(n_raw - keep.sum())
tok = np.ascontiguousarray(tok[keep])
out = OUT / ("%s.npy" % corpus)
np.save(out, tok)
log(" -> %s shape=%s dropped_oob=%d" % (out.name, tok.shape, n_drop))
return {
"corpus": corpus, "weight": weight, "manifest_url": manifest_url,
"manifest_tokenizer": man.get("tokenizer"), "manifest_dtype": man.get("dtype"),
"manifest_total_tokens": man.get("total_tokens"),
"manifest_total_shards": man.get("total_shards") or len(shards),
"n_seqs": int(tok.shape[0]), "n_seqs_dropped_oob": n_drop, "seq_len": SEQ_LEN,
"file": out.name, "file_sha256": hashlib.sha256(out.read_bytes()).hexdigest(),
"chunks": prov,
}
def main():
OUT.mkdir(parents=True, exist_ok=True)
rs = np.random.default_rng(SEED)
entries, failed = [], {}
for corpus, (url, weight) in SOURCES.items():
try:
entries.append(build_corpus(corpus, url, weight, rs))
except Exception as exc:
log(" !! FAILED %s: %s" % (corpus, exc))
failed[corpus] = str(exc)
index = {
"name": "sn3-trainmix-v1",
"purpose": "TRAINING shards for full 10-corpus SN3 objective; disjoint-by-seed from truemix-v1 eval set",
"built_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"seq_len": SEQ_LEN, "vocab_guard": VOCAB_SIZE, "seed": SEED,
"weights_source": "upstream unarbos/teutonic npy_sources.py DEFAULT_SOURCE_WEIGHT_MAP",
"weights_verified_against": "live dashboard eval-0330 n_sequences/25000 exact match",
"protocol_delta": 0.0015, "tokenizer": "silx-ai/Quasar-10B",
"corpora": entries, "failed": failed,
"weight_total": round(sum(e["weight"] for e in entries), 6),
}
(OUT / "index.json").write_text(json.dumps(index, indent=2))
log("index.json: %d ok, %d failed, weight_total=%s" % (len(entries), len(failed), index["weight_total"]))
return 0 if not failed else 1
if __name__ == "__main__":
sys.exit(main())
|