Text Classification
Transformers
Safetensors
English
nli
cross-encoder
qwen3.5
reranker
image-text-to-text
Instructions to use ldov/openjevv with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ldov/openjevv with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="ldov/openjevv")# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("ldov/openjevv", device_map="auto") - Notebooks
- Google Colab
- Kaggle
|
Download README.md from ldov/openjevv: direct link, hf CLI and curl.
- Browser
- Download file 8.03 kB
-
https://huggingface.co/ldov/openjevv/resolve/main/README.md
- Command line
-
hf download hf://ldov/openjevv/README.md
-
curl -L -o README.md https://huggingface.co/ldov/openjevv/resolve/main/README.md
8.03 kB
| license: mit | |
| base_model: Qwen/Qwen3.5-4B | |
| pipeline_tag: text-classification | |
| library_name: transformers | |
| tags: | |
| - nli | |
| - cross-encoder | |
| - qwen3.5 | |
| - reranker | |
| - text-classification | |
| - image-text-to-text | |
| language: | |
| - en | |
| # openjev β Qwen3.5 trained as jev model | |
| <video controls src="https://huggingface.co/AlexWortega/openjev/resolve/main/videos/v2/doom_vision_v2.mp4" width="720"></video> | |
| <video controls src="https://huggingface.co/AlexWortega/openjev/resolve/main/videos/minecraft_chain.mp4" width="720"></video> | |
| **openjev** is Qwen3.5 turned into a *jev* model: a single cross-encoder that reads a premise and a hypothesis and | |
| answers with entailment, contradiction or neutral. That one primitive is enough to rerank answers, grade them against a | |
| reference, guard content, and play games in real time: hand it the game state and a few statements about it, and the | |
| argmax entailment is the move. Nothing is trained per task. | |
| ## openjev-4B v2: text, images and agents | |
| The new 4B checkpoint (`qwen3.5-4b-nli-v2/`) reads images as well as text and was trained on a much larger and harder | |
| mixture. It is strictly zero-shot on everything shown here. | |
| * Doom straight from the pixels (first video): **10.4 kills** per episode, twice the v1 model (5.2); random play gets 1. | |
| * Crafts an **iron pickaxe from nothing in real Minecraft** (second video): 11 milestones in ~22 decisions, driven by a | |
| backward-chaining scaffold where the jev model only checks statements about the inventory and the world. | |
| * Much stronger on adversarial NLI (ANLI r3 0.42 β 0.63, WANLI 0.63 β 0.77) and on image claims (0.52 β 0.84), | |
| better reranking (ARC-Challenge 0.59 β 0.72, MMLU 0.47 β 0.53), same MNLI (0.91). | |
| Doom from the text state (v2, 11 kills per episode; a perfect-information bot gets 18.8): | |
| <video controls src="https://huggingface.co/AlexWortega/openjev/resolve/main/videos/v2/doom_position_v2.mp4" width="720"></video> | |
|  | |
| Bigger jev: Qwen3.5-35B-A3B (MoE) as the backbone (`qwen3.5-35b-a3b-nli/`). Zero-shot, and with the backbone frozen plus | |
| a small MLP head on the last-token latent (`mlp_heads_35b/`, one head per task, loadable with `LatentMLPHead.load`): | |
|  | |
| ## What's inside | |
| * `qwen3.5-0.8b-nli-v2s-long/` β the small v2s checkpoint (0.8B, 4k context): the v2 mixture plus | |
| faithfulness / instruction-following / false-premise data and a long-document stage. MNLI 86.2/87.1, | |
| ANLI r1 65.1, SciTail 93.2, RAGTruth AUROC 0.90, LLM-AggreFact avg bAcc 69.5. This is what the | |
| [demo Space](https://huggingface.co/spaces/AlexWortega/openjev) serves. | |
| * `qwen3.5-4b-nli-v2/` β **recommended**: the 4B v2 jev checkpoint, text + images. | |
| * `qwen3.5-4b-nli/` β the original 4B jev checkpoint (text). | |
| * `qwen3.5-35b-a3b-nli/` β the 35B-A3B MoE jev checkpoint (load with `modeling_qwen35_moe_seqcls.py`). | |
| * All checkpoints: `Qwen3_5ForSequenceClassification`, 3 labels `contradiction`, `entailment`, `neutral`, last-token | |
| pooling, trained with plain cross-entropy over the three classes. | |
| * `modeling_openjev.py` β `OpenJevCrossEncoder`: `predict`, `predict_hypotheses`, `rerank`, `grade`, | |
| `latents`, `latents_hypotheses`; `LatentMLPHead` for the per-task heads. | |
| * `modeling_qwen35_moe_seqcls.py` β `Qwen3_5MoeForSequenceClassification` for the 35B-A3B backbone. | |
| * `mlp_heads_35b/<task>/` β `head.pt` + `norm.npz` + `meta.json`, the 35B latent + MLP heads behind the second radar. | |
| * `code/` β everything used here: the trainer and data mixture builder, the evaluation harness, Flappy Bird, Doom | |
| (text and pixels), the Minecraft scaffold and bot, the radar, the SGLang package / launcher / client / benchmark. | |
| * `videos/` β Flappy Bird, Doom and Minecraft replays; `results/` β raw JSON for every run and the full report. | |
| ## Use it | |
| ```python | |
| from modeling_openjev import OpenJevCrossEncoder | |
| jev = OpenJevCrossEncoder("AlexWortega/openjev", subfolder="qwen3.5-4b-nli-v2") | |
| jev.predict([("The bird is 0.05 below the centre of the gap.", "The bird is below the centre of the gap.")]) | |
| # -> [[contradiction, entailment, neutral]] probabilities | |
| jev.rerank("Which gas do plants absorb during photosynthesis?", ["oxygen", "carbon dioxide", "nitrogen"]) | |
| # -> index of the option with the highest entailment | |
| jev.predict_hypotheses("Which gas do plants absorb during photosynthesis?", | |
| ["The correct answer is: oxygen", "The correct answer is: carbon dioxide", | |
| "The correct answer is: nitrogen"]) | |
| # -> one [contradiction, entailment, neutral] row per hypothesis | |
| ``` | |
| `predict_hypotheses` and `latents_hypotheses` use the existing pairwise batch for one or two hypotheses. For three or | |
| more, they compute the common token prefix once, then score every hypothesis in one batched continuation. `rerank` | |
| uses the same rule. Qwen3.5 has recurrent linear-attention layers, so a 4D packed | |
| tree mask alone would mix branches; the shared prefix cache is copied into separate batch entries for the suffixes. | |
| This path is for text inputs. Its batch size and suffix padding use memory proportional to the number and length of | |
| the hypotheses; split very large option sets into smaller calls. | |
| Or with plain transformers: | |
| ```python | |
| from transformers import AutoModelForSequenceClassification, AutoTokenizer | |
| tok = AutoTokenizer.from_pretrained("AlexWortega/openjev", subfolder="qwen3.5-4b-nli-v2") | |
| model = AutoModelForSequenceClassification.from_pretrained("AlexWortega/openjev", subfolder="qwen3.5-4b-nli-v2") | |
| text = model.config.nli_template.format(premise="...", hypothesis="...") | |
| ``` | |
| Images go inside the premise as `<|vision_start|><|image_pad|>β¦<|vision_end|>` with `pixel_values` / `image_grid_thw` | |
| from the Qwen3.5 image processor; see `code/doom_vision.py` and `code/eval_image_nli.py`. | |
| ## Serve it with SGLang | |
| SGLang has no sequence-classification class for Qwen3.5, so `code/sglang_openjev/` is an external model package | |
| that adds the `score` head to SGLang's `Qwen3_5ForConditionalGeneration` (hybrid cache, mrope and the vision tower | |
| stay as they are). Text and images both work; `/classify` returns the three raw logits. | |
| ```bash | |
| hf download AlexWortega/openjev --include "qwen3.5-0.8b-nli-v2s-long/*" "code/*" --local-dir openjev | |
| cd openjev/code && bash serve_sglang.sh ../qwen3.5-0.8b-nli-v2s-long 30000 # tested with sglang 0.5.19 | |
| ``` | |
| ```python | |
| from sglang_client import OpenJevSGLang # code/sglang_client.py | |
| jev = OpenJevSGLang("http://127.0.0.1:30000") | |
| jev.predict([("A man is playing a guitar.", "Someone is making music.")]) # [[con, ent, neu]] | |
| jev.predict([("A photograph of a scene:", "There is a dog.")], images=["dog.jpg"]) | |
| ``` | |
| Without the script: `SGLANG_EXTERNAL_MODEL_PACKAGE=sglang_openjev` with `code/` on `PYTHONPATH`, then | |
| `python -m sglang.launch_server --model-path <dir> --is-embedding --json-model-override-args | |
| '{"architectures": ["Qwen3_5ForConditionalGeneration"]}'`. | |
| Same predictions as transformers, 1.5-3x the throughput. `qwen3.5-0.8b-nli-v2s-long` on one RTX A6000 that was | |
| shared with another job (so absolute speed is a lower bound), transformers at batch 32 (`code/bench_sglang.py`): | |
| | task | pairs | tokens / pair | acc SGLang | acc transformers | SGLang pairs/s | transformers pairs/s | speed-up | | |
| |---|---|---|---|---|---|---|---| | |
| | MNLI m+mm | 19647 | 44 | 86.63 | 86.64 | 321 | 152 | 2.1x | | |
| | ANLI r1 | 1000 | 105 | 65.3 | 64.9 | 189 | 119 | 1.6x | | |
| | ANLI r2 | 1000 | 103 | 50.6 | 50.8 | 181 | 110 | 1.6x | | |
| | ANLI r3 | 1200 | 92 | 48.6 | 48.6 | 218 | 95 | 2.3x | | |
| | WANLI | 5000 | 40 | 73.7 | 73.7 | 400 | 196 | 2.0x | | |
| | SciTail | 2126 | 43 | 93.3 | 93.4 | 319 | 217 | 1.5x | | |
| | ConTRoL (long) | 805 | 612 | 51.9 | 51.4 | 45 | 15 | 3.1x | | |
| | ARC-Challenge (rerank) | 9374 | 49 | 49.2 | 49.2 | 334 | 197 | 1.7x | | |
| | HellaSwag (rerank, 2k questions) | 16000 | 114 | 37.6 | 37.5 | 176 | 109 | 1.6x | | |
| Reference point: [dleemiller's NLI cross-encoders](https://huggingface.co/blog/dleemiller/nli-xenc-ways-to-use). Licence MIT. | |