Upload build/build_mix.py with huggingface_hub
Browse files- build/build_mix.py +48 -2
build/build_mix.py
CHANGED
|
@@ -392,13 +392,20 @@ def merge(args, st, tokenizer):
|
|
| 392 |
audit and the merge replay the source's own record.json shard order.
|
| 393 |
"""
|
| 394 |
vocab = tokenizer.get_vocab_size()
|
| 395 |
-
masks, dropped = {}, {}
|
| 396 |
if args.exclude_dir:
|
| 397 |
fj = os.path.join(args.exclude_dir, "filter.json")
|
| 398 |
if not os.path.exists(fj):
|
| 399 |
print(f"FATAL: --exclude-dir {args.exclude_dir} contains no filter.json", flush=True)
|
| 400 |
sys.exit(5)
|
| 401 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 402 |
for key in sorted(os.listdir(args.exclude_dir)):
|
| 403 |
if key.endswith(".u8"):
|
| 404 |
with open(os.path.join(args.exclude_dir, key), "rb") as fh:
|
|
@@ -446,6 +453,42 @@ def merge(args, st, tokenizer):
|
|
| 446 |
# silent success in this pipeline, so it is a failure and not a return.
|
| 447 |
print("FATAL merge: nothing staged. Refusing to emit an empty mix.", flush=True)
|
| 448 |
sys.exit(4)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 449 |
total_w = sum(s["weight"] for s in streams)
|
| 450 |
print(f"merge: {len(streams)} sources, "
|
| 451 |
f"{sum(s['weight'] for s in streams) / 1e6:,.0f}M staged tokens", flush=True)
|
|
@@ -568,6 +611,9 @@ def main():
|
|
| 568 |
ap.add_argument("--exclude-dir", default="",
|
| 569 |
help="directory of per-source <key>.u8 contamination masks + filter.json written by "
|
| 570 |
"audit_contamination --write-filter; documents flagged there are not merged (§3.3)")
|
|
|
|
|
|
|
|
|
|
| 571 |
args = ap.parse_args()
|
| 572 |
|
| 573 |
if args.only:
|
|
|
|
| 392 |
audit and the merge replay the source's own record.json shard order.
|
| 393 |
"""
|
| 394 |
vocab = tokenizer.get_vocab_size()
|
| 395 |
+
masks, dropped, filter_digest = {}, {}, None
|
| 396 |
if args.exclude_dir:
|
| 397 |
fj = os.path.join(args.exclude_dir, "filter.json")
|
| 398 |
if not os.path.exists(fj):
|
| 399 |
print(f"FATAL: --exclude-dir {args.exclude_dir} contains no filter.json", flush=True)
|
| 400 |
sys.exit(5)
|
| 401 |
+
filter_digest = hashlib.sha256(open(fj, "rb").read()).hexdigest()
|
| 402 |
+
fj_obj = json.load(open(fj))
|
| 403 |
+
if not fj_obj.get("scanned_sources"):
|
| 404 |
+
print("FATAL: filter.json records no scanned_sources -- the audit that wrote it predates "
|
| 405 |
+
"the completeness field, so an unscanned source could be silently unfiltered. Re-run "
|
| 406 |
+
"audit_contamination --write-filter.", flush=True)
|
| 407 |
+
sys.exit(5)
|
| 408 |
+
dropped = fj_obj.get("sources", {})
|
| 409 |
for key in sorted(os.listdir(args.exclude_dir)):
|
| 410 |
if key.endswith(".u8"):
|
| 411 |
with open(os.path.join(args.exclude_dir, key), "rb") as fh:
|
|
|
|
| 453 |
# silent success in this pipeline, so it is a failure and not a return.
|
| 454 |
print("FATAL merge: nothing staged. Refusing to emit an empty mix.", flush=True)
|
| 455 |
sys.exit(4)
|
| 456 |
+
if args.exclude_dir:
|
| 457 |
+
# A missing mask must never mean "this source is clean" (review finding): it means the audit
|
| 458 |
+
# never scanned it, and the default behaviour would have been to merge every contaminated doc.
|
| 459 |
+
unscanned = [s["key"] for s in streams if s["key"] not in masks]
|
| 460 |
+
if unscanned:
|
| 461 |
+
print(f"FATAL merge: --exclude-dir has no mask for {unscanned}. Refusing to merge sources "
|
| 462 |
+
"the contamination audit did not read.", flush=True)
|
| 463 |
+
sys.exit(5)
|
| 464 |
+
|
| 465 |
+
# A re-merge is only correct if it starts from nothing. The loop below stops when merged_tokens()
|
| 466 |
+
# reaches the input total, so on a mixroot that already holds 141 shards it would emit ZERO new
|
| 467 |
+
# shards, exit 0, and let the manifest claim a filter that never happened (found in review while
|
| 468 |
+
# Gate 2c was running against exactly this path). Resume-after-a-kill stays legal -- it is the same
|
| 469 |
+
# inputs -- so the test is on a digest of the merge's inputs, not on whether shards exist.
|
| 470 |
+
digest = hashlib.sha256(json.dumps({
|
| 471 |
+
"streams": [[s["key"], round(s["weight"]) ] for s in streams], "seed": SHUFFLE_SEED,
|
| 472 |
+
"tokens_per_shard": TOKENS_PER_SHARD, "val_stride": VAL_STRIDE,
|
| 473 |
+
"filter": filter_digest}, sort_keys=True).encode()).hexdigest()
|
| 474 |
+
prior = st.d.get("merge_inputs")
|
| 475 |
+
if prior is not None and prior != digest and st.d["merged_shards"]:
|
| 476 |
+
if not (args.rebuild_merged or args.exclude_dir):
|
| 477 |
+
print("FATAL merge: the mix root already holds shards merged from DIFFERENT inputs "
|
| 478 |
+
f"(recorded {prior[:12]}, now {digest[:12]}). Continuing would be a silent no-op. "
|
| 479 |
+
"Pass --rebuild-merged to discard them deliberately.", flush=True)
|
| 480 |
+
sys.exit(6)
|
| 481 |
+
n_old = len(st.d["merged_shards"]) + len(st.d.get("val_shards", []))
|
| 482 |
+
for grp in ("merged_shards", "val_shards"):
|
| 483 |
+
for rec in st.d.get(grp, []):
|
| 484 |
+
p = os.path.join(st.root, rec["file"])
|
| 485 |
+
if os.path.exists(p):
|
| 486 |
+
os.remove(p)
|
| 487 |
+
st.d["merged_shards"] = []
|
| 488 |
+
st.d["val_shards"] = []
|
| 489 |
+
print(f"merge: inputs changed ({prior[:12]} -> {digest[:12]}); discarded {n_old} old shard(s) "
|
| 490 |
+
"and rebuilding from the staged sources", flush=True)
|
| 491 |
+
st.d["merge_inputs"] = digest
|
| 492 |
total_w = sum(s["weight"] for s in streams)
|
| 493 |
print(f"merge: {len(streams)} sources, "
|
| 494 |
f"{sum(s['weight'] for s in streams) / 1e6:,.0f}M staged tokens", flush=True)
|
|
|
|
| 611 |
ap.add_argument("--exclude-dir", default="",
|
| 612 |
help="directory of per-source <key>.u8 contamination masks + filter.json written by "
|
| 613 |
"audit_contamination --write-filter; documents flagged there are not merged (§3.3)")
|
| 614 |
+
ap.add_argument("--rebuild-merged", action="store_true",
|
| 615 |
+
help="discard shards already recorded in build_state.json before merging. Implied by "
|
| 616 |
+
"--exclude-dir, because a filter changes every shard's contents")
|
| 617 |
args = ap.parse_args()
|
| 618 |
|
| 619 |
if args.only:
|