Spaces:
Sleeping
Sleeping
| """SMOKE TEST of the AI detector on a small hand-written labelled set. | |
| CAVEAT: this set is NOT a trustworthy accuracy benchmark. | |
| * the "blatant AI" rows are phrase-stuffed caricatures whose GPT-2 perplexity | |
| is genuinely human-range, so a well-calibrated model scores several low; | |
| * the "subtle AI" rows were hand-written to look human, so "human" is arguably | |
| the correct call on them. | |
| The headline metric is held-out-source AUC on real data | |
| (scripts/diag_overfit.py / recalibrate_v3.py: ~0.96). Use this harness only to | |
| eyeball the per-sample calibrated p_ai and to confirm humans are not falsely | |
| accused β not to read a recall percentage off the caricatures. | |
| """ | |
| import os | |
| import sys | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from plagdetect import aidetect | |
| from plagdetect.textutils import sentences | |
| from data.ai_eval_samples import SAMPLES | |
| def band_says_ai(band): | |
| return band in ("likely_ai", "mixed_or_uncertain") # caught (not cleared) | |
| def decided_ai_final(score): | |
| return score >= 45 # _band threshold for "not likely_human" | |
| rows = [] | |
| for name, label, text in SAMPLES: | |
| r = aidetect.detect_ai(text) | |
| dets = {d["name"]: d["score"] for d in r["detectors"]} | |
| strongest = max(dets.values()) if dets else 0.0 | |
| meta_p = r.get("conformal", {}).get("p_ai") if r.get("conformal") else None | |
| # what the META alone (100*p_ai) would have scored, before max-pool override | |
| meta_score = 100.0 * meta_p if meta_p is not None else None | |
| rows.append(dict(name=name, label=label, score=r["score"], band=r["band"], | |
| fusion=r.get("fusion"), meta_p=meta_p, meta_score=meta_score, | |
| strongest=strongest, nsent=len(sentences(text)))) | |
| # ---------------------------------------------------------------- per sample | |
| print("=" * 92) | |
| print(f"{'sample':26s} {'lbl':3s} {'final':6s} {'band':20s} " | |
| f"{'meta_p':7s} {'fusion':10s} {'maxdet':6s}") | |
| print("-" * 92) | |
| for x in rows: | |
| mp = f"{x['meta_p']:.3f}" if x['meta_p'] is not None else " - " | |
| flag = "" | |
| if x['label'] == 1 and not decided_ai_final(x['score']): | |
| flag = " <== MISSED AI (R1 fail)" | |
| if x['label'] == 0 and decided_ai_final(x['score']): | |
| flag = " <== false alarm" | |
| print(f"{x['name']:26s} {x['label']:<3d} {x['score']:6.1f} {x['band']:20s} " | |
| f"{mp:7s} {str(x['fusion']):10s} {x['strongest']:6.1f}{flag}") | |
| # ------------------------------------------------------- final-band metrics | |
| ai = [x for x in rows if x['label'] == 1] | |
| hu = [x for x in rows if x['label'] == 0] | |
| ai_caught = sum(decided_ai_final(x['score']) for x in ai) | |
| hu_clean = sum(not decided_ai_final(x['score']) for x in hu) | |
| print("\n" + "=" * 92) | |
| print("FINAL DECISION (with R1 max-pool override active β what the user sees)") | |
| print(f" AI recall : {ai_caught}/{len(ai)} caught " | |
| f"({100*ai_caught/len(ai):.0f}%) <- R1 wants ~100%") | |
| print(f" Human specificity: {hu_clean}/{len(hu)} correctly cleared " | |
| f"({100*hu_clean/len(hu):.0f}%)") | |
| print(f" AI missed (false negatives): {len(ai)-ai_caught}") | |
| print(f" Human false alarms : {len(hu)-hu_clean}") | |
| # ------------------------------------------------- META-ONLY (overfit probe) | |
| THR = 50.0 # meta_score >= 50 == p_ai >= 0.5 | |
| ai_meta = [x for x in ai if x['meta_score'] is not None] | |
| hu_meta = [x for x in hu if x['meta_score'] is not None] | |
| ai_meta_caught = sum(x['meta_score'] >= THR for x in ai_meta) | |
| hu_meta_clean = sum(x['meta_score'] < THR for x in hu_meta) | |
| print("\n" + "-" * 92) | |
| print("META CLASSIFIER ALONE (ignore the override β this is the trained model)") | |
| print(f" ran on {len(ai_meta)}/{len(ai)} AI and {len(hu_meta)}/{len(hu)} human " | |
| f"(rest hit hand-fusion fallback)") | |
| if ai_meta: | |
| print(f" AI recall (meta only): {ai_meta_caught}/{len(ai_meta)} " | |
| f"({100*ai_meta_caught/len(ai_meta):.0f}%)") | |
| print(f" mean meta p_ai on AI : " | |
| f"{sum(x['meta_p'] for x in ai_meta)/len(ai_meta):.3f}") | |
| if hu_meta: | |
| print(f" mean meta p_ai on human: " | |
| f"{sum(x['meta_p'] for x in hu_meta)/len(hu_meta):.3f}") | |
| saved = [x for x in ai if decided_ai_final(x['score']) | |
| and (x['meta_score'] is None or x['meta_score'] < THR)] | |
| print(f"\n meta path now runs on {len(ai_meta)+len(hu_meta)}/{len(rows)} samples " | |
| f"(was 9/29 with the all-7 gate).") | |
| print(" NOTE: low recall here is the caricature caveat above, NOT overfit β " | |
| "real held-out-source AUC is ~0.96 (scripts/diag_overfit.py).") | |