Skip to main content
Guides 11 min read

Build an AI Text Detector, Then Test If It Can Ship

Build an AI text detector with small local models, stress-test it on short and human-edited text, and use the error rates to decide if it ships.

An AI text detector built on a locally fine-tuned language model scores writing samples to separate machine-generated text from human writing.

Somewhere between vendors advertising 98 percent accuracy and skeptics declaring detection dead sits the detector you can actually build yourself. Fine-tune a 1.5B model on a consumer GPU and mid-90s accuracy on clean, in-domain text is a realistic outcome. That number feels shippable right up until the model flags a 40-word support reply a human dashed off, or an essay that only went through a grammar checker.

Stay in the loop.

Get the latest posts and exclusive content delivered to your inbox.

Join 3 readers. No spam. Unsubscribe in one click, anytime.

This guide covers the full arc: build an AI text detector with small local models, stress-test its false positives on short and human-edited text, then convert measured error rates into a ship-or-don't-ship decision. Detection behaves like any classification problem, with one nasty twist: the base rate of AI text in your traffic decides whether a given accuracy figure is usable or worthless. Three questions run through everything below. Can a small local model compete with commercial detectors? Where do its errors concentrate? When is deployment defensible? Each one gets a number by the end.

What Detectors Actually Deliver Today

Start with the honest scoreboard. OpenAI launched a text classifier in early 2023, then pulled its own classifier in July 2023, citing its low rate of accuracy. The company that built GPT-4 could not ship a detector it trusted. Meanwhile Turnitin, which processes student submissions at enormous scale, advertises roughly 98 percent accuracy with a false positive rate around one percent. Both facts are true at once, and the gap between them is where your project lives.

Independent testing keeps finding the same pattern: commercial numbers hold on clean, full-length, in-domain prose and fray on everything else. Peer-reviewed studies document meaningful false positives on real human writing, falling hardest on non-native English writers. So the right frame for AI-generated text detection is neither hype nor fatalism. It is a measurable classification task with known, testable failure modes. Your job is not to beat Turnitin. It is to measure your own error curve against your own traffic before anything ships.

Three Ways to Build an AI Text Detector

PathPrivacyMarginal costLatencyRealistic ceilingDominant failure
Commercial API detectorText leaves your machinePer-call fee, scales with volumeNetwork round tripHigh on clean prose, vendor-tunedOpaque false positive behavior, no threshold control
Fine-tuned local LLM classifierText never leaves the boxOne-time GPU time, then freeTens of ms on a consumer GPUMid-90s in-domain, degrades off-domainDistribution shift, unseen generators
Stylometric features plus classic MLFully localNear zero, CPU onlySub-millisecondOften 80s to low 90s in-domainLight paraphrasing guts the signal

Each path wins somewhere. The API route needs no training loop and makes sense when you want a baseline fast and can accept the privacy trade. Stylometric detection features, meaning sentence-length variance, type-token ratio, function-word n-grams, and punctuation habits fed to logistic regression or gradient boosting, are explainable, nearly free, and double as a sanity check on what the neural model actually learned. Raschka's from-scratch build with a DistilBERT encoder is a good reference for how little machinery the neural path needs.

But if your goal is to detect ChatGPT text locally, control the threshold, and keep user text on the machine, the case for choosing to fine-tune a small language model yourself is structural: no per-call fees, no data leaving your infrastructure, and full ownership of the operating point. Its lower accuracy ceiling is the price, and the rest of this guide measures whether that price is worth paying.

Building the Local Model Step by Step

Local GPU training setup used for LoRA fine-tuning of a binary text classification model.

This is where you actually build an AI text detector. Training a model to detect AI-generated text comes down to three decisions: what paired data it sees, which backbone you fine-tune, and where you put the threshold.

Construct paired training data

Classifiers learn the contrast you show them, so build pairs. The HC3 corpus is the canonical starting point: human answers and ChatGPT answers to the same questions, tens of thousands of pairs deep. Three rules matter more than volume:

  1. Balance the classes so the model learns style, not base rates.
  2. Split by question, not by row. If both answers to one question land in different splits, the model memorizes topics instead of writing style.
  3. Augment with your own domain. Generate answers to your users' actual prompts with the exact models you expect in production. A detector trained only on generic ChatGPT answers is guessing the moment traffic contains Claude or Llama output.

Choose a backbone

Two viable routes. A decoder model (Qwen2.5-1.5B or Llama 3.2 1B with a sequence-classification head) handles long context and trains cheaply with adapters. An encoder (RoBERTa-base, DistilBERT) trains in minutes on a modest GPU and is consistently competitive at this task. Start with the encoder. Upgrade only if long documents or multilingual traffic demand it.

A LoRA config that trains on a consumer GPU

LoRA fine-tuning for binary text classification is forgiving; the defaults below are a sane start, not a tuning exercise.

from peft import LoraConfig, get_peft_model
from transformers import AutoModelForSequenceClassification

base = AutoModelForSequenceClassification.from_pretrained(
    "Qwen/Qwen2.5-1.5B", num_labels=2, torch_dtype="bfloat16")

cfg = LoraConfig(r=16, lora_alpha=32, lora_dropout=0.05,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
    task_type="SEQ_CLS")
model = get_peft_model(base, cfg)  # ~1% of weights trainable

# Trainer: lr 2e-4, cosine schedule, 5% warmup, 2 epochs,
# effective batch 16, max_len 1024, bf16

Hugging Face PEFT ships the adapter machinery, and the Transformers training guide covers the surrounding Trainer plumbing. Expect mid-90s accuracy on a clean, in-domain held-out split; RoBERTa-class encoders land in the same range. That result is real, and it is also the last easy number you will get.

Pick a threshold, not a default

Never ship argmax. Sweep thresholds on the validation split and select an operating point by false-positive budget: if you can tolerate 2 percent, find the threshold that delivers it, then record the recall you get at that point. That pair, recall at a fixed false positive rate, is the only accuracy claim worth writing down.

An Evaluation Protocol Beyond Accuracy

Evaluation metrics like a confusion matrix help evaluate an AI text detector with precision and recall instead of top-line accuracy.

Top-line accuracy misleads three ways at once. It hides class balance, it ignores which class you failed on, and it averages over text lengths that behave completely differently. To evaluate an AI text detector with precision and recall honestly, report per-class precision and recall, false positive rate at your chosen operating point, and calibration: a score of 0.85 should mean roughly 85 percent of such texts are AI, and neural classifiers are routinely overconfident. Wrapping the model with Platt scaling or isotonic regression is a small job with scikit-learn's calibration guide.

Then bucket by length, because errors are not uniform:

Length bucketTypical false positive patternTypical false negative pattern
500+ wordsLow, near the headline rateLow
100 to 200 wordsSeveral times higherNoticeably higher
Under 50 wordsOften an order of magnitude worseHigh

Directional illustration of the pattern this harness reliably surfaces; your absolute numbers will differ, which is exactly why you must measure them.

Finally, test across generators and domains. The M4 benchmark spans multiple generators, domains, and languages precisely because training on one generator and testing on the same one overstates robustness. Cross-generator and cross-domain drops are the norm, not the exception.

Stress Testing Short and Human-Edited Text

Your held-out set, however honest, is still clean data. Production is not. AI detector false positives are not evenly distributed, and AI detector accuracy on human-edited text is the number almost no vendor publishes. Four stress conditions, in rising order of pain:

  1. Length. Recompute the false positive rate per word-count bucket on your human set. Short human text (chat replies, comments, ticket responses) is where detectors embarrass themselves, because style signal accumulates with length.
  2. Human editing. Apply realistic edits to AI passages: grammar fixes, a swapped phrase, a human intro stapled to an AI body. SemEval-2024 Task 8 ran a dedicated subtask on mixed human-machine text, and results there sit well below clean-text numbers.
  3. Paraphrase attacks. Ask any strong model to rewrite your AI set. Sadasivan et al. showed paraphrasing can drive even strong detectors toward random performance, and Krishna et al. demonstrated the same attack broadly, proposing retrieval-based defenses as a partial answer. If a free rewrite breaks your detector, assume motivated users will find that out.
  4. Writer bias. The Patterns study on detectors found popular detectors flagged the majority of TOEFL essays by non-native English writers as AI-generated, with average false positive rates above 60 percent. Test against writing by non-native speakers in your own user base before trusting any aggregate number.

Any evaluation that skips these conditions overstates ship-readiness. That is not a caveat; it is the finding.

The Base-Rate Math Behind Ship or Kill

Most detector deployments die on arithmetic, not modeling. Suppose your detector is excellent: 99 percent sensitivity, 99 percent specificity. What a flag means still depends entirely on how much AI text you actually have.

Prevalence of AI textPPV of a flagPractical reading
1 percent~50 percentA flag is a coin flip
10 percent~92 percentAbout 1 flag in 12 is wrong
50 percent~99 percentAbout 1 flag in 100 is wrong

At 1 percent prevalence, a flag from a 99/99 detector is wrong about half the time.

Walk the 1 percent case. Of 10,000 submissions, about 100 are AI. The detector catches 99 of them, and it also flags about 99 of the 9,900 human texts. The flag queue is half innocent. This is where the AI text detector false positive rate meets the base rate, and no amount of fine-tuning fixes it, because the numbers are already excellent. Prevalence does the damage.

The deployment asymmetry decides the rest: in a review workflow, a false accusation (a student penalized, a contributor banned) costs far more than a missed detection. So the rubric:

  • High prevalence, symmetric cost. Spam pre-filtering where a false positive is a held message, not an accusation. A verdict can be defensible.
  • Moderate prevalence with review capacity. Ship as assist-only triage: scores route to humans, a low-confidence band abstains.
  • Low prevalence, high stakes. Academic integrity, account bans. Do not ship a verdict.

At realistic prevalence, most single-verdict deployments fail this table. That is the point where the question of when an AI detector is too unreliable to deploy stops being philosophical and becomes arithmetic.

Guardrails If You Ship It Anyway

If the math clears triage, earn the deployment:

  • Abstain band. Scores between two thresholds return "unclear" rather than a class. Triage tools get to say they don't know.
  • Confidence tiers. Only high-confidence AI scores route anywhere. The middle band logs for monitoring and never auto-penalizes anyone.
  • Human in the loop. A flag opens a review queue. Detector output is evidence in a human decision, never the decision itself.
  • Drift monitoring. Track the score distribution weekly. A generator you never trained on shows up as distribution shift long before your dashboards notice. Retrain on a cadence and fold in new generators as they appear.
  • Show score and length. Short, high-confidence flags deserve extra skepticism. Make reviewers see both numbers.

Honest Limits and What to Build Instead

Close the loops. Can a small local model compete? On clean, in-domain text, yes: mid-90s with LoRA on a 1.5B backbone or a RoBERTa-class encoder is a normal result, and the local build wins structurally on privacy and cost, since user text never leaves the machine and inference is free. Where do errors concentrate? Short inputs, human-edited and mixed text, paraphrased output, and non-native writing. When is shipping defensible? At moderate prevalence with a human in the loop and an abstain option, or at high prevalence when a false positive is cheap and recoverable.

What a local detector is genuinely good for: spam and low-quality content triage, corpus cleaning, internal tooling where a human adjudicates, and writing feedback that warns you your own draft reads machine-polished. When the base rate says no, build signals detection cannot fake instead: draft version history, editing provenance, consented session telemetry, watermarking when you control the generation side, and disclosure flows that make honesty cheaper than evasion.

The correct output of this build is a triage instrument with an abstain option, not a judge, plus a table of numbers that tells you honestly whether even that much ships.

Stay in the loop.

Get the latest posts and exclusive content delivered to your inbox.

Join 3 readers. No spam. Unsubscribe in one click, anytime.

About the author

Tyler Brooks

Tools Analyst

Tyler has tested developer tooling for a decade, first as a platform engineer and now as an independent analyst. He reviews models, frameworks, and APIs the way he would want them reviewed before relying on them for real work.

Related Posts