| """Memory-lean data prep using polars directly on the hub parquet shards.""" |
| import glob |
| import os |
| import sys |
|
|
| import numpy as np |
| import polars as pl |
|
|
| N_TRAIN_Q = 80_000 |
| N_DEV_Q = 2_000 |
| N_NEGS = 4 |
| SEED = 42 |
|
|
| SNAP = glob.glob(os.path.expanduser( |
| "~/.cache/huggingface/hub/datasets--sentence-transformers--msmarco/snapshots/*"))[0] |
|
|
| print("scanning margin-mse shards...", flush=True) |
| df = ( |
| pl.scan_parquet(f"{SNAP}/bert-ensemble-margin-mse/*.parquet") |
| .select( |
| pl.col("query_id").cast(pl.Int64), |
| pl.col("positive_id").cast(pl.Int64), |
| pl.col("negative_id").cast(pl.Int64), |
| pl.col("score").cast(pl.Float32), |
| ) |
| .collect() |
| ) |
| print("rows:", df.height, flush=True) |
|
|
| |
| first_pos = df.group_by("query_id").agg(pl.col("positive_id").first().alias("pos")) |
| g = ( |
| df.join(first_pos, on="query_id") |
| .filter((pl.col("positive_id") == pl.col("pos")) & (pl.col("negative_id") != pl.col("pos"))) |
| .unique(subset=["query_id", "negative_id"], keep="first", maintain_order=True) |
| .group_by("query_id") |
| .agg( |
| pl.col("pos").first(), |
| pl.col("negative_id").head(N_NEGS).alias("negs"), |
| pl.col("score").head(N_NEGS).alias("margins"), |
| ) |
| .filter(pl.col("negs").list.len() >= N_NEGS) |
| ) |
| print("eligible queries:", g.height, flush=True) |
|
|
| g = g.sample(n=min(N_TRAIN_Q + N_DEV_Q, g.height), seed=SEED, shuffle=True) |
| print("sampled:", g.height, flush=True) |
|
|
| qids = g["query_id"].to_numpy() |
| pos = g["pos"].to_numpy() |
| negs = np.stack(g["negs"].to_numpy()) |
| margins = np.stack(g["margins"].to_numpy()).astype(np.float32) |
|
|
| need_docs = np.unique(np.concatenate([pos, negs.reshape(-1)])) |
| print("unique docs:", len(need_docs), "queries:", len(qids), flush=True) |
|
|
| print("loading corpus/queries parquet...", flush=True) |
| corpus_files = glob.glob(f"{SNAP}/corpus/*.parquet") |
| queries_files = glob.glob(f"{SNAP}/queries/*.parquet") |
| assert corpus_files and queries_files, "run: hf download for corpus/queries first" |
|
|
| corpus = ( |
| pl.scan_parquet(corpus_files) |
| .select(pl.col("passage_id").cast(pl.Int64).alias("id"), pl.col("passage").alias("text")) |
| .filter(pl.col("id").is_in(need_docs)) |
| .collect() |
| ) |
| queries = ( |
| pl.scan_parquet(queries_files) |
| .select(pl.col("query_id").cast(pl.Int64).alias("id"), pl.col("query").alias("text")) |
| .filter(pl.col("id").is_in(qids)) |
| .collect() |
| ) |
| print("resolved docs:", corpus.height, "queries:", queries.height, flush=True) |
| assert corpus.height == len(need_docs), "missing corpus texts" |
| assert queries.height == len(qids), "missing query texts" |
|
|
| qtext = dict(zip(queries["id"].to_list(), queries["text"].to_list())) |
| dtext = dict(zip(corpus["id"].to_list(), corpus["text"].to_list())) |
|
|
| clean = lambda s: " ".join(s.split()) |
| id2idx = {} |
| with open("/home/anon/pog/data/manifest.tsv", "w") as f: |
| for q in qids: |
| id2idx[f"q{q}"] = len(id2idx) |
| f.write(f"q{q}\tq\t{clean(qtext[q])}\n") |
| for d in need_docs: |
| id2idx[f"d{d}"] = len(id2idx) |
| f.write(f"d{d}\td\t{clean(dtext[d])}\n") |
| print("manifest rows:", len(id2idx), flush=True) |
|
|
| q_idx = np.array([id2idx[f"q{q}"] for q in qids], dtype=np.int64) |
| p_idx = np.array([id2idx[f"d{d}"] for d in pos], dtype=np.int64) |
| n_idx = np.array([[id2idx[f"d{d}"] for d in row] for row in negs], dtype=np.int64) |
|
|
| tr = slice(0, N_TRAIN_Q) |
| dv = slice(N_TRAIN_Q, None) |
| np.savez("/home/anon/pog/data/train.npz", q=q_idx[tr], pos=p_idx[tr], neg=n_idx[tr], margin=margins[tr]) |
| np.savez("/home/anon/pog/data/dev.npz", q=q_idx[dv], pos=p_idx[dv], neg=n_idx[dv], margin=margins[dv]) |
| print(f"DONE train={N_TRAIN_Q} dev={len(qids)-N_TRAIN_Q}", flush=True) |
|
|