pub struct VisionConfig {Show 23 fields
pub depth: usize,
pub hidden_size: usize,
pub num_heads: usize,
pub patch_size: usize,
pub temporal_patch_size: usize,
pub spatial_merge_size: usize,
pub intermediate_size: usize,
pub out_hidden_size: usize,
pub deepstack_visual_indexes: Vec<usize>,
pub image_pad_token_id: u32,
pub video_pad_token_id: u32,
pub max_pixels: Option<usize>,
pub hidden_act: String,
pub rms_norm_eps: f32,
pub attention_bias: bool,
pub projection_intermediate_size: usize,
pub swiglu_limit: f32,
pub rope_theta: f32,
pub image_mean: [f32; 3],
pub image_std: [f32; 3],
pub min_image_tokens: usize,
pub max_image_tokens: usize,
pub block_major_patches: bool,
}Expand description
Vision encoder configuration for Qwen3-VL models.
Fields§
§depth: usizeNumber of ViT transformer blocks (depth=27).
ViT hidden dimension (1152).
num_heads: usizeNumber of attention heads (16).
patch_size: usizeSpatial patch size in pixels (16).
temporal_patch_size: usizeTemporal patch size: still images are replicated this many times (2).
spatial_merge_size: usize2×2 spatial merge: this many patch-lengths merged into one token (2).
intermediate_size: usizeViT MLP intermediate size (4304).
Projection output dimension = LLM hidden_size (2048).
deepstack_visual_indexes: Vec<usize>Layer indices after which deepstack mergers are applied ([8, 16, 24]).
image_pad_token_id: u32Placeholder token ID that marks where vision embeddings get spliced into the text embedding stream. Qwen3-VL uses 151655; Qwen3.6 uses 248056. When 0 the runtime falls back to the legacy Qwen3-VL value.
video_pad_token_id: u32Placeholder token ID for VIDEO frames, the temporal sibling of
Self::image_pad_token_id. Qwen3.6/3.8 use 248057. A distinct token
is what lets the position builder tell a video item from an image one
in the token stream, which matters because their MRoPE treatment
differs: an image holds T constant across its whole pad run, a video
advances T once per temporal group. When 0 the runtime falls back to
the family default.
max_pixels: Option<usize>Resolved vision AREA bound in pixels: the operator’s
--vision-max-pixels, else the checkpoint’s preprocessor_config.json,
else None.
★ THE SINGLE SOURCE OF TRUTH, and it exists because there used to be
two. The CPU preprocessor clamped every image to 1280px on the long
side while the GPU encoder allocated its buffers for 6400 patches —
exactly 1280×1280 — with nothing in the code connecting them. They
agreed only by coincidence, so raising one on 2026-08-14 made every
image above 1280px fail an H2D copy with CUDA_ERROR_INVALID_VALUE
from deep inside the scheduler.
Both now derive from this field, resolved once at config load, before
the encoder is constructed. None keeps the historical behaviour on
both sides.
Block-MLP activation. "silu" for GLM (a clamped SwiGLU);
"gelu_pytorch_tanh" for the Qwen family’s 2-matrix GELU MLP.
rms_norm_eps: f32Epsilon of every RMSNorm in the tower (GLM: 1e-5). Unused by the Qwen tower, whose norms are LayerNorms at a compiled 1e-6.
attention_bias: boolWhether the ViT’s qkv/proj/MLP Linears carry biases (GLM: true).
projection_intermediate_size: usizeHidden width of GLM’s merger SwiGLU (10240). 0 when the family has no such stage.
swiglu_limit: f32Symmetric clamp applied inside GLM’s SwiGLU, on both the block MLP and the merger (10.0). 0 disables it.
rope_theta: f32Base of the ViT’s own rotary embedding. GLM’s vision_config carries
rope_parameters = {rope_theta: 10000.0, rope_type: "axial"}.
image_mean: [f32; 3]Per-channel normalization the image processor applies. GLM ships
CLIP-style stats; the Qwen family ships SigLIP’s 0.5/0.5/0.5, which is
the default here so an absent preprocessor_config.json is unchanged.
image_std: [f32; 3]§min_image_tokens: usizeDynamic-resolution token budget (processor_config.json). GLM states
its bound in MERGED tokens (16 / 8000) rather than as a pixel area;
min_pixels/max_pixels = tokens * (patch_size * merge_size)^2.
max_image_tokens: usize§block_major_patches: boolPatch tokens are emitted in 2x2-block-major order rather than raster.
GLM’s image processor and its get_vision_position_ids share this
nesting, which is the ONLY reason the tower’s view(-1, 2, 2, C)
downsample reshape is free. A raster-ordered buffer through the same
tower produces a perfectly plausible, wrong embedding — so the order is
a declared property of the checkpoint, not an implementation detail of
whoever writes the loop.
Implementations§
Source§impl VisionConfig
impl VisionConfig
Sourcepub fn merger_input_size(&self) -> usize
pub fn merger_input_size(&self) -> usize
Dimension of the merger input (spatial_merge_size² × hidden_size).
Trait Implementations§
Source§impl Clone for VisionConfig
impl Clone for VisionConfig
Source§fn clone(&self) -> VisionConfig
fn clone(&self) -> VisionConfig
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more