CELL 1 rewritten as an INCREMENTAL materializer: the fused set is ~160GB and the campaign needs ~9k rows — shards visited in sha256(name) order one at a time, gated+deduped+slimmed into the pool, source deleted per shard, hard stop at TRAIN_TARGET+EVAL_N (8192+512). Download bounded to ~10GB instead of the corpus. Split logic executed green against a real shard.
Browse files- colab/anima_closeout.ipynb +107 -93
colab/anima_closeout.ipynb
CHANGED
|
@@ -171,126 +171,138 @@
|
|
| 171 |
"execution_count": null,
|
| 172 |
"outputs": [],
|
| 173 |
"source": [
|
| 174 |
-
"# \u2550\u2550\u2550 CELL 1 \u2014 materialize the gated
|
| 175 |
-
"#
|
| 176 |
-
"#
|
| 177 |
-
"#
|
| 178 |
-
"#
|
| 179 |
-
"#
|
| 180 |
-
"#
|
| 181 |
-
"#
|
| 182 |
-
"#
|
| 183 |
-
"#
|
| 184 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
"import pyarrow as pa\n",
|
| 186 |
"import pyarrow.parquet as pq\n",
|
| 187 |
"from huggingface_hub import hf_hub_download\n",
|
| 188 |
"\n",
|
| 189 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
"TRAIN_DIR = DATA / \"train\"; TRAIN_DIR.mkdir(exist_ok=True)\n",
|
| 191 |
"EVAL_PARQUET = DATA / \"eval.parquet\"\n",
|
| 192 |
"MANIFEST = DATA / \"split_manifest.json\"\n",
|
|
|
|
| 193 |
"\n",
|
| 194 |
-
"def _passes_gate(
|
| 195 |
-
" vals = [str(
|
| 196 |
-
" if
|
| 197 |
" return (not vals) or any((\"approved\" in v.lower() or \"pass\" in v.lower())\n",
|
| 198 |
" for v in vals)\n",
|
| 199 |
"\n",
|
| 200 |
"if not MANIFEST.exists():\n",
|
| 201 |
-
"
|
| 202 |
-
"
|
| 203 |
-
"\n",
|
| 204 |
-
"
|
| 205 |
-
"
|
| 206 |
-
"
|
| 207 |
-
"
|
| 208 |
-
" seen_ids, ranked = set(), []\n",
|
| 209 |
" n_seen = n_gated = n_dup = 0\n",
|
| 210 |
-
"
|
| 211 |
-
"
|
| 212 |
-
"
|
| 213 |
-
"
|
| 214 |
-
"
|
| 215 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 216 |
" for j in range(batch.num_rows):\n",
|
|
|
|
|
|
|
| 217 |
" n_seen += 1\n",
|
| 218 |
-
" get = lambda c: (
|
| 219 |
-
" rid = str(get(\"id\"))\n",
|
| 220 |
" if not _passes_gate(get):\n",
|
| 221 |
" n_gated += 1\n",
|
| 222 |
-
"
|
|
|
|
|
|
|
| 223 |
" n_dup += 1\n",
|
| 224 |
-
"
|
| 225 |
-
"
|
| 226 |
-
"
|
| 227 |
-
"
|
| 228 |
-
"
|
| 229 |
-
"
|
| 230 |
-
"
|
| 231 |
-
"
|
| 232 |
-
"
|
| 233 |
-
"
|
| 234 |
-
"
|
| 235 |
-
"
|
| 236 |
-
"\n",
|
| 237 |
-
"
|
| 238 |
-
"
|
| 239 |
-
"
|
| 240 |
-
"
|
| 241 |
-
"
|
| 242 |
-
"
|
| 243 |
-
"
|
| 244 |
-
"
|
| 245 |
-
"
|
| 246 |
-
"
|
| 247 |
-
"
|
| 248 |
-
"
|
| 249 |
-
"
|
| 250 |
-
"
|
| 251 |
-
"
|
| 252 |
-
"
|
| 253 |
-
"
|
| 254 |
-
"
|
| 255 |
-
"
|
| 256 |
-
"
|
| 257 |
-
"
|
| 258 |
-
" tsub = tbl.filter(pa.array([k and not e for k, e\n",
|
| 259 |
-
" in zip(kmask, emask)]))\n",
|
| 260 |
-
" if esub.num_rows:\n",
|
| 261 |
-
" if ew is None:\n",
|
| 262 |
-
" ew = pq.ParquetWriter(EVAL_PARQUET, esub.schema)\n",
|
| 263 |
-
" ew.write_table(esub)\n",
|
| 264 |
-
" if tsub.num_rows:\n",
|
| 265 |
-
" if tw is None:\n",
|
| 266 |
-
" tw = pq.ParquetWriter(TRAIN_DIR / \"train.parquet\",\n",
|
| 267 |
-
" tsub.schema)\n",
|
| 268 |
-
" tw.write_table(tsub)\n",
|
| 269 |
-
" for w in (ew, tw):\n",
|
| 270 |
-
" if w:\n",
|
| 271 |
-
" w.close()\n",
|
| 272 |
" MANIFEST.write_text(json.dumps(\n",
|
| 273 |
-
" {\"dataset\": ANIMA[\"DATASET\"],
|
| 274 |
-
" \"
|
| 275 |
-
" \"
|
| 276 |
-
" \"
|
|
|
|
|
|
|
| 277 |
" \"caption_rule\": \"caption_joycaption or prompt_fused fallback \"\n",
|
| 278 |
" \"(dexp011:154; cond = caption_joycaption nl77)\",\n",
|
| 279 |
" \"gate_rule\": \"age_audit/audit must contain approved|pass \"\n",
|
| 280 |
" \"(dexp011:98-105)\",\n",
|
| 281 |
-
" \"split_rule\": \"sha256(id) ascending
|
|
|
|
| 282 |
" indent=1))\n",
|
| 283 |
-
" print(f\"split:
|
| 284 |
-
" f\"{len(
|
|
|
|
| 285 |
"else:\n",
|
| 286 |
" _m = json.loads(MANIFEST.read_text())\n",
|
| 287 |
-
" print(f\"split already materialized:
|
| 288 |
-
" f\"
|
| 289 |
"\n",
|
| 290 |
"# -- dataset TOMLs (fused recipe: derived caption column) -----------------\n",
|
| 291 |
-
"_wh = (\"image_width_column = 'image_width'\\n\"\n",
|
| 292 |
-
" \"image_height_column = 'image_height'\\n\") if ANIMA[\"HAS_WH\"] else \"\"\n",
|
| 293 |
-
"\n",
|
| 294 |
"def _dataset_toml(files):\n",
|
| 295 |
" return f\"\"\"# generated by anima_closeout.ipynb\n",
|
| 296 |
"resolutions = [512]\n",
|
|
@@ -305,7 +317,9 @@
|
|
| 305 |
"type = 'parquet'\n",
|
| 306 |
"parquet_files = '{files}'\n",
|
| 307 |
"image_column = 'image'\n",
|
| 308 |
-
"
|
|
|
|
|
|
|
| 309 |
"caption_type = 'text'\n",
|
| 310 |
"num_repeats = 1\n",
|
| 311 |
"skip_empty_caption = true\n",
|
|
|
|
| 171 |
"execution_count": null,
|
| 172 |
"outputs": [],
|
| 173 |
"source": [
|
| 174 |
+
"# \u2550\u2550\u2550 CELL 1 \u2014 materialize the gated split INCREMENTALLY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n",
|
| 175 |
+
"# The fused dataset is ~160GB across 200+ shards; the campaign needs ~9k\n",
|
| 176 |
+
"# rows. So: NEVER download the corpus. Shards are visited in sha256(name)\n",
|
| 177 |
+
"# order (deterministic, free of curation-order bias), one at a time; each\n",
|
| 178 |
+
"# is gated + deduped + slimmed into the pool, then its source file is\n",
|
| 179 |
+
"# DELETED; we stop the moment the row target is met. Disk stays bounded at\n",
|
| 180 |
+
"# roughly one source shard + the materialized output.\n",
|
| 181 |
+
"#\n",
|
| 182 |
+
"# Row rules, verbatim from the campaign record:\n",
|
| 183 |
+
"# AUDIT GATE (pod2/dexp011:98-105): age_audit/audit must contain\n",
|
| 184 |
+
"# \"approved\" or \"pass\" (case-insensitive); gated rows counted loudly.\n",
|
| 185 |
+
"# CAPTION = caption_joycaption or prompt_fused fallback (dexp011:154;\n",
|
| 186 |
+
"# cond = \"caption_joycaption nl77\"), materialized as `caption`.\n",
|
| 187 |
+
"# DEDUP by id (first occurrence wins).\n",
|
| 188 |
+
"# SPLIT: within the pool, sha256(id) ascending; first EVAL_N are eval.\n",
|
| 189 |
+
"import hashlib, json, os, shutil\n",
|
| 190 |
"import pyarrow as pa\n",
|
| 191 |
"import pyarrow.parquet as pq\n",
|
| 192 |
"from huggingface_hub import hf_hub_download\n",
|
| 193 |
"\n",
|
| 194 |
+
"TRAIN_TARGET = 8192 # ~2.7x exp004's criticized 2978; 10k steps @ mbs8\n",
|
| 195 |
+
"EVAL_N = 512 # = 80k samples ~= 9 epochs over this pool\n",
|
| 196 |
+
"KEEP_COLS = [\"id\", \"image\", \"image_width\", \"image_height\",\n",
|
| 197 |
+
" \"caption\", \"fused_json\", \"age_audit\"] # slim rows only\n",
|
| 198 |
+
"\n",
|
| 199 |
"TRAIN_DIR = DATA / \"train\"; TRAIN_DIR.mkdir(exist_ok=True)\n",
|
| 200 |
"EVAL_PARQUET = DATA / \"eval.parquet\"\n",
|
| 201 |
"MANIFEST = DATA / \"split_manifest.json\"\n",
|
| 202 |
+
"SRC = DATA / \"src\"; SRC.mkdir(exist_ok=True)\n",
|
| 203 |
"\n",
|
| 204 |
+
"def _passes_gate(val_of):\n",
|
| 205 |
+
" vals = [str(val_of(c)) for c in (\"age_audit\", \"audit\")\n",
|
| 206 |
+
" if val_of(c) is not None]\n",
|
| 207 |
" return (not vals) or any((\"approved\" in v.lower() or \"pass\" in v.lower())\n",
|
| 208 |
" for v in vals)\n",
|
| 209 |
"\n",
|
| 210 |
"if not MANIFEST.exists():\n",
|
| 211 |
+
" # deterministic pseudo-random shard order \u2014 never curation order\n",
|
| 212 |
+
" shard_order = sorted(\n",
|
| 213 |
+
" ANIMA[\"SHARDS\"],\n",
|
| 214 |
+
" key=lambda n: hashlib.sha256(n.encode()).hexdigest())\n",
|
| 215 |
+
"\n",
|
| 216 |
+
" pool = [] # slim row dicts\n",
|
| 217 |
+
" seen_ids = set()\n",
|
|
|
|
| 218 |
" n_seen = n_gated = n_dup = 0\n",
|
| 219 |
+
" shards_used = []\n",
|
| 220 |
+
" target = TRAIN_TARGET + EVAL_N\n",
|
| 221 |
+
"\n",
|
| 222 |
+
" for sh in shard_order:\n",
|
| 223 |
+
" if len(pool) >= target:\n",
|
| 224 |
+
" break\n",
|
| 225 |
+
" local = hf_hub_download(ANIMA[\"DATASET\"], sh, repo_type=\"dataset\",\n",
|
| 226 |
+
" local_dir=str(SRC))\n",
|
| 227 |
+
" pf = pq.ParquetFile(local)\n",
|
| 228 |
+
" names = pf.schema_arrow.names\n",
|
| 229 |
+
" cols = [c for c in (\"id\", \"image\", \"image_width\", \"image_height\",\n",
|
| 230 |
+
" \"caption_joycaption\", \"prompt_fused\",\n",
|
| 231 |
+
" \"fused_json\", \"age_audit\", \"audit\")\n",
|
| 232 |
+
" if c in names]\n",
|
| 233 |
+
" for batch in pf.iter_batches(batch_size=32, columns=cols):\n",
|
| 234 |
+
" if len(pool) >= target:\n",
|
| 235 |
+
" break\n",
|
| 236 |
+
" data = {c: batch.column(c) for c in\n",
|
| 237 |
+
" (set(cols) & set(batch.schema.names))}\n",
|
| 238 |
" for j in range(batch.num_rows):\n",
|
| 239 |
+
" if len(pool) >= target:\n",
|
| 240 |
+
" break\n",
|
| 241 |
" n_seen += 1\n",
|
| 242 |
+
" get = lambda c: (data[c][j].as_py() if c in data else None)\n",
|
|
|
|
| 243 |
" if not _passes_gate(get):\n",
|
| 244 |
" n_gated += 1\n",
|
| 245 |
+
" continue\n",
|
| 246 |
+
" rid = str(get(\"id\"))\n",
|
| 247 |
+
" if rid in seen_ids:\n",
|
| 248 |
" n_dup += 1\n",
|
| 249 |
+
" continue\n",
|
| 250 |
+
" cap = get(\"caption_joycaption\") or get(\"prompt_fused\") or \"\"\n",
|
| 251 |
+
" if not str(cap).strip():\n",
|
| 252 |
+
" n_gated += 1 # empty caption: unusable row\n",
|
| 253 |
+
" continue\n",
|
| 254 |
+
" seen_ids.add(rid)\n",
|
| 255 |
+
" pool.append({\n",
|
| 256 |
+
" \"id\": rid, \"image\": get(\"image\"),\n",
|
| 257 |
+
" \"image_width\": get(\"image_width\"),\n",
|
| 258 |
+
" \"image_height\": get(\"image_height\"),\n",
|
| 259 |
+
" \"caption\": str(cap),\n",
|
| 260 |
+
" \"fused_json\": str(get(\"fused_json\") or \"\"),\n",
|
| 261 |
+
" \"age_audit\": str(get(\"age_audit\") or \"\")})\n",
|
| 262 |
+
" shards_used.append(sh)\n",
|
| 263 |
+
" shutil.rmtree(SRC, ignore_errors=True) # bound the disk\n",
|
| 264 |
+
" SRC.mkdir(exist_ok=True)\n",
|
| 265 |
+
" print(f\"[split] {sh}: pool {len(pool)}/{target} \"\n",
|
| 266 |
+
" f\"(seen {n_seen}, gated {n_gated}, dup {n_dup})\", flush=True)\n",
|
| 267 |
+
"\n",
|
| 268 |
+
" assert len(pool) >= target, (\n",
|
| 269 |
+
" f\"exhausted ALL shards with only {len(pool)} usable rows \"\n",
|
| 270 |
+
" f\"(target {target}) \u2014 inspect the gate before training\")\n",
|
| 271 |
+
"\n",
|
| 272 |
+
" # split within the pool: sha256(id) ascending, first EVAL_N are eval\n",
|
| 273 |
+
" pool.sort(key=lambda r: hashlib.sha256(r[\"id\"].encode()).hexdigest())\n",
|
| 274 |
+
" eval_rows, train_rows = pool[:EVAL_N], pool[EVAL_N:target]\n",
|
| 275 |
+
"\n",
|
| 276 |
+
" def _write(rows, path):\n",
|
| 277 |
+
" tbl = pa.table({c: [r[c] for r in rows] for c in KEEP_COLS})\n",
|
| 278 |
+
" pq.write_table(tbl, path)\n",
|
| 279 |
+
" return tbl.num_rows\n",
|
| 280 |
+
"\n",
|
| 281 |
+
" ne = _write(eval_rows, EVAL_PARQUET)\n",
|
| 282 |
+
" nt = _write(train_rows, TRAIN_DIR / \"train.parquet\")\n",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 283 |
" MANIFEST.write_text(json.dumps(\n",
|
| 284 |
+
" {\"dataset\": ANIMA[\"DATASET\"],\n",
|
| 285 |
+
" \"shard_order\": \"sha256(shard_name) ascending\",\n",
|
| 286 |
+
" \"shards_used\": shards_used, \"rows_seen\": n_seen,\n",
|
| 287 |
+
" \"gated_or_empty\": n_gated, \"duplicate_ids\": n_dup,\n",
|
| 288 |
+
" \"train_rows\": nt, \"eval_n\": ne,\n",
|
| 289 |
+
" \"eval_ids\": [r[\"id\"] for r in eval_rows],\n",
|
| 290 |
" \"caption_rule\": \"caption_joycaption or prompt_fused fallback \"\n",
|
| 291 |
" \"(dexp011:154; cond = caption_joycaption nl77)\",\n",
|
| 292 |
" \"gate_rule\": \"age_audit/audit must contain approved|pass \"\n",
|
| 293 |
" \"(dexp011:98-105)\",\n",
|
| 294 |
+
" \"split_rule\": \"sha256(id) ascending within the pool; first \"\n",
|
| 295 |
+
" \"EVAL_N are eval\"},\n",
|
| 296 |
" indent=1))\n",
|
| 297 |
+
" print(f\"split DONE: {nt} train + {ne} eval from \"\n",
|
| 298 |
+
" f\"{len(shards_used)}/{len(ANIMA['SHARDS'])} shards \"\n",
|
| 299 |
+
" f\"(seen {n_seen}, gated/empty {n_gated}, dup {n_dup})\")\n",
|
| 300 |
"else:\n",
|
| 301 |
" _m = json.loads(MANIFEST.read_text())\n",
|
| 302 |
+
" print(f\"split already materialized: {_m['train_rows']} train + \"\n",
|
| 303 |
+
" f\"{_m['eval_n']} eval from {len(_m['shards_used'])} shards\")\n",
|
| 304 |
"\n",
|
| 305 |
"# -- dataset TOMLs (fused recipe: derived caption column) -----------------\n",
|
|
|
|
|
|
|
|
|
|
| 306 |
"def _dataset_toml(files):\n",
|
| 307 |
" return f\"\"\"# generated by anima_closeout.ipynb\n",
|
| 308 |
"resolutions = [512]\n",
|
|
|
|
| 317 |
"type = 'parquet'\n",
|
| 318 |
"parquet_files = '{files}'\n",
|
| 319 |
"image_column = 'image'\n",
|
| 320 |
+
"image_width_column = 'image_width'\n",
|
| 321 |
+
"image_height_column = 'image_height'\n",
|
| 322 |
+
"caption_column = 'caption'\n",
|
| 323 |
"caption_type = 'text'\n",
|
| 324 |
"num_repeats = 1\n",
|
| 325 |
"skip_empty_caption = true\n",
|