Skip to content

VLM: qwen3-vl exports CLIP normalization stats, but Qwen3-VL checkpoints specify image_mean = image_std = [0.5, 0.5, 0.5] #82

Description

@john-rocky

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions