ai-text-meta-classifier / src /data /raid_loader.py
BenoDugo's picture
Upload 35 files
ce3aedb verified
Raw
History Blame Contribute Delete
3.6 kB
"""Load and subsample the RAID dataset using direct CSV download.
Streaming RAID gives heavily biased samples because the data is sorted on
disk by domain. This version downloads the train split CSV directly via the
Hugging Face datasets library, loads it into pandas, shuffles properly, then
applies stratified subsampling.
The label is derived from the `model` field: "human" -> 0, anything else -> 1.
"""
from __future__ import annotations
import argparse
import random
from pathlib import Path
import pandas as pd
from datasets import load_dataset
from tqdm import tqdm
def load_raid_subsample(
target_n: int = 2000,
seed: int = 42,
include_adversarial: bool = False,
min_text_chars: int = 200,
max_text_chars: int = 4000,
) -> pd.DataFrame:
"""Download RAID train split and return a stratified subsample."""
print("Loading RAID train split (this downloads ~800MB on first run, cached after)...")
ds = load_dataset("liamdugan/raid", split="train")
df = ds.to_pandas()
print(f"Full dataset loaded: {len(df):,} rows.")
# Filter adversarial unless requested.
if not include_adversarial:
df = df[df["attack"] == "none"].copy()
print(f"After removing adversarial: {len(df):,} rows.")
# Filter by text length.
df["text_len"] = df["generation"].str.len()
df = df[(df["text_len"] >= min_text_chars) & (df["text_len"] <= max_text_chars)].copy()
print(f"After length filter: {len(df):,} rows.")
# Derive binary label.
df["label"] = (df["model"] != "human").astype(int)
df = df.rename(columns={"model": "generator", "generation": "text"})
# Shuffle before sampling.
df = df.sample(frac=1, random_state=seed).reset_index(drop=True)
# Stratified sample: equal numbers per (domain, label) bucket.
domains = df["domain"].unique()
labels = [0, 1]
n_buckets = len(domains) * len(labels)
per_bucket = max(1, target_n // n_buckets)
chunks = []
for domain in domains:
for label in labels:
bucket = df[(df["domain"] == domain) & (df["label"] == label)]
n_take = min(per_bucket, len(bucket))
if n_take > 0:
chunks.append(bucket.head(n_take))
result = pd.concat(chunks, ignore_index=True)
result = result.sample(frac=1, random_state=seed).reset_index(drop=True)
# Keep only what we need.
result = result[["id", "text", "domain", "generator", "label", "attack"]].copy()
print(f"Final subsample: {len(result):,} rows.")
return result
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--target-n", type=int, default=2000)
parser.add_argument("--output", type=str, default="data/raid_subsample.parquet")
parser.add_argument("--seed", type=int, default=42)
parser.add_argument("--include-adversarial", action="store_true")
args = parser.parse_args()
df = load_raid_subsample(
target_n=args.target_n,
seed=args.seed,
include_adversarial=args.include_adversarial,
)
out_path = Path(args.output)
out_path.parent.mkdir(parents=True, exist_ok=True)
df.to_parquet(out_path, index=False)
print(f"Wrote {len(df)} rows to {out_path}")
print("\nLabel distribution:")
print(df["label"].value_counts())
print("\nDomain distribution:")
print(df["domain"].value_counts())
print("\nGenerator distribution:")
print(df["generator"].value_counts())
if __name__ == "__main__":
main()