Model name: qwen3-vl (VLM)
Command run
uv run coreai.vlm.export qwen3-vl
# then any image inference through llm-runner --image
What happens
SUPPORTED_MODELS["qwen3-vl"] in python/src/coreai_models/vlm/export.py writes OpenAI-CLIP normalization stats into the bundle's vision metadata block:
image_mean=(0.48145466, 0.4578275, 0.40821073),
image_std=(0.26862954, 0.26130258, 0.27577711),
These are the class defaults of Qwen2VLImageProcessor, but the Qwen3-VL checkpoints override them in preprocessor_config.json:
"image_mean": [0.5, 0.5, 0.5],
"image_std": [0.5, 0.5, 0.5]
Since ImagePreprocessor applies (x * rescale/255 − mean) / std per channel (swift/Sources/CoreAIShared/Image/ImagePreprocessor.swift), every image reaching the exported vision encoder is normalized in the wrong space:
- what the ViT was trained on:
(x/255 − 0.5) / 0.5
- what the bundle computes:
(x/255 − 0.4815) / 0.2686 (R channel; G/B analogous)
That is roughly a 1.86× overscale plus a channel-dependent offset on the encoder input. Generation still produces plausible captions (which makes this easy to miss), but the vision-encoder inputs differ from the HF reference pipeline on every pixel, so fine-grained visual fidelity is silently degraded. I have not run a quantitative A/B on the released bundle — the mismatch above is verified from the configs and the preprocessing code paths.
Suggested fix
Set image_mean=(0.5, 0.5, 0.5), image_std=(0.5, 0.5, 0.5) in the qwen3-vl VLMSpec.
Longer term: read the stats from the checkpoint at export time (AutoImageProcessor.from_pretrained(...).image_mean / image_std) instead of hardcoding them per spec. The CLIP fallback documented in VisionConfig ("the most common across VLMs") is a trap for exactly this case — the Qwen processor class default is CLIP, while the Qwen3-VL checkpoints ship 0.5/0.5.
macOS / iOS target: any (export-side metadata bug)
Full error output: n/a — no error is raised; this is a silent numerics divergence.
Model name: qwen3-vl (VLM)
Command run
uv run coreai.vlm.export qwen3-vl # then any image inference through llm-runner --imageWhat happens
SUPPORTED_MODELS["qwen3-vl"]inpython/src/coreai_models/vlm/export.pywrites OpenAI-CLIP normalization stats into the bundle'svisionmetadata block:These are the class defaults of
Qwen2VLImageProcessor, but the Qwen3-VL checkpoints override them inpreprocessor_config.json:Qwen3-VL-4B-InstructandQwen3-VL-8B-Instruct; it also matches the transformersQwen3VLVideoProcessordefaults (video_processing_qwen3_vl.py).Since
ImagePreprocessorapplies(x * rescale/255 − mean) / stdper channel (swift/Sources/CoreAIShared/Image/ImagePreprocessor.swift), every image reaching the exported vision encoder is normalized in the wrong space:(x/255 − 0.5) / 0.5(x/255 − 0.4815) / 0.2686(R channel; G/B analogous)That is roughly a 1.86× overscale plus a channel-dependent offset on the encoder input. Generation still produces plausible captions (which makes this easy to miss), but the vision-encoder inputs differ from the HF reference pipeline on every pixel, so fine-grained visual fidelity is silently degraded. I have not run a quantitative A/B on the released bundle — the mismatch above is verified from the configs and the preprocessing code paths.
Suggested fix
Set
image_mean=(0.5, 0.5, 0.5),image_std=(0.5, 0.5, 0.5)in the qwen3-vlVLMSpec.Longer term: read the stats from the checkpoint at export time (
AutoImageProcessor.from_pretrained(...).image_mean / image_std) instead of hardcoding them per spec. The CLIP fallback documented inVisionConfig("the most common across VLMs") is a trap for exactly this case — the Qwen processor class default is CLIP, while the Qwen3-VL checkpoints ship 0.5/0.5.macOS / iOS target: any (export-side metadata bug)
Full error output: n/a — no error is raised; this is a silent numerics divergence.