Plaiglab / scripts /build_mage_corpus.py
SanidhyaDhangar's picture
PlaigLab — Hugging Face Space (Docker) clean deploy
ebebfe8
Raw
History Blame Contribute Delete
3.61 kB
"""Pull a balanced scientific-domain human-vs-machine corpus from MAGE
(yaful/MAGE) via the HF datasets-server filter API — no full download.
MAGE: text / label (1=human, 0=machine) / src (domain+model). The 'sci_gen_*'
rows are scientific/academic text; the model name lives in src, giving us
leave-one-MODEL-out groups for honest validation. Length-filtered so every
doc has enough sentences for the burstiness/diversity features.
Output: data/calibration/mage_sci.jsonl ({text, y, src_model})
Run: python scripts/build_mage_corpus.py [per_model] (default 120)
"""
import json, os, sys, time, urllib.parse, urllib.request
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
OUT = os.path.join(ROOT, "data", "calibration", "mage_sci.jsonl")
DS, CONFIG = "yaful/MAGE", "default"
MIN_CHARS = 800 # need a real document, not a snippet
PER_MODEL = int(sys.argv[1]) if len(sys.argv) > 1 else 120
def _get(url, tries=4):
for t in range(tries):
try:
return json.load(urllib.request.urlopen(url, timeout=40))
except Exception as e:
if t == tries - 1:
raise
time.sleep(2 * (t + 1))
def filt(where, split, offset, length=100):
u = (f"https://datasets-server.huggingface.co/filter?dataset={urllib.parse.quote(DS)}"
f"&config={CONFIG}&split={split}&where={urllib.parse.quote(where)}"
f"&offset={offset}&length={length}")
return _get(u)
def collect(where, split, cap, by_model):
"""Page through a filtered view, bucket by src, length-filter, cap/model."""
offset, total = 0, None
while True:
d = filt(where, split, offset)
total = d.get("num_rows_total", 0)
rows = d.get("rows", [])
if not rows:
break
for r in rows:
row = r["row"]
txt, src = row.get("text", ""), row.get("src", "")
if len(txt) < MIN_CHARS:
continue
bucket = by_model.setdefault(src, [])
if len(bucket) < cap:
bucket.append(txt)
offset += len(rows)
got = sum(min(len(v), cap) for v in by_model.values())
print(f" {split} offset={offset}/{total} collected={got}", end="\r")
if offset >= total or offset > 40000:
break
print()
def main():
os.makedirs(os.path.dirname(OUT), exist_ok=True)
human, machine = {}, {}
for split in ("test", "validation", "train"):
print(f"[human] {split}")
collect("\"src\"='sci_gen_human'", split, PER_MODEL * 6, human)
print(f"[machine] {split}")
collect("\"src\" LIKE 'sci_gen_machine%'", split, PER_MODEL, machine)
h = sum(len(v) for v in human.values())
m = sum(len(v) for v in machine.values())
if h >= PER_MODEL * 6 and len(machine) >= 3:
break
rows = []
for txts in human.values():
for t in txts[:PER_MODEL * 6]:
rows.append({"text": t, "y": 1, "src_model": "human"})
for src, txts in machine.items():
model = src.replace("sci_gen_machine_continuation_", "").replace(
"sci_gen_machine_", "")
for t in txts:
rows.append({"text": t, "y": 0, "src_model": "ai_" + model})
with open(OUT, "w", encoding="utf-8") as f:
for r in rows:
f.write(json.dumps(r) + "\n")
from collections import Counter
c = Counter(r["src_model"] for r in rows)
print(f"\nwrote {len(rows)} docs -> {OUT}")
for k, v in sorted(c.items()):
print(f" {k:28s} {v}")
if __name__ == "__main__":
main()