| """HC3 loader (human vs ChatGPT). Loaded via the HF datasets-server parquet API because the HF |
| repo ships a loading script that modern `datasets` (>=3) refuses to run.""" |
| from __future__ import annotations |
| import io |
| import random |
| import requests |
|
|
|
|
| def _hf_parquet_frames(dataset: str, config: str | None = None, split: str | None = None): |
| import pandas as pd |
| api = f"https://datasets-server.huggingface.co/parquet?dataset={dataset}" |
| meta = requests.get(api, timeout=60).json() |
| files = meta.get("parquet_files", []) |
| if not files: |
| raise RuntimeError(f"No parquet files for {dataset}: {meta}") |
| sel = [f for f in files |
| if (config is None or f["config"] == config) |
| and (split is None or f["split"] == split)] |
| if not sel: |
| raise RuntimeError(f"No parquet for {dataset} config={config} split={split}; " |
| f"configs={sorted({f['config'] for f in files})}") |
| frames = [pd.read_parquet(io.BytesIO(requests.get(f["url"], timeout=180).content)) for f in sel] |
| return pd.concat(frames, ignore_index=True) |
|
|
|
|
| def _collect(n_needed: int, min_words: int, seed: int): |
| """Collect (and deterministically shuffle) at least n_needed human and AI texts.""" |
| df = _hf_parquet_frames("Hello-SimpleAI/HC3", config="all", split="train") |
| human, ai = [], [] |
| for _, row in df.iterrows(): |
| ha = row.get("human_answers"); ca = row.get("chatgpt_answers") |
| for h in (ha if ha is not None else []): |
| if isinstance(h, str) and len(h.split()) >= min_words: |
| human.append(h.strip()) |
| for a in (ca if ca is not None else []): |
| if isinstance(a, str) and len(a.split()) >= min_words: |
| ai.append(a.strip()) |
| if len(human) >= n_needed and len(ai) >= n_needed: |
| break |
| random.Random(seed).shuffle(human) |
| random.Random(seed + 1).shuffle(ai) |
| return human, ai |
|
|
|
|
| def load_hc3(n_per_class: int = 500, min_words: int = 30, seed: int = 42): |
| """Return (human_texts, ai_texts), each truncated to n_per_class.""" |
| human, ai = _collect(n_per_class, min_words, seed) |
| return human[:n_per_class], ai[:n_per_class] |
|
|
|
|
| def load_hc3_split(n_train: int = 1500, n_test: int = 300, min_words: int = 30, seed: int = 42): |
| """Disjoint train/test split: returns (train_human, train_ai), (test_human, test_ai).""" |
| need = n_train + n_test |
| human, ai = _collect(need, min_words, seed) |
| tr = (human[:n_train], ai[:n_train]) |
| te = (human[n_train:need], ai[n_train:need]) |
| return tr, te |
|
|