Download data/split_pipeline/04_index.py from Ericu950/Stoicheia-code: direct link, hf CLI and curl.
- Browser
- Download file 4.57 kB
-
https://huggingface.co/Ericu950/Stoicheia-code/resolve/main/data/split_pipeline/04_index.py
- Command line
-
hf download hf://Ericu950/Stoicheia-code/data/split_pipeline/04_index.py
-
curl -L -o 04_index.py https://huggingface.co/Ericu950/Stoicheia-code/resolve/main/data/split_pipeline/04_index.py
4.57 kB
| #!/usr/bin/env python3 | |
| """Stage 4: global contamination index. | |
| For every record that can ever be val/test (all pristine records in buckets | |
| 0-9 or PTEST/PVAL, plus Inscriptions_2 variants in PTEST/PVAL) emit hash keys: | |
| - exact skeleton hash of each sentence | |
| - bag hash (sorted words) of each sentence [catches reorderings] | |
| - every word-8-gram hash over the record's whole word stream | |
| (cross-sentence, so differing punctuation/splits can't hide a quote) | |
| each mapped to the record's zone bit. Keys are merged by bitwise OR. | |
| Output: work/index_keys.npy (sorted uint64), work/index_masks.npy (uint16) | |
| """ | |
| import glob | |
| import json | |
| import os | |
| import sys | |
| from concurrent.futures import ProcessPoolExecutor | |
| import numpy as np | |
| import pyarrow.parquet as pq | |
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) | |
| from common import h64, NGRAM, ZONE_TRAIN | |
| ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| PARTS = os.path.join(ROOT, "work", "index_parts") | |
| def load_literary_zones(): | |
| t = pq.read_table(os.path.join(ROOT, "work", "literary_zones.parquet"), | |
| columns=["rid", "zone"]) | |
| return dict(zip(t["rid"].to_pylist(), t["zone"].to_pylist())) | |
| def process_shard(args): | |
| tier, path, out_path = args | |
| zf = ZONES_CACHE.get("z") | |
| t = pq.read_table(path, columns=["rid", "zone", "skels"]) | |
| keys, masks = [], [] | |
| n_contrib = 0 | |
| for rid, _z1, skels in zip(t["rid"].to_pylist(), t["zone"].to_pylist(), | |
| t["skels"].to_pylist()): | |
| zone = zf.get(rid, -1) | |
| if zone < 0 or zone >= ZONE_TRAIN: | |
| continue # train-always records never appear in val/test | |
| bit = 1 << zone | |
| n_contrib += 1 | |
| words_all = [] | |
| for sk in skels: | |
| w = sk.split() | |
| keys.append(h64(sk)) | |
| masks.append(bit) | |
| keys.append(h64(" ".join(sorted(w)))) | |
| masks.append(bit) | |
| words_all.extend(w) | |
| if len(words_all) >= NGRAM: | |
| for i in range(len(words_all) - NGRAM + 1): | |
| keys.append(h64(" ".join(words_all[i:i + NGRAM]))) | |
| masks.append(bit) | |
| k = np.array(keys, dtype=np.uint64) | |
| m = np.array(masks, dtype=np.uint16) | |
| np.savez(out_path, k=k, m=m) | |
| return len(k), n_contrib | |
| ZONES_CACHE = {} | |
| def init_worker(zpath): | |
| import pyarrow.parquet as pq2 | |
| t = pq2.read_table(zpath, columns=["rid", "zone"]) | |
| ZONES_CACHE["z"] = dict(zip(t["rid"].to_pylist(), t["zone"].to_pylist())) | |
| def main(): | |
| os.makedirs(PARTS, exist_ok=True) | |
| zpath = os.path.join(ROOT, "work", "zones_final.parquet") | |
| tasks = [] | |
| for tier in ("pristine", "inscriptions"): | |
| for p in sorted(glob.glob(os.path.join(ROOT, "work", "sentences", | |
| tier, "shard_*.parquet"))): | |
| out = os.path.join(PARTS, "%s_%s.npz" % | |
| (tier, os.path.basename(p).split(".")[0])) | |
| tasks.append((tier, p, out)) | |
| print("%d shards" % len(tasks), flush=True) | |
| workers = max(4, os.cpu_count() - 8) | |
| total_keys = 0 | |
| total_contrib = 0 | |
| with ProcessPoolExecutor(max_workers=workers, initializer=init_worker, | |
| initargs=(zpath,)) as ex: | |
| for nk, nc in ex.map(process_shard, tasks, chunksize=1): | |
| total_keys += nk | |
| total_contrib += nc | |
| print("raw keys: %d from %d contributing records" % | |
| (total_keys, total_contrib), flush=True) | |
| # merge: concat -> sort -> OR-reduce duplicate keys | |
| parts = sorted(glob.glob(os.path.join(PARTS, "*.npz"))) | |
| ks, ms = [], [] | |
| for p in parts: | |
| z = np.load(p) | |
| ks.append(z["k"]) | |
| ms.append(z["m"]) | |
| k = np.concatenate(ks) | |
| m = np.concatenate(ms) | |
| del ks, ms | |
| order = np.argsort(k, kind="stable") | |
| k = k[order] | |
| m = m[order] | |
| del order | |
| boundary = np.empty(len(k), dtype=bool) | |
| boundary[0] = True | |
| np.not_equal(k[1:], k[:-1], out=boundary[1:]) | |
| starts = np.flatnonzero(boundary) | |
| uk = k[starts] | |
| um = np.bitwise_or.reduceat(m, starts) | |
| np.save(os.path.join(ROOT, "work", "index_keys.npy"), uk) | |
| np.save(os.path.join(ROOT, "work", "index_masks.npy"), um) | |
| stats = {"raw_keys": int(total_keys), "unique_keys": int(len(uk)), | |
| "contributing_records": int(total_contrib)} | |
| with open(os.path.join(ROOT, "work", "stage4_stats.json"), "w") as f: | |
| json.dump(stats, f, indent=2) | |
| print(json.dumps(stats, indent=2), flush=True) | |
| if __name__ == "__main__": | |
| main() | |