Cion-lab commited on
Commit
a3bf1dd
·
verified ·
1 Parent(s): 484f967

Upload build/build_mix.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. build/build_mix.py +58 -3
build/build_mix.py CHANGED
@@ -375,23 +375,59 @@ def stage(args, st, tokenizer):
375
 
376
  # --------------------------------------------------------------------- merge
377
  def merge(args, st, tokenizer):
378
- """Interleave staged documents proportionally to each source's staged token total."""
 
 
 
 
 
 
379
  vocab = tokenizer.get_vocab_size()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  streams = []
381
  for src in SOURCES:
382
  key = src_key(src)
383
  rec = st.d["staged"].get(key)
384
  if not rec or not rec.get("shards"):
385
  continue
 
 
 
 
 
 
386
  weight = rec.get("tokens") or sum(s["tokens"] for s in rec["shards"])
 
387
  if weight <= 0:
 
388
  continue
389
  iters = [iter_shard_docs(os.path.join(st.root, s["file"])) for s in rec["shards"]]
390
 
391
- def gen(its=iters):
 
 
 
 
 
 
392
  for it in its:
393
  for d in it:
394
- yield d
 
 
395
 
396
  streams.append({"key": key, "weight": float(weight), "credit": 0.0, "it": gen(iters),
397
  "pending": None, "emitted_tokens": 0, "exhausted": False})
@@ -520,6 +556,9 @@ def main():
520
  "empty string disables sync")
521
  ap.add_argument("--stage-only", action="store_true")
522
  ap.add_argument("--merge-only", action="store_true")
 
 
 
523
  args = ap.parse_args()
524
 
525
  if args.only:
@@ -608,6 +647,22 @@ def main():
608
  default=0),
609
  "seconds": round(time.monotonic() - t0, 1),
610
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
611
  with open(os.path.join(args.root, "manifest.json"), "w") as f:
612
  json.dump(manifest, f, indent=1)
613
  # Nothing may be printed between the JSON and its END marker: the caller parses this span with
 
375
 
376
  # --------------------------------------------------------------------- merge
377
  def merge(args, st, tokenizer):
378
+ """Interleave staged documents proportionally to each source's staged token total.
379
+
380
+ --exclude-dir applies per-source uint8 masks written by audit_contamination --write-filter: a 1 byte
381
+ means "this document's 13-grams appear in a benchmark train/validation/dev split, do not emit it"
382
+ (§3.3). Masks are indexed by document ordinal within the source, which is stable because both the
383
+ audit and the merge replay the source's own record.json shard order.
384
+ """
385
  vocab = tokenizer.get_vocab_size()
386
+ masks, dropped = {}, {}
387
+ if args.exclude_dir:
388
+ fj = os.path.join(args.exclude_dir, "filter.json")
389
+ if not os.path.exists(fj):
390
+ print(f"FATAL: --exclude-dir {args.exclude_dir} contains no filter.json", flush=True)
391
+ sys.exit(5)
392
+ dropped = json.load(open(fj)).get("sources", {})
393
+ for key in sorted(os.listdir(args.exclude_dir)):
394
+ if key.endswith(".u8"):
395
+ with open(os.path.join(args.exclude_dir, key), "rb") as fh:
396
+ masks[key[:-3]] = fh.read()
397
+ print(f"exclude: {len(masks)} mask(s), "
398
+ f"{sum(len(m) for m in masks.values()):,} docs covered, "
399
+ f"{sum(int(v.get('dropped_docs', 0)) for v in dropped.values()):,} flagged", flush=True)
400
  streams = []
401
  for src in SOURCES:
402
  key = src_key(src)
403
  rec = st.d["staged"].get(key)
404
  if not rec or not rec.get("shards"):
405
  continue
406
+ staged_docs = sum(int(s.get("docs", 0)) for s in rec["shards"])
407
+ m = masks.get(key)
408
+ if m is not None and len(m) != staged_docs:
409
+ # A short mask would silently keep exactly the documents the audit did not reach.
410
+ print(f"FATAL: mask for {key} covers {len(m)} docs but {staged_docs} are staged", flush=True)
411
+ sys.exit(5)
412
  weight = rec.get("tokens") or sum(s["tokens"] for s in rec["shards"])
413
+ weight = weight - int((dropped.get(key) or {}).get("dropped_tokens", 0))
414
  if weight <= 0:
415
+ print(f" {key}: fully excluded, dropped from the merge", flush=True)
416
  continue
417
  iters = [iter_shard_docs(os.path.join(st.root, s["file"])) for s in rec["shards"]]
418
 
419
+ def gen(its=iters, m=m):
420
+ if m is None:
421
+ for it in its:
422
+ for d in it:
423
+ yield d
424
+ return
425
+ i = 0
426
  for it in its:
427
  for d in it:
428
+ if not m[i]:
429
+ yield d
430
+ i += 1
431
 
432
  streams.append({"key": key, "weight": float(weight), "credit": 0.0, "it": gen(iters),
433
  "pending": None, "emitted_tokens": 0, "exhausted": False})
 
556
  "empty string disables sync")
557
  ap.add_argument("--stage-only", action="store_true")
558
  ap.add_argument("--merge-only", action="store_true")
559
+ ap.add_argument("--exclude-dir", default="",
560
+ help="directory of per-source <key>.u8 contamination masks + filter.json written by "
561
+ "audit_contamination --write-filter; documents flagged there are not merged (§3.3)")
562
  args = ap.parse_args()
563
 
564
  if args.only:
 
647
  default=0),
648
  "seconds": round(time.monotonic() - t0, 1),
649
  }
650
+ if args.exclude_dir:
651
+ # Provenance: a filtered mix is only reproducible if the exclusion set travels with it, so the
652
+ # counts go in the manifest and the mask bytes go in mixroot/filter/ (publish_mix ships them).
653
+ try:
654
+ fj = json.load(open(os.path.join(args.exclude_dir, "filter.json")))
655
+ manifest["contamination_filter"] = {
656
+ "criterion": fj.get("criterion"), "k": fj.get("k"),
657
+ "docs_dropped": sum(int(v.get("dropped_docs", 0)) for v in fj["sources"].values()),
658
+ "tokens_dropped": sum(int(v.get("dropped_tokens", 0) or 0)
659
+ for v in fj["sources"].values()),
660
+ "per_source": {k2: {"docs": v2.get("dropped_docs"),
661
+ "tokens": v2.get("dropped_tokens")}
662
+ for k2, v2 in fj["sources"].items()}}
663
+ except Exception as e:
664
+ print(f"FATAL: --exclude-dir set but filter.json unreadable: {e}", flush=True)
665
+ sys.exit(5)
666
  with open(os.path.join(args.root, "manifest.json"), "w") as f:
667
  json.dump(manifest, f, indent=1)
668
  # Nothing may be printed between the JSON and its END marker: the caller parses this span with