Image-Text-to-Text
Transformers
Safetensors
qwen3_vl
vision-language
image-classification
calibrated-probabilities
structured-outputs
typed-questions
jev-inspired
qwen3-vl
conversational
Instructions to use MeerDevelopment/Qevi-2B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use MeerDevelopment/Qevi-2B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="MeerDevelopment/Qevi-2B") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("MeerDevelopment/Qevi-2B") model = AutoModelForMultimodalLM.from_pretrained("MeerDevelopment/Qevi-2B", device_map="auto") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] inputs = processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use MeerDevelopment/Qevi-2B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "MeerDevelopment/Qevi-2B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "MeerDevelopment/Qevi-2B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/MeerDevelopment/Qevi-2B
- SGLang
How to use MeerDevelopment/Qevi-2B with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "MeerDevelopment/Qevi-2B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "MeerDevelopment/Qevi-2B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "MeerDevelopment/Qevi-2B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "MeerDevelopment/Qevi-2B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use MeerDevelopment/Qevi-2B with Docker Model Runner:
docker model run hf.co/MeerDevelopment/Qevi-2B
Add model card
Browse files
README.md
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: cc-by-nc-4.0
|
| 3 |
+
base_model: Qwen/Qwen3-VL-2B-Instruct
|
| 4 |
+
pipeline_tag: image-text-to-text
|
| 5 |
+
library_name: transformers
|
| 6 |
+
tags:
|
| 7 |
+
- vision-language
|
| 8 |
+
- image-classification
|
| 9 |
+
- calibrated-probabilities
|
| 10 |
+
- qwen3-vl
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
# Qevi-2B
|
| 14 |
+
|
| 15 |
+
A full fine-tune of [Qwen3-VL-2B-Instruct](https://huggingface.co/Qwen/Qwen3-VL-2B-Instruct) that
|
| 16 |
+
answers **typed, closed questions about images** by reading the model's own logits instead of
|
| 17 |
+
generating text.
|
| 18 |
+
|
| 19 |
+
It is not a chat model. Ask it "is there a ladder in this image?" and it returns
|
| 20 |
+
`P(Yes) = 0.97`, not a sentence. In exchange for giving up open-ended answers you get
|
| 21 |
+
substantially better accuracy on closed questions, calibrated probabilities you can threshold on,
|
| 22 |
+
and up to ~21x faster inference when asking many questions about one image.
|
| 23 |
+
|
| 24 |
+
| | Base Qwen3-VL-2B | **Qevi-2B** |
|
| 25 |
+
|---|---|---|
|
| 26 |
+
| Accuracy, in-domain | 0.855 | **0.977** |
|
| 27 |
+
| Accuracy, held-out domains | 0.745 | **0.889** |
|
| 28 |
+
| Expected Calibration Error, held-out | 0.160 | **0.054** |
|
| 29 |
+
|
| 30 |
+
---
|
| 31 |
+
|
| 32 |
+
## Read this before you use it
|
| 33 |
+
|
| 34 |
+
**This model must be used through a logit readout, not `.generate()`.** All the numbers above are
|
| 35 |
+
measured by reading the LM-head logits at the position where the model would begin its reply,
|
| 36 |
+
restricted to the allowed answer tokens. If you call `.generate()` and parse the text, you will not
|
| 37 |
+
reproduce them, because that is not the path the model was fine-tuned on.
|
| 38 |
+
|
| 39 |
+
The readout is ~30 lines of ordinary transformers code and needs no `trust_remote_code`. A
|
| 40 |
+
self-contained version is below; the [`qevi`](#the-qevi-package) package adds question packing.
|
| 41 |
+
|
| 42 |
+
---
|
| 43 |
+
|
| 44 |
+
## Usage
|
| 45 |
+
|
| 46 |
+
### Minimal, dependency-free (one question)
|
| 47 |
+
|
| 48 |
+
```python
|
| 49 |
+
import torch
|
| 50 |
+
from PIL import Image
|
| 51 |
+
from transformers import AutoProcessor, Qwen3VLForConditionalGeneration
|
| 52 |
+
|
| 53 |
+
REPO = "MeerDevelopment/Qevi-2B"
|
| 54 |
+
model = Qwen3VLForConditionalGeneration.from_pretrained(REPO, dtype=torch.bfloat16).cuda().eval()
|
| 55 |
+
processor = AutoProcessor.from_pretrained(REPO)
|
| 56 |
+
tok = processor.tokenizer
|
| 57 |
+
|
| 58 |
+
image = Image.open("photo.jpg").convert("RGB")
|
| 59 |
+
statement = "There is a ladder in this image."
|
| 60 |
+
answers = ["Yes", "No"]
|
| 61 |
+
|
| 62 |
+
prompt = (
|
| 63 |
+
"<|im_start|>user\n<|vision_start|><|vision_end|>"
|
| 64 |
+
f"Statement: {statement}\n"
|
| 65 |
+
"Is this statement true of the image? Answer Yes or No."
|
| 66 |
+
"<|im_end|>\n<|im_start|>assistant\n"
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
inputs = processor(text=[prompt], images=[image], return_tensors="pt").to(model.device)
|
| 70 |
+
with torch.no_grad():
|
| 71 |
+
logits = model(**inputs).logits[0, -1] # scores at the sentinel position
|
| 72 |
+
|
| 73 |
+
cand = [tok.encode(a, add_special_tokens=False)[0] for a in answers]
|
| 74 |
+
probs = torch.softmax(logits[cand].float() / 2.45, dim=-1) # 2.45 = calibration temperature
|
| 75 |
+
print(dict(zip(answers, probs.tolist()))) # {'Yes': 0.97, 'No': 0.03}
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
### With the `qevi` package (many questions, one forward pass)
|
| 79 |
+
|
| 80 |
+
```python
|
| 81 |
+
from qevi import build_readouts, forward_logits, softmax, noul_question, choice_question_generic
|
| 82 |
+
|
| 83 |
+
specs = [
|
| 84 |
+
(noul_question("There is a ladder in this image."), ["Yes", "No"]),
|
| 85 |
+
(noul_question("There is a bicycle in this image."), ["Yes", "No"]),
|
| 86 |
+
(choice_question_generic("Which is shown?", ["ladder", "bicycle",
|
| 87 |
+
"car", "person"]), ["A", "B", "C", "D"]),
|
| 88 |
+
]
|
| 89 |
+
readouts = build_readouts(tok, specs)
|
| 90 |
+
logit_sets = forward_logits(model, processor, image, readouts) # ONE forward pass
|
| 91 |
+
|
| 92 |
+
for lg in logit_sets:
|
| 93 |
+
print([round(p, 4) for p in softmax(lg, T=2.45)])
|
| 94 |
+
```
|
| 95 |
+
|
| 96 |
+
All three questions share a single encoding of the image and are isolated from each other by a
|
| 97 |
+
block-diagonal attention mask, so the answers are identical to asking them separately
|
| 98 |
+
(verified bitwise, see [Order invariance](#order-invariance)).
|
| 99 |
+
|
| 100 |
+
---
|
| 101 |
+
|
| 102 |
+
## Question types
|
| 103 |
+
|
| 104 |
+
| Type | Answer alphabet | Calibration temperature |
|
| 105 |
+
|---|---|---|
|
| 106 |
+
| `noul` | `["Yes", "No"]` | 2.45 |
|
| 107 |
+
| `choice` | `["A", "B", ...]`, up to 26 options | 1.9 |
|
| 108 |
+
| `score` | ordered level words, e.g. `["Poor","Fair","Good","Excellent"]` | 3.0 |
|
| 109 |
+
|
| 110 |
+
Temperatures were fitted on held-out data; dividing logits by them before softmax is what makes
|
| 111 |
+
the returned probabilities trustworthy (held-out ECE ~0.03-0.05 rather than ~0.16).
|
| 112 |
+
|
| 113 |
+
---
|
| 114 |
+
|
| 115 |
+
## Evaluation
|
| 116 |
+
|
| 117 |
+
Scored against **base Qwen3-VL-2B-Instruct run through the identical readout path**, on the same
|
| 118 |
+
images, in the same session. Both models therefore differ only in their weights, which is what
|
| 119 |
+
isolates the effect of fine-tuning from the effect of the readout. Neither model saw any of these
|
| 120 |
+
images during training.
|
| 121 |
+
|
| 122 |
+
### In-domain (17,100 questions / 5,700 images, domains seen in training)
|
| 123 |
+
|
| 124 |
+
| Metric | Base 2B | Qevi-2B | Ξ |
|
| 125 |
+
|---|---|---|---|
|
| 126 |
+
| Accuracy | 0.855 | **0.977** | +0.122 |
|
| 127 |
+
| Brier (lower better) | 0.231 | **0.039** | β0.192 |
|
| 128 |
+
| ECE (lower better) | 0.088 | **0.025** | β0.063 |
|
| 129 |
+
| Overconfidence gap | +0.088 | **β0.015** | β0.103 |
|
| 130 |
+
|
| 131 |
+
### Held-out (10,800 questions / 3,600 images, 12 domains **never trained on**)
|
| 132 |
+
|
| 133 |
+
| Metric | Base 2B | Qevi-2B | Ξ |
|
| 134 |
+
|---|---|---|---|
|
| 135 |
+
| Accuracy | 0.745 | **0.889** | +0.144 |
|
| 136 |
+
| Brier | 0.407 | **0.183** | β0.224 |
|
| 137 |
+
| ECE | 0.160 | **0.054** | β0.106 |
|
| 138 |
+
| Overconfidence gap | +0.160 | **+0.054** | β0.106 |
|
| 139 |
+
|
| 140 |
+
The model improved **more** on unseen domains (+14.4) than on trained ones (+12.2), which is the
|
| 141 |
+
main evidence that it learned a transferable skill rather than memorising the corpus.
|
| 142 |
+
|
| 143 |
+
> **A note on the baseline.** The corpus stores `teacher_probs` recorded when it was built, and an
|
| 144 |
+
> earlier draft of this card used those as the baseline. Three of the 31 groups (`food101`,
|
| 145 |
+
> `flowers102`, and the content-safety set) had been labelled by the larger Qwen3-VL-**8B**, so
|
| 146 |
+
> that baseline was a mixture of two models rather than the 2B this model was fine-tuned from.
|
| 147 |
+
> The tables above instead come from re-running base 2B over both eval sets directly. The
|
| 148 |
+
> difference is small (in-domain 0.855 vs the mixed 0.863; held-out 0.745 vs 0.741) and moves the
|
| 149 |
+
> headline slightly in this model's favour, but the comparison is now exactly what it claims to be.
|
| 150 |
+
|
| 151 |
+
### Generalisation to unseen corruptions
|
| 152 |
+
|
| 153 |
+
Training included only `clean`, `contrast`, `gaussian_noise` and `impulse_noise`. The other four
|
| 154 |
+
corruptions were held out entirely, and gained just as much:
|
| 155 |
+
|
| 156 |
+
| Satellite imagery | Seen in training? | Base | Qevi-2B |
|
| 157 |
+
|---|---|---|---|
|
| 158 |
+
| clean / contrast / gaussian / impulse | yes | 0.54β0.72 | 0.96β0.98 |
|
| 159 |
+
| jpeg / motion blur / pixelate / spatter | **no** | 0.45β0.61 | 0.89β0.97 |
|
| 160 |
+
|
| 161 |
+
### Known regressions
|
| 162 |
+
|
| 163 |
+
Two of 31 domains got **worse**. Stated plainly because they bound what this model is good for:
|
| 164 |
+
|
| 165 |
+
- **German traffic signs (GTSRB): 0.834 vs 0.879 base.** The only true regression. No traffic-sign
|
| 166 |
+
data was in the training corpus, so nothing anchored this domain against weight drift. If signs
|
| 167 |
+
matter to you, use the base model or fine-tune further with sign data included.
|
| 168 |
+
- **Pixelated car models: 0.474 vs 0.501 base.** Both models are near the floor. Distinguishing 196
|
| 169 |
+
car models after heavy pixelation is close to impossible; the information is not in the image.
|
| 170 |
+
|
| 171 |
+
### Speed
|
| 172 |
+
|
| 173 |
+
Packed inference vs. asking the same questions one at a time. Both paths read logits rather than
|
| 174 |
+
generating, so this measures the benefit of packing alone and is not flattered by comparing
|
| 175 |
+
against text generation. RTX 3090, bfloat16, CUDA-synchronised, median of 3 runs after warm-up:
|
| 176 |
+
|
| 177 |
+
| Questions per image | 1 | 5 | 10 | 20 | 30 |
|
| 178 |
+
|---|---|---|---|---|---|
|
| 179 |
+
| Packed (ms) | 504 | 509 | 518 | 577 | 618 |
|
| 180 |
+
| One at a time (ms) | 501 | 2,504 | 5,002 | 9,988 | 15,013 |
|
| 181 |
+
| **Speedup** | 0.99x | **4.9x** | **9.7x** | **17.3x** | **24.3x** |
|
| 182 |
+
|
| 183 |
+
Marginal cost of each additional question is ~5 ms on this hardware, against ~500 ms to encode the
|
| 184 |
+
image; that ratio is the whole mechanism.
|
| 185 |
+
|
| 186 |
+
**There is no benefit at N=1** (0.99x, i.e. parity), which is expected: packing one question is
|
| 187 |
+
just asking one question. The gain comes entirely from amortising the image encode, so it grows
|
| 188 |
+
with the number of questions you ask per image.
|
| 189 |
+
|
| 190 |
+
Absolute latencies are image-dependent (a larger image means more vision tokens) and
|
| 191 |
+
hardware-dependent; the speedup ratio is the portable number.
|
| 192 |
+
|
| 193 |
+
### Order invariance
|
| 194 |
+
|
| 195 |
+
Permuting question order yields bitwise-identical logits within float tolerance (atol 1e-5). On
|
| 196 |
+
choice and score tasks, 90/90 images produced byte-for-byte identical predictions packed vs.
|
| 197 |
+
unpacked. Packing does not silently change answers.
|
| 198 |
+
|
| 199 |
+
---
|
| 200 |
+
|
| 201 |
+
## Training
|
| 202 |
+
|
| 203 |
+
| | |
|
| 204 |
+
|---|---|
|
| 205 |
+
| Base | Qwen3-VL-2B-Instruct (2,127,532,032 params, Apache 2.0) |
|
| 206 |
+
| Method | Full fine-tune, all parameters trainable (no LoRA) |
|
| 207 |
+
| Objective | Cross-entropy over candidate-answer logits only, label smoothing 0.05 |
|
| 208 |
+
| Data | 85,500 questions over 28,500 images, 3 questions per image |
|
| 209 |
+
| Epochs | 1 |
|
| 210 |
+
| LR | 2e-5, OneCycle, 10% warm-up |
|
| 211 |
+
| Batch | 8 images via gradient accumulation |
|
| 212 |
+
| Optimiser | 8-bit AdamW (bitsandbytes), grad clip 1.0 |
|
| 213 |
+
| Precision | bfloat16, gradient checkpointing enabled |
|
| 214 |
+
| Hardware | 2x RTX 3090, ~2.5 hours |
|
| 215 |
+
|
| 216 |
+
Training targeted **ground-truth labels** from the source datasets, not the base model's
|
| 217 |
+
predictions, so the model learns to be correct rather than to imitate its teacher.
|
| 218 |
+
|
| 219 |
+
### Training data
|
| 220 |
+
|
| 221 |
+
Assembled from 31 publicly available image datasets spanning natural photos, sketches, paintings,
|
| 222 |
+
clipart, satellite imagery, textures, fine-grained species and product categories, plus synthetic
|
| 223 |
+
image corruptions. **The corpus itself is not redistributed with this model.**
|
| 224 |
+
|
| 225 |
+
## Licensing
|
| 226 |
+
|
| 227 |
+
**Weights: CC BY-NC 4.0 (non-commercial).** Not because the base model requires it, but because of
|
| 228 |
+
training-data provenance. Of the twelve source dataset families, only EuroSAT and the
|
| 229 |
+
content-safety set are permissively licensed (MIT); the other ten (CUB-200-2011, Stanford Cars,
|
| 230 |
+
Food-101, SUN397, Oxford Flowers-102, Oxford-IIIT Pets, DTD, GTSRB, DomainNet, RESISC45) are
|
| 231 |
+
distributed for academic or research use only. Whether a research-only dataset licence reaches the
|
| 232 |
+
weights of a model trained on it is genuinely unsettled, so this release takes the cautious side
|
| 233 |
+
rather than granting commercial rights it may not be in a position to grant.
|
| 234 |
+
|
| 235 |
+
**Code: Apache 2.0.** The [`qevi`](#the-qevi-package) package carries no data-provenance
|
| 236 |
+
encumbrance and is freely usable, including commercially.
|
| 237 |
+
|
| 238 |
+
**Base model: Apache 2.0** ([Qwen3-VL-2B-Instruct](https://huggingface.co/Qwen/Qwen3-VL-2B-Instruct)),
|
| 239 |
+
unaffected by the above.
|
| 240 |
+
|
| 241 |
+
**If you need commercially-licensable weights:** the method, the code and the base model are all
|
| 242 |
+
permissive. Retrain using the `qevi` package on data you own or that is licensed for commercial
|
| 243 |
+
use, and the resulting weights carry no encumbrance from this corpus. The recipe is fully
|
| 244 |
+
documented above (1 epoch, lr 2e-5, ~5 GPU-hours on two consumer cards).
|
| 245 |
+
|
| 246 |
+
---
|
| 247 |
+
|
| 248 |
+
## Limitations
|
| 249 |
+
|
| 250 |
+
- **Closed answers only.** Every question needs a finite answer set declared up front. It cannot
|
| 251 |
+
caption, describe, or answer something you did not anticipate.
|
| 252 |
+
- **Free-form generation survives, but answers get terser.** The fine-tune optimised only the
|
| 253 |
+
typed-readout objective, so we checked whether ordinary `.generate()` still works. It does:
|
| 254 |
+
across 5 held-out images x 3 open prompts, greedy decoding produced coherent, grammatical,
|
| 255 |
+
on-topic text with no repetition collapse (degeneracy 0.002 vs 0.001 for base) and no empty
|
| 256 |
+
outputs. The measurable change is **length: mean 28.3 generated tokens vs 50.7 for the base**,
|
| 257 |
+
a ~44% drop. Having been trained on one-word answers, the model answers open questions more
|
| 258 |
+
directly ("Spiders" where the base writes "The main object in this image is a spider.").
|
| 259 |
+
This is a behavioural shift, not a capability loss, but if you want verbose descriptions the
|
| 260 |
+
base model is the better choice.
|
| 261 |
+
- **Answer tokens must be single-token-distinguishable.** Candidates are matched on their first
|
| 262 |
+
token, so options sharing a first token (e.g. "Poor" vs "Poorly") collide.
|
| 263 |
+
- **English prompts only**, matching the prompt templates used in training.
|
| 264 |
+
- **Not a safety classifier.** One training domain involved content-severity labels, but this model
|
| 265 |
+
has not been validated for moderation use and should not be deployed for it without its own
|
| 266 |
+
evaluation.
|
| 267 |
+
|
| 268 |
+
## The `qevi` package
|
| 269 |
+
|
| 270 |
+
Packing, prompt construction and the readout live in a small Apache-2.0 package
|
| 271 |
+
(~350 lines, torch only):
|
| 272 |
+
|
| 273 |
+
```bash
|
| 274 |
+
pip install qevi
|
| 275 |
+
```
|
| 276 |
+
|
| 277 |
+
## Citation
|
| 278 |
+
|
| 279 |
+
```bibtex
|
| 280 |
+
@misc{qevi2b,
|
| 281 |
+
title = {Qevi-2B: typed question answering over images without text generation},
|
| 282 |
+
year = {2026},
|
| 283 |
+
note = {Full fine-tune of Qwen3-VL-2B-Instruct with packed native-logit readout}
|
| 284 |
+
}
|
| 285 |
+
```
|