"""Quick, network-free discrimination probe: extract the new model-agnostic AI features on the 13 real drafts and see which separate the Turnitin-flagged AI papers (AI% >= 40) from the clearly-human ones (suppressed <20). This tells us whether the features are worth building a full training corpus around, BEFORE investing in that. Pure ranking sanity check, not a trained model. """ import json, os, sys import numpy as np ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, ROOT) from plagdetect.webpipeline import extract_text, split_body_references # noqa from plagdetect.aifeatures import extract, FEATURES # noqa DSET = os.path.join(ROOT, "DATASET FOR training of turnitin") GT = os.path.join(ROOT, "data", "turnitin_groundtruth.json") def main(): gt = json.load(open(GT, encoding="utf-8")) rows = [] for rec in gt: draft = rec.get("draft") ai = (rec.get("ai") or {}).get("ai_pct") if not draft or ai is None: continue _t, text = extract_text(os.path.join(DSET, draft)) body, _ = split_body_references(text) f = extract(body) lbl = 0 if ai == "*" else (1 if ai >= 40 else -1) # -1 = ambiguous 20-39 rows.append((draft, ai, lbl, f)) ai_rows = [r for r in rows if r[2] == 1] hu_rows = [r for r in rows if r[2] == 0] print(f"AI(>=40%): {len(ai_rows)} human(<20%): {len(hu_rows)} " f"ambiguous: {sum(1 for r in rows if r[2]==-1)}\n") # per-feature separation: mean(AI) vs mean(human), and a crude AUC print(f"{'feature':22s} {'mean_AI':>9s} {'mean_HU':>9s} {'AUC':>6s}") print("-" * 50) scored = [] for k in FEATURES: a = np.array([r[3][k] for r in ai_rows]) h = np.array([r[3][k] for r in hu_rows]) # AUC = P(random AI > random human) wins = sum((x > y) + 0.5 * (x == y) for x in a for y in h) auc = wins / (len(a) * len(h)) if len(a) and len(h) else 0.5 scored.append((abs(auc - 0.5), auc, k, a.mean(), h.mean())) scored.sort(reverse=True) for _, auc, k, ma, mh in scored: flag = " <<" if abs(auc - 0.5) >= 0.25 else "" print(f"{k:22s} {ma:9.3f} {mh:9.3f} {auc:6.2f}{flag}") print("\nper-paper (sorted by Turnitin AI%):") top = [s[2] for s in scored[:4]] print(f"{'draft':30s} {'aiT':>4s} " + " ".join(f"{k[:10]:>10s}" for k in top)) for draft, ai, lbl, f in sorted(rows, key=lambda r: (r[1] if isinstance(r[1], int) else -1)): print(f"{draft[:29]:30s} {str(ai):>4s} " + " ".join(f"{f[k]:10.3f}" for k in top)) if __name__ == "__main__": main()