Qevi-2B / qevi /__init__.py
zandervdm's picture
Use plain softmax (T=1.0): 2.45/1.9/3.0 were fitted on the base model; mark score as untested
9a0f5ec verified
Raw History Blame Contribute Delete
2.78 kB
"""Qevi: answer typed questions about an image in one forward pass, without generating text.
A normal vision-language model answers a question by decoding tokens one at a time, and
re-encodes the image for every question you ask. When the answer is known to be one of a small
fixed set ("Yes"/"No", "A"/"B"/"C"/"D", "Poor"/"Fair"/"Good"), both of those costs are waste.
Qevi instead encodes the image once, packs every question into a single sequence behind a
block-diagonal attention mask that keeps the questions provably isolated from each other, runs
one forward pass, and reads the model's own LM-head logits at the position where it would have
started writing each answer. Softmax over just that question's candidate tokens gives a
calibrated probability.
from transformers import AutoProcessor, Qwen3VLForConditionalGeneration
from qevi import build_readouts, noul_question, forward_logits, softmax
model = Qwen3VLForConditionalGeneration.from_pretrained(MODEL, dtype="bfloat16").cuda()
processor = AutoProcessor.from_pretrained(MODEL)
specs = [(noul_question("There is a ladder in this image."), ["Yes", "No"])]
readouts = build_readouts(processor.tokenizer, specs)
logits = forward_logits(model, processor, image, readouts)
print(softmax(logits[0])) # -> [0.97, 0.03]
Everything here works with a stock Qwen3-VL checkpoint. The fine-tuned weights are simply more
accurate through the same path; no custom architecture and no trust_remote_code is involved.
"""
from .core import (
Readout,
build_readouts,
forward_logits,
softmax,
unwrap,
wrap_turn,
noul_question,
choice_question,
choice_question_generic,
score_question,
score_question_generic,
NOUL_TEMPLATE,
LETTERS,
PREFIX_PRE,
PREFIX_POST,
)
from .pack import Question, PackedBatch, Segment, build_packed_batch, permute_questions
__version__ = "0.1.0"
# The fine-tuned weights in MeerDevelopment/Qevi-2B are calibrated as-is: label smoothing during
# training removed the base model's overconfidence, and T=1.0 minimises ECE on both eval splits.
# The older values (noul 2.45, choice 1.9, score 3.0) were fitted against the BASE model and make
# the fine-tune noticeably UNDER-confident; do not apply them to these weights.
DEFAULT_TEMPERATURES = {"noul": 1.0, "choice": 1.0, "score": 1.0}
__all__ = [
"Readout", "build_readouts", "forward_logits", "softmax", "unwrap", "wrap_turn",
"noul_question", "choice_question", "choice_question_generic",
"score_question", "score_question_generic",
"NOUL_TEMPLATE", "LETTERS", "PREFIX_PRE", "PREFIX_POST",
"Question", "PackedBatch", "Segment", "build_packed_batch", "permute_questions",
"DEFAULT_TEMPERATURES", "__version__",
]