AbsoluteZero NLI 2B
Observable NLI with think traces
Provide a text (premise) and a statement (hypothesis), and it returns calibrated probabilities that the text entails, contradicts, or is neutral to the statement. One model answers in two ways:
- Fast mode: a single forward pass and a classifier head (similar to cross-encoders).
- Think mode: the model writes a short reasoning trace, then the scoring head reads it and gives the verdict. Use it on the hard cases, such as multi-step logic, quantifiers and negation, long contracts and policies. Traces are short (~55 tokens on average on ANLI, ~220 on first-order-logic problems).
Benefits of observation
Typical zero-shot encoder models cannot provide information that elucidates why they chose their classification. Think traces enable users to inspect and determine how to augment hypotheses to fix or improve classification. This could be done in conjunction with automation for grouping classification failures and using an LLM to test changes to the hypothesis against a holdout set.
Examples
| Use | Text → statement | Result (fast mode) |
|---|---|---|
| Zero-shot classification / tagging | review → "This review is positive." | 92.6% (zs_meta, 18 tasks) |
| RAG grounding | retrieved passages → generated answer | 85.8% (RAGTruth) |
| Guardrails / Moderation | message → policy statement | 92.1% (CrossingGuard) |
| Clinical extraction | clinical note → "The patient is unemployed." | 98.3% (SDOH-NLI) |
| Contract review | contract → "The agreement permits sharing data with third parties." | 92.6% (ContractNLI) |
1.9B parameters · documents up to 32k tokens · #2 on the OpenNLI Skill Index (11-09-26, fast mode) · Apache-2.0
from sentence_transformers import CrossEncoder
model = CrossEncoder("dleemiller/AbsoluteZero-nli-2b", trust_remote_code=True)
text = "My card was charged twice for the same order."
labels = ["billing", "shipping", "account access"]
probs = model.predict([(text, f"This text is about {l}.") for l in labels], apply_softmax=True)
print({l: round(float(p[0]), 3) for l, p in zip(labels, probs)}) # p[0] = P(entailment)
# {'billing': 0.976, 'shipping': 0.002, 'account access': 0.002}
More in Think Mode, Recommended Applications and the 45-dataset evaluation.
Table of Contents
- NLI Evaluation Results
- Usage
- Think Mode
- Recommended Applications
- Extended Evaluation (45 datasets)
- Training Details
- Limitations
- Acknowledgments
- Citation
NLI Evaluation Results
Comparison on the OpenNLI leaderboard (v0.2.8), fast mode.
| Model | Params | Skill Index | Accuracy | mnli | mnli_mismatched | snli | anli_r1 | anli_r2 | anli_r3 | wanli | lingnli | CAL | Throughput (pairs/s) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
dleemiller/AbsoluteZero-nli-2b |
1.9B | 59.6 | 0.8262 | 0.9130 | 0.9146 | 0.9332 | 0.7860 | 0.6470 | 0.6042 | 0.7656 | 0.8741 | 7.7 | 353 |
dleemiller/AbsoluteZero-nli-4b |
4.2B | 61.8 | 0.8372 | 0.9211 | 0.9227 | 0.9366 | 0.8250 | 0.7060 | 0.6517 | 0.7718 | 0.8829 | 7.2 | 159 |
dleemiller/finecat-nli-v2-xl |
1.03B | 59.5 | 0.8263 | 0.9202 | 0.9191 | 0.9356 | 0.7860 | 0.6600 | 0.6067 | 0.7710 | 0.8834 | 6.1 | 576 |
dleemiller/finecat-nli-ultra-l |
378M | 58.6 | 0.8213 | 0.9127 | 0.9132 | 0.9302 | 0.7910 | 0.6400 | 0.5808 | 0.7698 | 0.8708 | 6.6 | 1,487 |
dleemiller/finecat-nli-v2-l |
396M | 57.8 | 0.8182 | 0.9164 | 0.9122 | 0.9329 | 0.7760 | 0.6040 | 0.5767 | 0.7664 | 0.8775 | 6.5 | 1,224 |
tasksource/ModernBERT-large-nli* |
396M | 37.8 | 0.7246 | 0.8981 | 0.9223 | 0.9192 | 0.7290 | 0.5130 | 0.4917 | 0.6970 | 0.8497 | 13.2 | 1,218 |
dleemiller/finecat-nli-l* |
396M | 37.6 | 0.7292 | 0.9155 | 0.9269 | 0.9162 | 0.7470 | 0.5680 | 0.5425 | 0.7708 | 0.8745 | 20.9 | 1,225 |
MoritzLaurer/DeBERTa-v3-large-mnli-fever-anli-ling-wanli |
435M | 27.9 | 0.6767 | 0.9123 | 0.9077 | 0.8897 | 0.7950 | 0.6820 | 0.6392 | 0.7696 | 0.8815 | 25.3 | 1,092 |
sileod/deberta-v3-large-tasksource-nli* |
435M | 24.8 | 0.6341 | 0.9298 | 0.9447 | 0.9211 | 0.7370 | 0.5770 | 0.4975 | 0.6786 | 0.8673 | 17.9 | 1,091 |
* trained on one or more OpenNLI test sets; those scores are replaced by estimates, as on the leaderboard.
Usage
Label Map:
entailment: 0neutral: 1contradiction: 2
Fast mode (Sentence Transformers)
pip install -U sentence-transformers
from sentence_transformers import CrossEncoder
import numpy as np
model = CrossEncoder("dleemiller/AbsoluteZero-nli-2b", trust_remote_code=True)
id2label = model.model.config.id2label # {0:'entailment', 1:'neutral', 2:'contradiction'}
pairs = [
("The glass fell off the counter and shattered on the tile floor.",
"The glass broke when it hit the floor."), # E
("The store opens at 9 a.m. every day.",
"The store opens at 7 a.m. on weekdays."), # C
("A researcher presented results at the conference.",
"The presentation won the best paper award."), # N
]
logits = model.predict(pairs) # shape: (3, 3)
for (prem, hyp), row in zip(pairs, logits):
print(f"[{id2label[int(np.argmax(row))]}] Premise: {prem} | Hypothesis: {hyp}")
The model reads each pair in its training layout
(Premise: … / Hypothesis: … / <think></think> / Relation:); the tokenizer shipped with the model builds it, so pass
plain (premise, hypothesis) pairs. Inputs are right-padded; the head reads each row's last token.
Scoring multiple hypotheses on one text. The model is causal and the premise comes first, so for tagging or checking many
claims against one document, score_many encodes the premise once and scores every hypothesis from that cached state:
the same predictions as scoring pair by pair, ~10x faster for 50 labels on a ~500-token document.
doc = "My card was charged twice for the same order, and support hasn't replied in a week."
labels = ["billing", "shipping", "account access", "customer support"]
logits = model.model.score_many(model.tokenizer, doc, [f"This text is about {l}." for l in labels])
print({l: round(p, 3) for l, p in zip(labels, logits.softmax(-1)[:, 0].tolist())}) # P(entailment) per label
# {'billing': 0.965, 'shipping': 0.002, 'account access': 0.002, 'customer support': 0.991}
Think Mode
Think mode is a method on the loaded model; it returns the verdict, its probabilities and the trace.
from sentence_transformers import CrossEncoder
model = CrossEncoder("dleemiller/AbsoluteZero-nli-2b", trust_remote_code=True)
pairs = [
("The glass fell off the counter and shattered on the tile floor.", "The glass broke when it hit the floor."),
("The store opens at 9 a.m. every day.", "The store opens at 7 a.m. on weekdays."),
("A researcher presented results at the conference.", "The presentation won the best paper award."),
]
for r in model.model.think(model.tokenizer, pairs):
print(r["label"], "|", r["trace"])
entailment | Shattering on the floor means it broke upon hitting the floor, so the premise entails the hypothesis.
contradiction | The premise states the store opens at 9 a.m. daily, while the hypothesis states it opens at 7 a.m. on weekdays. These times conflict, so the premise contradicts the hypothesis.
neutral | Presenting results at a conference does not indicate whether the presentation won an award, so the premise neither guarantees nor rules out the hypothesis.
Think vs. fast (test sets: n=300 pairs each; ANLI dev: full dev splits):
| Dataset | Fast | Think | Think tokens (avg.) |
|---|---|---|---|
| anli_r2 (test) | 0.6500 | 0.6900 | 57 |
| anli_r3 (test) | 0.5933 | 0.6200 | 57 |
| wanli (test) | 0.7133 | 0.7200 | 47 |
| vitaminc (test) | 0.8667 | 0.8133 | 53 |
| control (test) | 0.7233 | 0.7133 | 68 |
| fol (test) | 0.8067 | 0.7467 | 225 |
| anli_dev_r1 | 0.7911 | 0.7943 | 56 |
| anli_dev_r2 | 0.6578 | 0.6952 | 56 |
| anli_dev_r3 | 0.5917 | 0.5926 | 57 |
Both modes have comparable accuracy with some variation. Confidence is calibrated for fast mode, while adding think traces will make the classification head certain toward the think trace, so confidence cannot be used in that mode.
Recommended Applications
Frame each check as a premise (text) and hypothesis (statement), and use the entailment probability.
| Application | Example hypothesis | Supporting results |
|---|---|---|
| Zero-shot tagging / topic classification | "This text is about billing." | zs_meta, fin_topics |
| Routing and decisions in agent pipelines | "The user wants to cancel an order." | zs_meta, mnli, snli |
| RAG grounding / hallucination checks | Each answer sentence vs. retrieved passages | ragtruth, wice, vitaminc |
| Contract and policy review | "The agreement permits sharing data with third parties." | contractnli_a/b, control, docnli |
| Biomedical and health claim checking | Claim vs. abstract or clinical note | sdoh_nli, biosift, bionli, scifact, pubhealth |
| Dialogue fact-checking | Assistant reply vs. knowledge source | dialfact, vitaminc |
| Guardrails / policy matching | Policy statement vs. user or model text | crossingguard |
| Logical reasoning (think mode) | Conclusion vs. premises | fol, control, anli |
Less suited: financial and general legal verification (findver, legal_domain), and adversarial reasoning (anli_r3, nope_adv).
Extended Evaluation (45 datasets)
Accuracy (fast mode) on the test splits of 45 NLI and adjacent datasets. 2-way datasets (entailed vs. not) are scored as entailment vs. any non-entailment prediction. DocNLI and VitaminC are evaluated on fixed random subsets of 5,000 and 10,000 pairs; FRANK is scored per summary. — = not on the same items.
| Dataset | n | AbsoluteZero-nli-2b |
AbsoluteZero-nli-4b |
finecat-nli-v2-xl |
|---|---|---|---|---|
| finecat | 18012 | 0.8412 | 0.8533 | 0.8445 |
| mnli | 9815 | 0.9130 | 0.9207 | 0.9202 |
| mnli_mismatched | 9830 | 0.9148 | 0.9226 | 0.9191 |
| snli | 9824 | 0.9335 | 0.9367 | 0.9356 |
| anli_r1 | 1000 | 0.7850 | 0.8260 | 0.7860 |
| anli_r2 | 1000 | 0.6470 | 0.7060 | 0.6600 |
| anli_r3 | 1200 | 0.6033 | 0.6542 | 0.6067 |
| wanli | 5000 | 0.7662 | 0.7720 | 0.7710 |
| lingnli | 7361 | 0.8739 | 0.8830 | 0.8834 |
| glue_diagnostic | 1100 | 0.7464 | 0.7564 | 0.7582 |
| med | 5380 | 0.7851 | 0.7941 | 0.8147 |
| nan_nli | 258 | 0.7403 | 0.7481 | 0.7171 |
| scone_test | 1000 | 0.9990 | 1.0000 | 1.0000 |
| nmonli_test | 200 | 1.0000 | 1.0000 | 1.0000 |
| conjnli | 623 | 0.7255 | 0.7303 | 0.7191 |
| nope_adv | 345 | 0.5159 | 0.5507 | 0.5739 |
| nli4ct | 199 | 0.8543 | 0.8543 | 0.8141 |
| sdoh_nli | 990 | 0.9828 | 0.9818 | 0.9879 |
| healthver | 230 | 0.7957 | 0.7565 | 0.7609 |
| scifact | 300 | 0.8767 | 0.8767 | 0.8733 |
| biosift | 1000 | 0.9190 | 0.9240 | 0.9200 |
| scientsbank | 971 | 0.7549 | 0.7683 | 0.7508 |
| beetle | 819 | 0.6654 | 0.6667 | 0.6422 |
| legal_domain | 1163 | 0.5374 | 0.5813 | 0.4918 |
| findver | 500 | 0.6940 | 0.7240 | 0.6560 |
| dialfact | 1499 | 0.8359 | 0.8512 | 0.8412 |
| frank | 1572 | 0.7023 | 0.7417 | — |
| ragtruth | 1497 | 0.8584 | 0.8644 | 0.8544 |
| vast | 1500 | 0.7820 | 0.7907 | 0.7933 |
| zs_meta | 5400 | 0.9257 | 0.9341 | 0.9304 |
| seahorse | 800 | 0.8425 | 0.8413 | 0.8562 |
| pubhealth | 600 | 0.8533 | 0.8617 | 0.8500 |
| bionli | 600 | 0.9117 | 0.9167 | 0.9133 |
| fin_topics | 600 | 0.9583 | 0.9700 | 0.9767 |
| vitaminc | 10000 | 0.8752 | 0.9030 | 0.9062 |
| docnli | 5000 | 0.8420 | 0.8516 | — |
| contractnli_a | 1707 | 0.9262 | 0.9291 | 0.9279 |
| contractnli_b | 2074 | 0.8992 | 0.9026 | 0.8954 |
| control | 800 | 0.7137 | 0.7675 | 0.7525 |
| fol | 10277 | 0.8189 | 0.8481 | 0.8727 |
| scitail | 2126 | 0.9647 | 0.9671 | 0.9661 |
| wice | 958 | 0.8977 | 0.8998 | 0.8810 |
| sick | 4906 | 0.9217 | 0.9246 | 0.9317 |
| crossingguard | 5397 | 0.9211 | 0.9248 | 0.9250 |
| hans | 15000 | 0.8649 | 0.8468 | 0.8463 |
Macro average over the 45 datasets: 0.8264 (4B: 0.8383).
Training Details
This model is a fine-tune of Qwen/Qwen3.5-2B (text backbone), trained in two stages.
Stage 1: distillation. Full fine-tune (all weights) & two classifier heads, trained on the
FineCat-NLI-Reason compilation relabelled by an NLI fine-tune of Qwen3.8-27B.
Stage 2: reinforcement learning on think mode (GRPO). Think mode was further trained with 32 sampled traces per prompt, reward = correctness of the verdict with a soft overlong penalty, a repetition penalty and a floor that discourages trace collapse.
Stage 2 made think mode much shorter at equal or better accuracy. On first-order-logic validation problems, accuracy 0.792 → 0.806 with traces cut from ~365 to ~217 tokens on average.
The released weights are the stage-2 GRPO LoRA merged into the stage-1 weights, stored in bfloat16.
Limitations
- English only.
- Throughput: ~350 pairs/s on short pairs (~90 on long documents) in fast mode; use the FineCat encoders for very high
volume or tight latency. Think mode generates a trace per pair (~1-10 pairs/s with
transformers, depending on trace length; vLLM is faster). - Trained on inputs up to 8k tokens; scored at up to 32k, but accuracy on documents longer than 8k is not separately tested.
- Weaker on financial and general legal verification (findver, legal_domain) and adversarial reasoning (anli_r3, nope_adv).
Acknowledgments
We thank the Qwen team for making their work available. This model would not be possible without their efforts and open source contributions.
Citation
@misc{absolutezero-2026,
title = {AbsoluteZero: Observable NLI},
author = {Lee Miller},
year = {2026},
howpublished = {\url{https://huggingface.co/dleemiller/AbsoluteZero-nli-2b}}
}