File size: 2,780 Bytes
77f9eec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9a0f5ec
77f9eec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9a0f5ec
 
 
 
 
77f9eec
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
"""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__",
]