gemma-4-E2B AI-text detector v2 (on-device, GPU)
QAT-LoRA + linear-probe classifier on google/gemma-4-E2B-it-qat-mobile-transformers
for AI-text detection. Quantization-aware trained (int8 activations + int8 KV cache)
so runtime accuracy matches the unquantized model.
v2 change: the int8 KV-cache fake-quant during training is now applied to the
post-RoPE, post-norm K/V — the exact tensors the runtime caches (v1 quantized the
pre-RoPE k_proj output, the wrong tensor). This fixes score calibration on short text:
v1 could over-flag short inputs on-device; v2 restores clean separation
(human mean score ≈0.05, AI mean ≈0.99). The base model.litertlm platform is
unchanged and byte-identical to v1 — only lora_adapter.tflite and head.tflite
differ.
Files
model.litertlm (2.2 GB) — base bundle: quantized weights equivalent to
litert-community/gemma-4-E2B-it-litert-lm, int8 composite KV cache, static int8
activations, 200 LoRA input sockets, activations decode output.
lora_adapter.tflite (11 MB) — trained detector LoRA (attention q/k/v/o, r=8).
head.tflite (41 KB) — linear-probe classifier: [1,1536] activations -> 4 bucket logits.
ARCHITECTURE.md — signature/socket spec + runtime integration notes.
Accuracy (device-faithful post-RoPE int8 activations + int8 KV cache)
Measured with the training-time fake-quant proxy that mirrors the runtime's int8
arithmetic (static int8 activations on 275 FC modules + post-RoPE int8 K/V on all
layers), which matches observed on-device behavior.
- human-vs-AI: 0.996 val / 0.996 test (raw-score threshold 0.596)
- human-vs-rest: 0.936 val / 0.933 test · ai-vs-rest: 0.973 val / 0.948 test
- Twitter slice: human-vs-AI 0.984, human-vs-rest 0.897, ai-vs-rest 0.927
- Class separation: human mean score 0.05, AI mean score 0.99
Deployment
- GPU backend (iOS Metal / ml_drift): verified, base model 33/35, ~4 s.
- CPU (XNNPACK) not supported (int2 MLP layers are GPU/NPU-only). Use GPU.
- Inference: clean-text preprocess (lowercase/normalize, NO chat template, no special
tokens) -> prefill -> 1 decode -> read
activations -> head.tflite -> softmax ->
score = p·[0,1,2,3]/3. score ≥ 0.596 → AI. Calibrate the threshold on your own
runtime scores if your preprocessing differs.
Reference scores (device check)
Four sample texts, scored with the post-RoPE proxy (compare directly against device):
| text | score | label |
|---|
| "This is a brutal but clear bet on an AI-first operating model…" | 0.9997 | AI |
| "Every decision carries consequences, whether positive or negative…" | 0.9947 | AI |
| "This is the canary. You're one of the first CEOs to say…" | 0.2879 | human-leaning |
| "Hard to ignore the shift here. Most CEOs wait until the numbers…" | 0.9807 | AI |
License
CC BY-NC-SA 4.0. Non-commercial research use only.