Jev-style calibrated decision model (Choice/Score/Noul) on Qwen3.5-0.8B
Python
5
12 commits
updated Oct 4, 2026
A prototype of a "System One" decision model, inspired by TypeSafe AI's Jev. The model takes a state (context) and a set of typed questions. In a single forward pass it returns calibrated probability distributions over a fixed set of answers, with no text generation.
Status of claims in this doc: Anything under "Public facts" comes from TypeSafe's docs and launch coverage. Everything else is our hypothesis of how such a model could work. TypeSafe has not published Jev's architecture.
The implementation targets Qwen/Qwen3.5-0.8B-Base. See PLAN.md for milestones. Current scope is inference only; training and fine-tuning (PLAN.md M2) are on hold.
| Milestone | Status |
|---|---|
| M0 backbone spike (prefix fork) | ✅ Done. Fork = full forward on real weights (CPU, CUDA + fla, fp32/fp16); 8–19× faster than one forward per branch; chunked branches bound memory. Gradient-through-cache check deferred until training resumes. |
| M1 templates, heads, typed API, zero-shot baselines | 🟡 Inference slice done: predict() with typed answers, letter / likelihood / PMI / `< |
| M2 training (LoRA + heads) | ⏸ On hold |
| M3 calibration and eval | Metrics (metrics.py: acc, NLL, Brier, ECE-15, MAE) in place; temperature fitting and stress suite not started |
| M4 serving / M5 multimodal | Not started |
Environment: the dev box is 2× RTX 2080 Ti (11 GB, sm75). GPU 0 fell off the PCIe bus during a dual-GPU benchmark on 2026-09-24, and CUDA init fails on both cards until a reboot or root reset. Run one GPU job at a time on this box.
Qwen3.5-0.8B is a hybrid model: 24 layers = 18 Gated DeltaNet (linear attention) + 6 gated full attention. A tree attention mask (§2.2, §5.4 below) cannot isolate sibling branches inside recurrent layers, so this repo uses prefix-fork execution instead:
question + answer branch as one right-padded batch at the same position offset, and read the hidden state at each branch's last real token.Branches are isolated in every layer type by construction, and results can't depend on question or option order.
| Path | What it does |
|---|---|
system_one/fork.py | prefill_state, expand_cache, forward_branches, fork_forward (optional chunk_size to bound memory), plus a sequential_reads reference (one uncached full forward per branch) |
tests/test_fork_equivalence.py | Fork vs. sequential equivalence on a tiny random hybrid model and on real 0.8B weights; covers padding and sibling independence, conv-kernel and delta-rule chunk edge cases, source-cache immutability, and sensitivity checks that must fail when DeltaNet state isn't forked. Runs on CUDA when available. |
scripts/bench_fork.py | Latency and peak memory: fork vs. one forward per branch, across state lengths and branch counts |
notebooks/m0_gpu_checks.ipynb | Colab notebook: installs flash-linear-attention, runs the tests on GPU, then the benchmark |
| Check | Max abs diff |
|---|---|
| Tiny model, fork vs. sequential | ~7e-7 (tolerance 1e-5) |
| Qwen3.5-0.8B-Base, fork vs. sequential | 4.8e-5 (tolerance 1e-3) |
| 0.8B with forked recurrent state zeroed (negative control) | 6.3 |
| 0.8B with forked conv state zeroed (negative control) | 3.1 |
causal-conv1d)All fast tests pass on CUDA with both the torch fallback and the fla DeltaNet kernels (fla is picked up automatically once installed).
| Check | Result |
|---|---|
| Qwen3.5-0.8B-Base, fork vs. sequential, fp32, fla | max abs diff 2.3e-5 (tolerance 1e-3), min cos 1.000000 |
| Same, fp16 (Turing has no bf16) | max abs diff 4.7e-2, min cos 0.999998 |
Benchmark (scripts/bench_fork.py --chunk-size 25, 24-token branches, median of 5). Sequential = one full forward per branch, measured up to 10 branches.
| state tokens | branches | fork ms fp32 | fork ms fp16 | fork peak GiB fp32 | sequential ms fp32 | speedup (fp32) |
|---|---|---|---|---|---|---|
| 512 | 1 | 118 | 108 | 2.91 | 108 | 0.9× |
| 512 | 10 | 135 | 126 | 3.25 | 1137 | 8.4× |
| 512 | 50 | 394 | 361 | 3.87 | (7101 unchunked) | 19× |
| 512 | 200 | 1378 | 1256 | 3.87 | – | – |
| 2048 | 10 | 535 | 298 | 3.87 | 4618 | 8.6× |
| 2048 | 50 | 777 | 555 | 5.38 | – | – |
| 2048 | 200 | 1963 | 1545 | 5.38 | – | – |
| 4096 | 10 | 895 | 553 | 4.70 | 9976 | 11.2× |
| 4096 | 200 | 3196 | – | 7.39 | – | – |
DynamicCache.batch_repeat_interleave can't fork this model (transformers 5.17). LinearAttentionLayer has no such method, and LinearAttentionAndFullAttentionLayer inherits DynamicLayer's, which repeats only keys and values. Hence the custom expand_cache.copy_), so backward through a forked cache fails. This only matters for training, which is out of scope for now.causal-conv1d ran the VM out of memory and killed the runtime twice, so the notebook now installs only flash-linear-attention. A third attempt couldn't connect to a runtime at all.repeat_kv copies it again), so memory grows with #branches × state length: 2048 tokens × 50 branches OOMs on 11 GB. forward_branches(..., chunk_size=N) caps the rows per forward; with chunk_size=25, peak memory is flat in the branch count (table above). expand instead of copy wouldn't help, because DynamicLayer.update concatenates and materialises the KV anyway.ChunkGatedDeltaRule is 64% of GPU time (mostly chunk_fwd_kernel_o), linear layers 13%, attention 4%. That's why fp16 is barely faster than fp32 for short states. For 24-token branches the torch fallback was actually faster (512 × 50: 275 ms vs 361 ms fla), probably because the chunk kernel pads each short branch to a 64-token chunk. Recheck on Ampere+ before choosing a kernel per phase (fla for the state prefill, and possibly fused_recurrent or torch for short branches).tests/conftest.py hides fla when CUDA is unavailable. Do the same (sys.modules["fla"] = None before importing transformers) in any CPU script.nvidia-smi: "Unable to determine the device handle ... Unknown Error"). After that, CUDA init failed on both GPUs until a reset. Run one GPU job at a time on this box.| Path | What it does |
|---|---|
system_one/schema.py | Parses and validates the README §5.2 request (Request.from_dict). Typed ChoiceAnswer / ScoreAnswer / NoulAnswer |
system_one/templates.py | State, question-prefix and answer-branch text, with the compact option list inside every branch (§4). Choice options are always rendered in sorted key order, so outputs don't depend on the caller's order. Letter prompts for the baseline |
system_one/scorers.py | Backbone (model + tokenizer + fork) and three scorers, all running every branch of every question through one prefix fork: LetterScorer (letter-logit reading, §7.7 #1), LikelihoodScorer (sum/mean answer log-likelihood, optional PMI against an empty state, §7.7 #2), HeadScorer (linear heads at a `< |
system_one/predict.py | predict(request, scorer, temperature=None) → typed answers: softmax / sigmoid, confidence = 1 − H(p)/log K, score = Σ p_i·i (0-based) |
system_one/metrics.py | Accuracy, NLL, Brier, ECE-15, and MAE for Score |
tests/test_predict.py | Schema validation, rendering independent of option order, answer well-formedness, question/option-order invariance and fan-out = single-question answers for every scorer (< 1e-5), fork likelihood = unforked likelihood, PMI arithmetic, `< |
scripts/zero_shot_eval.py | Zero-shot baselines through predict on ARC-Challenge (choice), BoolQ (noul), SST-5 (score) |
scripts/zero_shot_eval.py --device cpu --limit 100: Qwen3.5-0.8B-Base, fp32, seed 0. At n = 100, the 95% CI on accuracy is about ±0.09.
| dataset (type, K) | scorer | acc | NLL | Brier | ECE-15 | MAE |
|---|---|---|---|---|---|---|
| ARC-Challenge (choice, 4–5) | letter | 0.60 | 0.92 | 0.50 | 0.16 | – |
| sum | 0.32 | 4.01 | 1.12 | 0.47 | – | |
| mean | 0.40 | 1.38 | 0.74 | 0.11 | – | |
| sum-pmi | 0.49 | 2.15 | 0.81 | 0.31 | – | |
| mean-pmi | 0.49 | 1.30 | 0.71 | 0.16 | – | |
| BoolQ (noul) | letter (yes/no logits) | 0.77 | 0.55 | 0.37 | 0.12 | – |
| sum / mean | 0.75 | 0.55 | 0.37 | 0.11 | – | |
| sum-pmi / mean-pmi | 0.79 | 0.54 | 0.36 | 0.13 | – | |
| SST-5 (score, 5) | letter | 0.23 | 2.19 | 1.01 | 0.38 | 1.48 |
| sum | 0.33 | 1.60 | 0.78 | 0.10 | 1.08 | |
| mean | 0.25 | 1.63 | 0.81 | 0.09 | 1.17 | |
| sum-pmi | 0.25 | 1.60 | 0.82 | 0.21 | 1.12 | |
| mean-pmi | 0.25 | 1.59 | 0.79 | 0.03 | 1.17 |
M1 design notes:
forward_branches_all returns every branch token's hidden state (the likelihood scorers need them). forward_branches reads the last one.<|read|> gets id 248077. The tokenizer uses 248,077 ids but the embedding has 248,320 rows, so no resize is needed.nvidia-smi -r), then on one GPU: finish the fp16 benchmark rows (4096 × 50+) and rerun zero_shot_eval.py at n ≥ 500fused_recurrent for short branches (recheck on Ampere+)DecisionHeads + LoRA so HeadScorer becomes the real model. Needs gradients through the forked cache, which in-place cache updates currently block (PLAN.md fallback A: per-row [state, branch] concatenation)uv venv --python 3.12 .venv && uv pip install -e '.[dev]'
.venv/bin/python -m pytest -q # fast tests (tiny model)
QWEN_RLCD_SLOW=1 .venv/bin/python -m pytest -q -s -k qwen35 # real Qwen3.5-0.8B-Base weights
uv pip install -e '.[cuda]' # CUDA only: fla DeltaNet kernels
.venv/bin/python scripts/bench_fork.py --chunk-size 25 # benchmark (use a CUDA GPU)
uv pip install -e '.[eval]' && .venv/bin/python scripts/zero_shot_eval.py --limit 200 # zero-shot baselines
from system_one.predict import predict
from system_one.scorers import Backbone, LikelihoodScorer
scorer = LikelihoodScorer(Backbone.from_pretrained(), normalize="sum", pmi=True)
predict({"state": "...", "questions": {"is_urgent": {"type": "noul", "instructions": "The message conveys urgency."}}}, scorer)
GPU: open the notebook in Colab, choose a GPU runtime, and click Run all.
choice, probabilities, and confidence.score (which can fall between levels), probabilities, and confidence.noul, P(yes) in [0, 1], with no separate confidence.Docs: https://docs.typesafe.ai/introduction · index: https://docs.typesafe.ai/llms.txt
Free output and per-input-token pricing imply no decode loop. A pretrained decoder LLM backbone runs one forward pass, and answers are read from hidden states at specific token positions.
Operationally this behaves like a cross-encoder or reranker:
Everything is packed into one sequence:
[ state ........ ][ q1 ][ a1_1 ][ a1_2 ][ a1_3 ] ... [ q2 ][ a2_1 ][ a2_2 ] ...
| Token belongs to | Attends to |
|---|---|
| state | state (causal) |
question qi | state + itself (causal) |
answer ai_k | state + its parent qi + itself (causal) |
Siblings never see each other. This applies both to questions and to answers under the same question.
Position ids:
0 .. S-1Sqi: starts at S + len(qi)Sibling branches therefore sit at identical positions, so their results cannot depend on order.
This is the prefix-shared form of the classic BERT/RoBERTa multiple-choice setup, where each (context, option) pair is encoded separately and a softmax runs across options.
Serving alternative: prefill the state once and serve the branches from the cached prefix (FlashInfer cascade attention, SGLang radix-tree prefix sharing, or vLLM prefix caching).
s_k.p = softmax(s / T) is taken within each question group.score = Σ p_i · i, which is why it can land between levels.[yes] and [no] branches (reuses the Choice path).Normalized entropy: confidence = 1 - H(p) / log K.
This keeps confidence comparable across different option counts K. Top-1/top-2 margin is an alternative.
T, fit by minimizing NLL on held-out data). Use vector or Dirichlet scaling if there is class-specific bias.(question_type, K-bucket). The softmax behaves differently as K grows, so a T fit on 3-option questions will not transfer to 200-option questions. As an alternative, condition T on log K.| Risk | Why | Mitigation |
|---|---|---|
| Options can't see their alternatives | Each answer is judged in absolute terms; relative options ("partially" vs "mostly", "other", "none of the above") suffer | Put a compact list of all option labels inside the question branch; shuffle that list during training |
| Near-duplicate options (red bus / blue bus) | Independent logits mean duplicates split and double-count mass | Require mutually exclusive options; train on sets containing paraphrases; optionally add a small set-attention layer over the K answer embeddings before the softmax |
| Calibration drifts with K | Softmax behavior depends on K | Temperature per K-bucket or conditioned on log K; entropy normalized by log K |
| Non-monotone Score distributions | Independent level branches can produce bumpy distributions, e.g. [0.4, 0.05, 0.4, 0.15] | Ordinal-aware loss (CE + distance penalty, or neighbor label smoothing) or a cumulative-link head (one scalar + learned thresholds) |
| Position bias | Options listed inline get position effects | Tree mask with shared position ids (Section 2.2) |
torch.nn.attention.flex_attention)transformers, with a small base decoder model (e.g. a Qwen3 base in the 0.6B–8B range){
"state": "Our API started returning 500s 20 minutes ago; we can't process orders.",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payment or subscription issues",
"technical": "Bugs or integration problems",
"sales": "Pricing or account questions"
}
},
"is_urgent": { "type": "noul", "instructions": "The message conveys urgency." },
"frustration": {
"type": "score",
"instructions": "How frustrated does the customer appear?",
"criteria": ["Calm", "Frustrated but civil", "Very angry"]
}
},
"labels": { "department": "technical", "is_urgent": 1, "frustration": 1 }
}
For each sample, the packer produces per-token tensors:
input_idsposition_ids (per Section 2.2)seg_id:
0 for state tokens,parent_id:
0 for state and question tokens,seg_id for answer tokens.read_idx: the index of the last token of each answer branch (Choice/Score) or question branch (Noul), plus a mapping to (question_id, option_key).from torch.nn.attention.flex_attention import create_block_mask
# seg_id, parent_id: LongTensor [B, L]
def mask_mod(b, h, q_idx, kv_idx):
causal = kv_idx <= q_idx
to_state = seg_id[b, kv_idx] == 0
to_self = seg_id[b, kv_idx] == seg_id[b, q_idx]
to_parent = seg_id[b, kv_idx] == parent_id[b, q_idx]
return causal & (to_state | to_self | to_parent)
block_mask = create_block_mask(mask_mod, B=B, H=None, Q_LEN=L, KV_LEN=L)
Causality in packed order is valid because the state and each parent question always come before their children. For small sequences or debugging, an equivalent dense 4D mask with SDPA is fine.
logits = head(h[read_idx]), grouped per question. Loss is listwise cross-entropy within the group.After training, fit T per (type, K-bucket) on a held-out split by minimizing NLL. Store the temperatures with the checkpoint.
(state, q, a) branch as a separate sequence (max abs diff < 1e-3 in fp32).system_one/
data/ # format converters, synthetic generation
packing.py # packer -> input_ids, position_ids, seg_id, parent_id, read_idx
masks.py # FlexAttention mask_mod + block mask builder
model.py # backbone wrapper + Choice/Score/Noul heads
losses.py # listwise CE, ordinal loss, BCE
calibrate.py # temperature fitting per (type, K-bucket)
metrics.py # ECE, reliability diagrams, NLL, Brier, AUROC
serve.py # simple batched inference API returning typed answers + confidence
evals/
converters/ # one converter per benchmark -> Section 5.2 format (text + multimodal)
stress/ # synthetic stress-test builders (Sections 7.6, 8.9)
baselines/ # letter-logit, sequence-likelihood, API, cross-encoder
run_eval.py # runs a benchmark suite, writes metrics + reliability plots
tests/
test_equivalence.py
test_permutation.py
Every benchmark below is converted to the sample format in Section 5.2 by a converter in evals/converters/. Each converter records:
Hugging Face dataset IDs are given as a starting point. Verify them before use: some datasets have moved namespaces, and some (e.g. GPQA) are gated.
Core tests of listwise option scoring. Each answer choice becomes an answer branch.
| Benchmark | HF ID (verify) | K | What it tests |
|---|---|---|---|
| MMLU | cais/mmlu | 4 | Broad knowledge across 57 subjects; standard calibration benchmark |
| MMLU-Pro | TIGER-Lab/MMLU-Pro | up to 10 | Harder, more options → K-dependence of calibration |
| ARC-Easy / ARC-Challenge | allenai/ai2_arc | mostly 4 | Grade-school science reasoning |
| CommonsenseQA | tau/commonsense_qa | 5 | Commonsense; options are often semantically close |
| OpenBookQA | allenai/openbookqa | 4 | Science facts + commonsense |
| HellaSwag | Rowan/hellaswag | 4 | Long answer branches (sentence endings) → length-bias check |
| PIQA | ybisk/piqa | 2 | Physical commonsense, long solutions |
| WinoGrande | allenai/winogrande | 2 | Minimal-pair options; hard for isolated branches |
| RACE | ehovy/race | 4 | Long state (reading passage) + short questions → prefix sharing |
| SciQ | allenai/sciq | 4 | Easy science; sanity check |
| TruthfulQA (MC1) | truthfulqa/truthful_qa | varies | Plausible-but-false options; tests overconfidence |
| GPQA | Idavidrein/gpqa (gated) | 4 | Very hard; checks that confidence drops when the model is out of its depth |
| MedQA / MedMCQA | GBaker/MedQA-USMLE-4-options, openlifescienceai/medmcqa | 4 | Domain transfer |
RACE is a good fan-out test. It has one long passage with several questions, so pack them all into one request against a shared state and check that the results match running each question separately.
These test the "hundreds of options" regime and calibration drift with K.
| Benchmark | HF ID (verify) | K | What it tests |
|---|---|---|---|
| AG News | fancyzhx/ag_news | 4 | Baseline topic classification |
| TREC (coarse / fine) | CogComp/trec | 6 / 50 | Same task at two values of K → direct K-drift comparison |
| DBpedia-14 | fancyzhx/dbpedia_14 | 14 | Mid-K ontology classification |
| 20 Newsgroups | SetFit/20_newsgroups | 20 | Overlapping topics (near-duplicate-ish classes) |
| Banking77 | PolyAI/banking77 | 77 | Fine-grained, confusable intents |
| CLINC150 | clinc/clinc_oos | 150 + out-of-scope | Very large K plus a catch-all class → tests "other" handling |
| Hierarchical (e.g. patents, WOS) | various | tree | Beam search over nested Choices (see TypeSafe's hierarchical classification cookbook) |
CLINC150 is the key one for risk #1 in Section 4 (options can't see their alternatives). Compare out-of-scope recall with and without the option list inside the question branch.
| Benchmark | HF ID (verify) | What it tests |
|---|---|---|
| BoolQ | google/boolq | Passage-grounded yes/no |
| SQuAD 2.0 (answerable?) | rajpurkar/squad_v2 | Recast as "Does the passage contain the answer to the question?" → abstention |
| FEVER / VitaminC | fever/fever, tals/vitaminc | Claim verification against evidence; VitaminC uses contrastive evidence |
| HaluEval | pminervini/HaluEval | Hallucination detection on QA/dialogue/summaries |
| MS MARCO / BEIR (relevance) | microsoft/ms_marco, BeIR/* | Recast as "Is this passage relevant?" → reranking via P(yes); report nDCG@10 and MRR as well |
| Benchmark | HF ID (verify) | What it tests |
|---|---|---|
| MNLI | nyu-mll/glue (mnli) | Entailment / neutral / contradiction; matched vs. mismatched = domain shift |
| SNLI | stanfordnlp/snli | Easier NLI; sanity check |
| ANLI (R1–R3) | facebook/anli | Adversarial; calibration under hard distribution shift |
The "neutral" label is defined relative to the other two, so NLI is also a probe for risk #1.
These test expected-level scoring and monotonicity.
| Benchmark | HF ID (verify) | Levels | What it tests |
|---|---|---|---|
| SST-5 | SetFit/sst5 | 5 | Sentiment on an ordered scale |
| Yelp Review Full | Yelp/yelp_review_full | 5 | Star ratings; noisy adjacent levels |
| Amazon Reviews (star rating) | mteb/amazon_reviews_multi | 5 | Multilingual ordinal ratings |
| STS-B (binned) | nyu-mll/glue (stsb) | bin 0–5 into 6 levels | Pairwise similarity as a rubric |
| Essay scoring (ASAP-style) | check license | rubric-dependent | Human rubric levels with long state |
Score-specific metrics (in addition to the standard set):
These target the specific failure modes of the tree-mask design.
| Test | How to build | Pass condition |
|---|---|---|
| Permutation invariance | Shuffle option order and question order on MMLU and CLINC150 | Per-option probabilities unchanged (max abs diff < 1e-3); compare against a baseline that lists options inline, which should show sensitivity |
| Near-duplicate injection | Add a paraphrase of one option (correct or distractor) to MMLU and Banking77 items | Report the mass inflation: P(meaning) with duplicate − P(meaning) without |
| Catch-all removal | Remove the gold option and add "none of the above" | P(none of the above) should rise; measure how often it becomes top-1 |
| K scaling | Take an easy item and pad with 5, 20, 100, 200 random distractors from other items | Accuracy degrades gracefully; ECE stays stable after per-K temperature scaling |
| Length bias | On HellaSwag, pad wrong endings with neutral filler | Predictions should not shift toward shorter or longer answers |
| Fan-out equivalence | Pack 1, 5, 13, 50 questions against the same state (RACE, BoolQ passages) | Answers identical to single-question runs |
| Empty-state prior | Run every question with an empty state | Probabilities should be near uniform; strong skews reveal prior bias |
| Abstention | Replace the state with an unrelated passage | Confidence should drop; NLL on "can't tell" labels should improve after training |
The state can contain images or video frames as well as text. The tree mask is unchanged, because vision tokens are simply part of the state segment.
| Benchmark | HF ID (verify) | K | What it tests |
|---|---|---|---|
| MMMU | MMMU/MMMU | mostly 4 (some open) | College-level multi-discipline reasoning with images; use the MC subset |
| MMBench | lmms-lab/MMBench | 2–4 | Broad perception + reasoning; its original protocol already uses circular option shifting, which pairs well with the permutation test |
| MMStar | Lin-Chen/MMStar | 4 | Filtered so questions require the image; good for the image-blind test |
| SEED-Bench | lmms-lab/SEED-Bench | 4 | Large, broad image understanding |
| ScienceQA (image subset) | derek-thomas/ScienceQA | 2–5 | Science diagrams; variable K |
| A-OKVQA (MC) | HuggingFaceM4/A-OKVQA | 4 | Commonsense/world knowledge about images |
| AI2D | lmms-lab/ai2d | 4 | Diagram understanding |
| RealWorldQA | xai-org/RealworldQA | varies | Real-world spatial understanding (partly MC) |
| BLINK | BLINK-Benchmark/BLINK | 2–4 | Low-level perception (depth, correspondence, counting); some options are image regions |
| CV-Bench | nyu-visionx/CV-Bench | 2–6 | 2D/3D spatial relations, counting, depth |
| MathVista (MC subset) | AI4Math/MathVista | varies | Visual math; charts, plots, figures |
| Benchmark | HF ID (verify) | Type | What it tests |
|---|---|---|---|
| POPE | lmms-lab/POPE | Noul | Object hallucination ("Is there a X in the image?"), random/popular/adversarial splits |
| MME | lmms-lab/MME | Noul | Paired yes/no questions per image across perception and cognition |
| HallusionBench | lmms-lab/HallusionBench | Noul | Visual illusions and knowledge-vs-image conflicts; tests overconfidence |
| AMBER | check source | Noul | Hallucination across existence, attributes, relations |
| NLVR2 | lil-lab/nlvr | Noul | Statement true/false over a pair of images |
| SNLI-VE | check source | Choice, K = 3 | Visual entailment (image premise, text hypothesis) |
| Winoground | facebook/winoground (gated) | Choice, K = 2 | Minimal-pair image-caption matching; very hard for isolated branches |
| Hateful Memes | check license | Noul | Multimodal classification where neither modality alone suffices |
Report POPE by split. Adversarial POPE is where calibration usually breaks.
Class names or captions become answer branches.
| Benchmark | Source (verify) | K | What it tests |
|---|---|---|---|
| CIFAR-100 | uoft-cs/cifar100 | 100 | Mid-K sanity check |
| Food-101 | ethz/food101 | 101 | Fine-grained, visually similar classes |
| ImageNet-1k (or a 100-class subset) | ILSVRC/imagenet-1k (gated) | 100 / 1000 | Very large K; compare against CLIP zero-shot |
| ImageNet-R / -A / -Sketch, ObjectNet | various | 200 / 1000 | Calibration under distribution shift |
| iNaturalist (subset) | check license | hundreds+ | Extreme fine-grained + long tail |
| RVL-CDIP | aharley/rvl_cdip | 16 | Document-type classification from scanned pages |
| Flickr30k / MS-COCO (image→caption) | various | 5–100 captions | Retrieval recast as Choice over candidate captions; report R@1/R@5 too |
Baseline for this group: CLIP/SigLIP zero-shot with temperature-scaled similarity. It is cheap, strong, and well studied for calibration.
| Benchmark | Source (verify) | Levels | What it tests |
|---|---|---|---|
| KonIQ-10k | check source | bin MOS into 5 | Perceptual image quality |
| AVA (aesthetics) | check license | bin 1–10 | Aesthetic rating; noisy adjacent levels |
| Q-Bench | check source | MC + ratings | Low-level visual quality perception and description |
These use the Score metrics from Section 7.5: MAE, QWK, and monotonicity rate.
| Benchmark | HF ID (verify) | K | What it tests |
|---|---|---|---|
| Video-MME | lmms-lab/Video-MME | 4 | Short/medium/long videos; report by duration bucket |
| MVBench | OpenGVLab/MVBench | 2–5 | 20 temporal task types (action order, counting, state change) |
| NExT-QA (MC) | lmms-lab/NExTQA | 5 | Causal and temporal reasoning |
| EgoSchema | lmms-lab/egoschema | 5 | Very long egocentric clips |
| Perception Test (MC) | check source | 3 | Memory, physics, abstraction over video |
| TempCompass | check source | varies | Temporal direction, speed, order; pairs well with the frame-shuffle test |
These are closest to real deployment triage: many Noul/Choice questions per clip, where confidence gates escalation to a human.
| Benchmark | Source (verify) | Type | What it tests |
|---|---|---|---|
| Kinetics-400 / 700 | check license (YouTube) | Choice, K = 400/700 | Large-K action recognition |
| Something-Something v2 | check license | Choice, K = 174 | Fine-grained temporal actions; direction matters |
| Charades | check source | Noul per class (multi-label) | Indoor multi-label activities |
| UCF-Crime | check license | Choice (13 anomaly types + normal), Noul (anomalous?) | Real surveillance anomalies; long untrimmed video |
| XD-Violence | check license | Choice (6 violence types + normal), Noul | Violence detection; weakly labeled |
| ShanghaiTech / UBnormal | check license | Noul per segment | Campus/synthetic anomaly detection with frame-level labels |
Surveillance-specific metrics:
| Benchmark | Source (verify) | Type | What it tests |
|---|---|---|---|
| ESC-50 | check source | Choice, K = 50 | Environmental sound classification |
| AudioSet (eval subset) | check license | Noul per class | Multi-label sound events |
| MMAU | check source | Choice | Audio understanding and reasoning MC |
| Test | How to build | Pass condition |
|---|---|---|
| Image-blind | Replace the image with a blank or noise image (MMStar, POPE, MMMU) | Accuracy near chance and confidence drops; a confident answer means the model is using text priors |
| Image swap | Pair each question with an image from a different item | Confidence drops; "none/cannot tell" rises if offered |
| Caption leakage | Add a misleading caption to the state that contradicts the image | Measure how often text overrides vision, and whether confidence reflects the conflict |
| Resolution scaling | Evaluate at several input resolutions / token budgets | Accuracy–cost curve; calibration stable across resolutions |
| Frame shuffle / reverse | Shuffle or reverse frame order (MVBench, TempCompass, SSv2) | Temporal questions should fail or lose confidence; static questions should be unaffected |
| Frame count scaling | 4, 8, 16, 32, 64 frames | Accuracy and latency curves; check the fan-out cost stays flat |
| Fan-out per frame | 1, 10, 50, 100 questions against one image or clip | Answers identical to single-question runs; latency near-flat |
| Multi-camera state | Pack clips from several cameras into one state and reference them by path (cameras[2]) | Answers match single-camera runs for camera-specific questions |
Licensing note: Kinetics, UCF-Crime, XD-Violence, ImageNet, Hateful Memes, AVA, and several others have access or usage restrictions. Check terms before downloading or training on them.
log K instead of being bucketed?Jev-style calibrated decision model (Choice/Score/Noul) on Qwen3.5-0.8B
Python
5
12 commits
updated Oct 4, 2026
A prototype of a "System One" decision model, inspired by TypeSafe AI's Jev. The model takes a state (context) and a set of typed questions. In a single forward pass it returns calibrated probability distributions over a fixed set of answers, with no text generation.
Status of claims in this doc: Anything under "Public facts" comes from TypeSafe's docs and launch coverage. Everything else is our hypothesis of how such a model could work. TypeSafe has not published Jev's architecture.
The implementation targets Qwen/Qwen3.5-0.8B-Base. See PLAN.md for milestones. Current scope is inference only; training and fine-tuning (PLAN.md M2) are on hold.
| Milestone | Status |
|---|---|
| M0 backbone spike (prefix fork) | ✅ Done. Fork = full forward on real weights (CPU, CUDA + fla, fp32/fp16); 8–19× faster than one forward per branch; chunked branches bound memory. Gradient-through-cache check deferred until training resumes. |
| M1 templates, heads, typed API, zero-shot baselines | 🟡 Inference slice done: predict() with typed answers, letter / likelihood / PMI / `< |
| M2 training (LoRA + heads) | ⏸ On hold |
| M3 calibration and eval | Metrics (metrics.py: acc, NLL, Brier, ECE-15, MAE) in place; temperature fitting and stress suite not started |
| M4 serving / M5 multimodal | Not started |
Environment: the dev box is 2× RTX 2080 Ti (11 GB, sm75). GPU 0 fell off the PCIe bus during a dual-GPU benchmark on 2026-09-24, and CUDA init fails on both cards until a reboot or root reset. Run one GPU job at a time on this box.
Qwen3.5-0.8B is a hybrid model: 24 layers = 18 Gated DeltaNet (linear attention) + 6 gated full attention. A tree attention mask (§2.2, §5.4 below) cannot isolate sibling branches inside recurrent layers, so this repo uses prefix-fork execution instead:
question + answer branch as one right-padded batch at the same position offset, and read the hidden state at each branch's last real token.Branches are isolated in every layer type by construction, and results can't depend on question or option order.
| Path | What it does |
|---|---|
system_one/fork.py | prefill_state, expand_cache, forward_branches, fork_forward (optional chunk_size to bound memory), plus a sequential_reads reference (one uncached full forward per branch) |
tests/test_fork_equivalence.py | Fork vs. sequential equivalence on a tiny random hybrid model and on real 0.8B weights; covers padding and sibling independence, conv-kernel and delta-rule chunk edge cases, source-cache immutability, and sensitivity checks that must fail when DeltaNet state isn't forked. Runs on CUDA when available. |
scripts/bench_fork.py | Latency and peak memory: fork vs. one forward per branch, across state lengths and branch counts |
notebooks/m0_gpu_checks.ipynb | Colab notebook: installs flash-linear-attention, runs the tests on GPU, then the benchmark |
| Check | Max abs diff |
|---|---|
| Tiny model, fork vs. sequential | ~7e-7 (tolerance 1e-5) |
| Qwen3.5-0.8B-Base, fork vs. sequential | 4.8e-5 (tolerance 1e-3) |
| 0.8B with forked recurrent state zeroed (negative control) | 6.3 |
| 0.8B with forked conv state zeroed (negative control) | 3.1 |
causal-conv1d)All fast tests pass on CUDA with both the torch fallback and the fla DeltaNet kernels (fla is picked up automatically once installed).
| Check | Result |
|---|---|
| Qwen3.5-0.8B-Base, fork vs. sequential, fp32, fla | max abs diff 2.3e-5 (tolerance 1e-3), min cos 1.000000 |
| Same, fp16 (Turing has no bf16) | max abs diff 4.7e-2, min cos 0.999998 |
Benchmark (scripts/bench_fork.py --chunk-size 25, 24-token branches, median of 5). Sequential = one full forward per branch, measured up to 10 branches.
| state tokens | branches | fork ms fp32 | fork ms fp16 | fork peak GiB fp32 | sequential ms fp32 | speedup (fp32) |
|---|---|---|---|---|---|---|
| 512 | 1 | 118 | 108 | 2.91 | 108 | 0.9× |
| 512 | 10 | 135 | 126 | 3.25 | 1137 | 8.4× |
| 512 | 50 | 394 | 361 | 3.87 | (7101 unchunked) | 19× |
| 512 | 200 | 1378 | 1256 | 3.87 | – | – |
| 2048 | 10 | 535 | 298 | 3.87 | 4618 | 8.6× |
| 2048 | 50 | 777 | 555 | 5.38 | – | – |
| 2048 | 200 | 1963 | 1545 | 5.38 | – | – |
| 4096 | 10 | 895 | 553 | 4.70 | 9976 | 11.2× |
| 4096 | 200 | 3196 | – | 7.39 | – | – |
DynamicCache.batch_repeat_interleave can't fork this model (transformers 5.17). LinearAttentionLayer has no such method, and LinearAttentionAndFullAttentionLayer inherits DynamicLayer's, which repeats only keys and values. Hence the custom expand_cache.copy_), so backward through a forked cache fails. This only matters for training, which is out of scope for now.causal-conv1d ran the VM out of memory and killed the runtime twice, so the notebook now installs only flash-linear-attention. A third attempt couldn't connect to a runtime at all.repeat_kv copies it again), so memory grows with #branches × state length: 2048 tokens × 50 branches OOMs on 11 GB. forward_branches(..., chunk_size=N) caps the rows per forward; with chunk_size=25, peak memory is flat in the branch count (table above). expand instead of copy wouldn't help, because DynamicLayer.update concatenates and materialises the KV anyway.ChunkGatedDeltaRule is 64% of GPU time (mostly chunk_fwd_kernel_o), linear layers 13%, attention 4%. That's why fp16 is barely faster than fp32 for short states. For 24-token branches the torch fallback was actually faster (512 × 50: 275 ms vs 361 ms fla), probably because the chunk kernel pads each short branch to a 64-token chunk. Recheck on Ampere+ before choosing a kernel per phase (fla for the state prefill, and possibly fused_recurrent or torch for short branches).tests/conftest.py hides fla when CUDA is unavailable. Do the same (sys.modules["fla"] = None before importing transformers) in any CPU script.nvidia-smi: "Unable to determine the device handle ... Unknown Error"). After that, CUDA init failed on both GPUs until a reset. Run one GPU job at a time on this box.| Path | What it does |
|---|---|
system_one/schema.py | Parses and validates the README §5.2 request (Request.from_dict). Typed ChoiceAnswer / ScoreAnswer / NoulAnswer |
system_one/templates.py | State, question-prefix and answer-branch text, with the compact option list inside every branch (§4). Choice options are always rendered in sorted key order, so outputs don't depend on the caller's order. Letter prompts for the baseline |
system_one/scorers.py | Backbone (model + tokenizer + fork) and three scorers, all running every branch of every question through one prefix fork: LetterScorer (letter-logit reading, §7.7 #1), LikelihoodScorer (sum/mean answer log-likelihood, optional PMI against an empty state, §7.7 #2), HeadScorer (linear heads at a `< |
system_one/predict.py | predict(request, scorer, temperature=None) → typed answers: softmax / sigmoid, confidence = 1 − H(p)/log K, score = Σ p_i·i (0-based) |
system_one/metrics.py | Accuracy, NLL, Brier, ECE-15, and MAE for Score |
tests/test_predict.py | Schema validation, rendering independent of option order, answer well-formedness, question/option-order invariance and fan-out = single-question answers for every scorer (< 1e-5), fork likelihood = unforked likelihood, PMI arithmetic, `< |
scripts/zero_shot_eval.py | Zero-shot baselines through predict on ARC-Challenge (choice), BoolQ (noul), SST-5 (score) |
scripts/zero_shot_eval.py --device cpu --limit 100: Qwen3.5-0.8B-Base, fp32, seed 0. At n = 100, the 95% CI on accuracy is about ±0.09.
| dataset (type, K) | scorer | acc | NLL | Brier | ECE-15 | MAE |
|---|---|---|---|---|---|---|
| ARC-Challenge (choice, 4–5) | letter | 0.60 | 0.92 | 0.50 | 0.16 | – |
| sum | 0.32 | 4.01 | 1.12 | 0.47 | – | |
| mean | 0.40 | 1.38 | 0.74 | 0.11 | – | |
| sum-pmi | 0.49 | 2.15 | 0.81 | 0.31 | – | |
| mean-pmi | 0.49 | 1.30 | 0.71 | 0.16 | – | |
| BoolQ (noul) | letter (yes/no logits) | 0.77 | 0.55 | 0.37 | 0.12 | – |
| sum / mean | 0.75 | 0.55 | 0.37 | 0.11 | – | |
| sum-pmi / mean-pmi | 0.79 | 0.54 | 0.36 | 0.13 | – | |
| SST-5 (score, 5) | letter | 0.23 | 2.19 | 1.01 | 0.38 | 1.48 |
| sum | 0.33 | 1.60 | 0.78 | 0.10 | 1.08 | |
| mean | 0.25 | 1.63 | 0.81 | 0.09 | 1.17 | |
| sum-pmi | 0.25 | 1.60 | 0.82 | 0.21 | 1.12 | |
| mean-pmi | 0.25 | 1.59 | 0.79 | 0.03 | 1.17 |
M1 design notes:
forward_branches_all returns every branch token's hidden state (the likelihood scorers need them). forward_branches reads the last one.<|read|> gets id 248077. The tokenizer uses 248,077 ids but the embedding has 248,320 rows, so no resize is needed.nvidia-smi -r), then on one GPU: finish the fp16 benchmark rows (4096 × 50+) and rerun zero_shot_eval.py at n ≥ 500fused_recurrent for short branches (recheck on Ampere+)DecisionHeads + LoRA so HeadScorer becomes the real model. Needs gradients through the forked cache, which in-place cache updates currently block (PLAN.md fallback A: per-row [state, branch] concatenation)uv venv --python 3.12 .venv && uv pip install -e '.[dev]'
.venv/bin/python -m pytest -q # fast tests (tiny model)
QWEN_RLCD_SLOW=1 .venv/bin/python -m pytest -q -s -k qwen35 # real Qwen3.5-0.8B-Base weights
uv pip install -e '.[cuda]' # CUDA only: fla DeltaNet kernels
.venv/bin/python scripts/bench_fork.py --chunk-size 25 # benchmark (use a CUDA GPU)
uv pip install -e '.[eval]' && .venv/bin/python scripts/zero_shot_eval.py --limit 200 # zero-shot baselines
from system_one.predict import predict
from system_one.scorers import Backbone, LikelihoodScorer
scorer = LikelihoodScorer(Backbone.from_pretrained(), normalize="sum", pmi=True)
predict({"state": "...", "questions": {"is_urgent": {"type": "noul", "instructions": "The message conveys urgency."}}}, scorer)
GPU: open the notebook in Colab, choose a GPU runtime, and click Run all.
choice, probabilities, and confidence.score (which can fall between levels), probabilities, and confidence.noul, P(yes) in [0, 1], with no separate confidence.Docs: https://docs.typesafe.ai/introduction · index: https://docs.typesafe.ai/llms.txt
Free output and per-input-token pricing imply no decode loop. A pretrained decoder LLM backbone runs one forward pass, and answers are read from hidden states at specific token positions.
Operationally this behaves like a cross-encoder or reranker:
Everything is packed into one sequence:
[ state ........ ][ q1 ][ a1_1 ][ a1_2 ][ a1_3 ] ... [ q2 ][ a2_1 ][ a2_2 ] ...
| Token belongs to | Attends to |
|---|---|
| state | state (causal) |
question qi | state + itself (causal) |
answer ai_k | state + its parent qi + itself (causal) |
Siblings never see each other. This applies both to questions and to answers under the same question.
Position ids:
0 .. S-1Sqi: starts at S + len(qi)Sibling branches therefore sit at identical positions, so their results cannot depend on order.
This is the prefix-shared form of the classic BERT/RoBERTa multiple-choice setup, where each (context, option) pair is encoded separately and a softmax runs across options.
Serving alternative: prefill the state once and serve the branches from the cached prefix (FlashInfer cascade attention, SGLang radix-tree prefix sharing, or vLLM prefix caching).
s_k.p = softmax(s / T) is taken within each question group.score = Σ p_i · i, which is why it can land between levels.[yes] and [no] branches (reuses the Choice path).Normalized entropy: confidence = 1 - H(p) / log K.
This keeps confidence comparable across different option counts K. Top-1/top-2 margin is an alternative.
T, fit by minimizing NLL on held-out data). Use vector or Dirichlet scaling if there is class-specific bias.(question_type, K-bucket). The softmax behaves differently as K grows, so a T fit on 3-option questions will not transfer to 200-option questions. As an alternative, condition T on log K.| Risk | Why | Mitigation |
|---|---|---|
| Options can't see their alternatives | Each answer is judged in absolute terms; relative options ("partially" vs "mostly", "other", "none of the above") suffer | Put a compact list of all option labels inside the question branch; shuffle that list during training |
| Near-duplicate options (red bus / blue bus) | Independent logits mean duplicates split and double-count mass | Require mutually exclusive options; train on sets containing paraphrases; optionally add a small set-attention layer over the K answer embeddings before the softmax |
| Calibration drifts with K | Softmax behavior depends on K | Temperature per K-bucket or conditioned on log K; entropy normalized by log K |
| Non-monotone Score distributions | Independent level branches can produce bumpy distributions, e.g. [0.4, 0.05, 0.4, 0.15] | Ordinal-aware loss (CE + distance penalty, or neighbor label smoothing) or a cumulative-link head (one scalar + learned thresholds) |
| Position bias | Options listed inline get position effects | Tree mask with shared position ids (Section 2.2) |
torch.nn.attention.flex_attention)transformers, with a small base decoder model (e.g. a Qwen3 base in the 0.6B–8B range){
"state": "Our API started returning 500s 20 minutes ago; we can't process orders.",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payment or subscription issues",
"technical": "Bugs or integration problems",
"sales": "Pricing or account questions"
}
},
"is_urgent": { "type": "noul", "instructions": "The message conveys urgency." },
"frustration": {
"type": "score",
"instructions": "How frustrated does the customer appear?",
"criteria": ["Calm", "Frustrated but civil", "Very angry"]
}
},
"labels": { "department": "technical", "is_urgent": 1, "frustration": 1 }
}
For each sample, the packer produces per-token tensors:
input_idsposition_ids (per Section 2.2)seg_id:
0 for state tokens,parent_id:
0 for state and question tokens,seg_id for answer tokens.read_idx: the index of the last token of each answer branch (Choice/Score) or question branch (Noul), plus a mapping to (question_id, option_key).from torch.nn.attention.flex_attention import create_block_mask
# seg_id, parent_id: LongTensor [B, L]
def mask_mod(b, h, q_idx, kv_idx):
causal = kv_idx <= q_idx
to_state = seg_id[b, kv_idx] == 0
to_self = seg_id[b, kv_idx] == seg_id[b, q_idx]
to_parent = seg_id[b, kv_idx] == parent_id[b, q_idx]
return causal & (to_state | to_self | to_parent)
block_mask = create_block_mask(mask_mod, B=B, H=None, Q_LEN=L, KV_LEN=L)
Causality in packed order is valid because the state and each parent question always come before their children. For small sequences or debugging, an equivalent dense 4D mask with SDPA is fine.
logits = head(h[read_idx]), grouped per question. Loss is listwise cross-entropy within the group.After training, fit T per (type, K-bucket) on a held-out split by minimizing NLL. Store the temperatures with the checkpoint.
(state, q, a) branch as a separate sequence (max abs diff < 1e-3 in fp32).system_one/
data/ # format converters, synthetic generation
packing.py # packer -> input_ids, position_ids, seg_id, parent_id, read_idx
masks.py # FlexAttention mask_mod + block mask builder
model.py # backbone wrapper + Choice/Score/Noul heads
losses.py # listwise CE, ordinal loss, BCE
calibrate.py # temperature fitting per (type, K-bucket)
metrics.py # ECE, reliability diagrams, NLL, Brier, AUROC
serve.py # simple batched inference API returning typed answers + confidence
evals/
converters/ # one converter per benchmark -> Section 5.2 format (text + multimodal)
stress/ # synthetic stress-test builders (Sections 7.6, 8.9)
baselines/ # letter-logit, sequence-likelihood, API, cross-encoder
run_eval.py # runs a benchmark suite, writes metrics + reliability plots
tests/
test_equivalence.py
test_permutation.py
Every benchmark below is converted to the sample format in Section 5.2 by a converter in evals/converters/. Each converter records:
Hugging Face dataset IDs are given as a starting point. Verify them before use: some datasets have moved namespaces, and some (e.g. GPQA) are gated.
Core tests of listwise option scoring. Each answer choice becomes an answer branch.
| Benchmark | HF ID (verify) | K | What it tests |
|---|---|---|---|
| MMLU | cais/mmlu | 4 | Broad knowledge across 57 subjects; standard calibration benchmark |
| MMLU-Pro | TIGER-Lab/MMLU-Pro | up to 10 | Harder, more options → K-dependence of calibration |
| ARC-Easy / ARC-Challenge | allenai/ai2_arc | mostly 4 | Grade-school science reasoning |
| CommonsenseQA | tau/commonsense_qa | 5 | Commonsense; options are often semantically close |
| OpenBookQA | allenai/openbookqa | 4 | Science facts + commonsense |
| HellaSwag | Rowan/hellaswag | 4 | Long answer branches (sentence endings) → length-bias check |
| PIQA | ybisk/piqa | 2 | Physical commonsense, long solutions |
| WinoGrande | allenai/winogrande | 2 | Minimal-pair options; hard for isolated branches |
| RACE | ehovy/race | 4 | Long state (reading passage) + short questions → prefix sharing |
| SciQ | allenai/sciq | 4 | Easy science; sanity check |
| TruthfulQA (MC1) | truthfulqa/truthful_qa | varies | Plausible-but-false options; tests overconfidence |
| GPQA | Idavidrein/gpqa (gated) | 4 | Very hard; checks that confidence drops when the model is out of its depth |
| MedQA / MedMCQA | GBaker/MedQA-USMLE-4-options, openlifescienceai/medmcqa | 4 | Domain transfer |
RACE is a good fan-out test. It has one long passage with several questions, so pack them all into one request against a shared state and check that the results match running each question separately.
These test the "hundreds of options" regime and calibration drift with K.
| Benchmark | HF ID (verify) | K | What it tests |
|---|---|---|---|
| AG News | fancyzhx/ag_news | 4 | Baseline topic classification |
| TREC (coarse / fine) | CogComp/trec | 6 / 50 | Same task at two values of K → direct K-drift comparison |
| DBpedia-14 | fancyzhx/dbpedia_14 | 14 | Mid-K ontology classification |
| 20 Newsgroups | SetFit/20_newsgroups | 20 | Overlapping topics (near-duplicate-ish classes) |
| Banking77 | PolyAI/banking77 | 77 | Fine-grained, confusable intents |
| CLINC150 | clinc/clinc_oos | 150 + out-of-scope | Very large K plus a catch-all class → tests "other" handling |
| Hierarchical (e.g. patents, WOS) | various | tree | Beam search over nested Choices (see TypeSafe's hierarchical classification cookbook) |
CLINC150 is the key one for risk #1 in Section 4 (options can't see their alternatives). Compare out-of-scope recall with and without the option list inside the question branch.
| Benchmark | HF ID (verify) | What it tests |
|---|---|---|
| BoolQ | google/boolq | Passage-grounded yes/no |
| SQuAD 2.0 (answerable?) | rajpurkar/squad_v2 | Recast as "Does the passage contain the answer to the question?" → abstention |
| FEVER / VitaminC | fever/fever, tals/vitaminc | Claim verification against evidence; VitaminC uses contrastive evidence |
| HaluEval | pminervini/HaluEval | Hallucination detection on QA/dialogue/summaries |
| MS MARCO / BEIR (relevance) | microsoft/ms_marco, BeIR/* | Recast as "Is this passage relevant?" → reranking via P(yes); report nDCG@10 and MRR as well |
| Benchmark | HF ID (verify) | What it tests |
|---|---|---|
| MNLI | nyu-mll/glue (mnli) | Entailment / neutral / contradiction; matched vs. mismatched = domain shift |
| SNLI | stanfordnlp/snli | Easier NLI; sanity check |
| ANLI (R1–R3) | facebook/anli | Adversarial; calibration under hard distribution shift |
The "neutral" label is defined relative to the other two, so NLI is also a probe for risk #1.
These test expected-level scoring and monotonicity.
| Benchmark | HF ID (verify) | Levels | What it tests |
|---|---|---|---|
| SST-5 | SetFit/sst5 | 5 | Sentiment on an ordered scale |
| Yelp Review Full | Yelp/yelp_review_full | 5 | Star ratings; noisy adjacent levels |
| Amazon Reviews (star rating) | mteb/amazon_reviews_multi | 5 | Multilingual ordinal ratings |
| STS-B (binned) | nyu-mll/glue (stsb) | bin 0–5 into 6 levels | Pairwise similarity as a rubric |
| Essay scoring (ASAP-style) | check license | rubric-dependent | Human rubric levels with long state |
Score-specific metrics (in addition to the standard set):
These target the specific failure modes of the tree-mask design.
| Test | How to build | Pass condition |
|---|---|---|
| Permutation invariance | Shuffle option order and question order on MMLU and CLINC150 | Per-option probabilities unchanged (max abs diff < 1e-3); compare against a baseline that lists options inline, which should show sensitivity |
| Near-duplicate injection | Add a paraphrase of one option (correct or distractor) to MMLU and Banking77 items | Report the mass inflation: P(meaning) with duplicate − P(meaning) without |
| Catch-all removal | Remove the gold option and add "none of the above" | P(none of the above) should rise; measure how often it becomes top-1 |
| K scaling | Take an easy item and pad with 5, 20, 100, 200 random distractors from other items | Accuracy degrades gracefully; ECE stays stable after per-K temperature scaling |
| Length bias | On HellaSwag, pad wrong endings with neutral filler | Predictions should not shift toward shorter or longer answers |
| Fan-out equivalence | Pack 1, 5, 13, 50 questions against the same state (RACE, BoolQ passages) | Answers identical to single-question runs |
| Empty-state prior | Run every question with an empty state | Probabilities should be near uniform; strong skews reveal prior bias |
| Abstention | Replace the state with an unrelated passage | Confidence should drop; NLL on "can't tell" labels should improve after training |
The state can contain images or video frames as well as text. The tree mask is unchanged, because vision tokens are simply part of the state segment.
| Benchmark | HF ID (verify) | K | What it tests |
|---|---|---|---|
| MMMU | MMMU/MMMU | mostly 4 (some open) | College-level multi-discipline reasoning with images; use the MC subset |
| MMBench | lmms-lab/MMBench | 2–4 | Broad perception + reasoning; its original protocol already uses circular option shifting, which pairs well with the permutation test |
| MMStar | Lin-Chen/MMStar | 4 | Filtered so questions require the image; good for the image-blind test |
| SEED-Bench | lmms-lab/SEED-Bench | 4 | Large, broad image understanding |
| ScienceQA (image subset) | derek-thomas/ScienceQA | 2–5 | Science diagrams; variable K |
| A-OKVQA (MC) | HuggingFaceM4/A-OKVQA | 4 | Commonsense/world knowledge about images |
| AI2D | lmms-lab/ai2d | 4 | Diagram understanding |
| RealWorldQA | xai-org/RealworldQA | varies | Real-world spatial understanding (partly MC) |
| BLINK | BLINK-Benchmark/BLINK | 2–4 | Low-level perception (depth, correspondence, counting); some options are image regions |
| CV-Bench | nyu-visionx/CV-Bench | 2–6 | 2D/3D spatial relations, counting, depth |
| MathVista (MC subset) | AI4Math/MathVista | varies | Visual math; charts, plots, figures |
| Benchmark | HF ID (verify) | Type | What it tests |
|---|---|---|---|
| POPE | lmms-lab/POPE | Noul | Object hallucination ("Is there a X in the image?"), random/popular/adversarial splits |
| MME | lmms-lab/MME | Noul | Paired yes/no questions per image across perception and cognition |
| HallusionBench | lmms-lab/HallusionBench | Noul | Visual illusions and knowledge-vs-image conflicts; tests overconfidence |
| AMBER | check source | Noul | Hallucination across existence, attributes, relations |
| NLVR2 | lil-lab/nlvr | Noul | Statement true/false over a pair of images |
| SNLI-VE | check source | Choice, K = 3 | Visual entailment (image premise, text hypothesis) |
| Winoground | facebook/winoground (gated) | Choice, K = 2 | Minimal-pair image-caption matching; very hard for isolated branches |
| Hateful Memes | check license | Noul | Multimodal classification where neither modality alone suffices |
Report POPE by split. Adversarial POPE is where calibration usually breaks.
Class names or captions become answer branches.
| Benchmark | Source (verify) | K | What it tests |
|---|---|---|---|
| CIFAR-100 | uoft-cs/cifar100 | 100 | Mid-K sanity check |
| Food-101 | ethz/food101 | 101 | Fine-grained, visually similar classes |
| ImageNet-1k (or a 100-class subset) | ILSVRC/imagenet-1k (gated) | 100 / 1000 | Very large K; compare against CLIP zero-shot |
| ImageNet-R / -A / -Sketch, ObjectNet | various | 200 / 1000 | Calibration under distribution shift |
| iNaturalist (subset) | check license | hundreds+ | Extreme fine-grained + long tail |
| RVL-CDIP | aharley/rvl_cdip | 16 | Document-type classification from scanned pages |
| Flickr30k / MS-COCO (image→caption) | various | 5–100 captions | Retrieval recast as Choice over candidate captions; report R@1/R@5 too |
Baseline for this group: CLIP/SigLIP zero-shot with temperature-scaled similarity. It is cheap, strong, and well studied for calibration.
| Benchmark | Source (verify) | Levels | What it tests |
|---|---|---|---|
| KonIQ-10k | check source | bin MOS into 5 | Perceptual image quality |
| AVA (aesthetics) | check license | bin 1–10 | Aesthetic rating; noisy adjacent levels |
| Q-Bench | check source | MC + ratings | Low-level visual quality perception and description |
These use the Score metrics from Section 7.5: MAE, QWK, and monotonicity rate.
| Benchmark | HF ID (verify) | K | What it tests |
|---|---|---|---|
| Video-MME | lmms-lab/Video-MME | 4 | Short/medium/long videos; report by duration bucket |
| MVBench | OpenGVLab/MVBench | 2–5 | 20 temporal task types (action order, counting, state change) |
| NExT-QA (MC) | lmms-lab/NExTQA | 5 | Causal and temporal reasoning |
| EgoSchema | lmms-lab/egoschema | 5 | Very long egocentric clips |
| Perception Test (MC) | check source | 3 | Memory, physics, abstraction over video |
| TempCompass | check source | varies | Temporal direction, speed, order; pairs well with the frame-shuffle test |
These are closest to real deployment triage: many Noul/Choice questions per clip, where confidence gates escalation to a human.
| Benchmark | Source (verify) | Type | What it tests |
|---|---|---|---|
| Kinetics-400 / 700 | check license (YouTube) | Choice, K = 400/700 | Large-K action recognition |
| Something-Something v2 | check license | Choice, K = 174 | Fine-grained temporal actions; direction matters |
| Charades | check source | Noul per class (multi-label) | Indoor multi-label activities |
| UCF-Crime | check license | Choice (13 anomaly types + normal), Noul (anomalous?) | Real surveillance anomalies; long untrimmed video |
| XD-Violence | check license | Choice (6 violence types + normal), Noul | Violence detection; weakly labeled |
| ShanghaiTech / UBnormal | check license | Noul per segment | Campus/synthetic anomaly detection with frame-level labels |
Surveillance-specific metrics:
| Benchmark | Source (verify) | Type | What it tests |
|---|---|---|---|
| ESC-50 | check source | Choice, K = 50 | Environmental sound classification |
| AudioSet (eval subset) | check license | Noul per class | Multi-label sound events |
| MMAU | check source | Choice | Audio understanding and reasoning MC |
| Test | How to build | Pass condition |
|---|---|---|
| Image-blind | Replace the image with a blank or noise image (MMStar, POPE, MMMU) | Accuracy near chance and confidence drops; a confident answer means the model is using text priors |
| Image swap | Pair each question with an image from a different item | Confidence drops; "none/cannot tell" rises if offered |
| Caption leakage | Add a misleading caption to the state that contradicts the image | Measure how often text overrides vision, and whether confidence reflects the conflict |
| Resolution scaling | Evaluate at several input resolutions / token budgets | Accuracy–cost curve; calibration stable across resolutions |
| Frame shuffle / reverse | Shuffle or reverse frame order (MVBench, TempCompass, SSv2) | Temporal questions should fail or lose confidence; static questions should be unaffected |
| Frame count scaling | 4, 8, 16, 32, 64 frames | Accuracy and latency curves; check the fan-out cost stays flat |
| Fan-out per frame | 1, 10, 50, 100 questions against one image or clip | Answers identical to single-question runs; latency near-flat |
| Multi-camera state | Pack clips from several cameras into one state and reference them by path (cameras[2]) | Answers match single-camera runs for camera-specific questions |
Licensing note: Kinetics, UCF-Crime, XD-Violence, ImageNet, Hateful Memes, AVA, and several others have access or usage restrictions. Check terms before downloading or training on them.
log K instead of being bucketed?