family qwen3_vl_text
Qwen3's block, queries and keys RMS-normed per head, under a multimodal rotary embedding; on an image prompt the text model adds vision features at the image positions again after blocks 0, 1 and 2, outside the blocks.
One block of the stack. Hover any part for its nnterp name; click to pin the card.
One block of the stack. Hover any part for its nnterp name; click to pin the card.
One block of the stack. Hover any part for its nnterp name; click to pin the card.
identity layers[i].input + self_attn.attention_output + mlp.mlp_output == layer_output
The image's path into the text model, then one block of the vision encoder. Hover any part for its nnterp name; click to pin the card.
[1, patches, vision_hidden_size], split per image by the processor's image_grid_thw. Each image's patches are in merge-block order (each 2 × 2 block the merger folds is consecutive), and on Qwen2.5-VL in window order inside the vision encoder.pos_embed, resampled to each image's grid, after patch_embed.vision.norm.identity vision.layers[i].input + self_attn.attention_output + mlp.mlp_output == layer_output
The image's path into the text model, then one block of the vision encoder. Hover any part for its nnterp name; click to pin the card.
[1, patches, vision_hidden_size], split per image by the processor's image_grid_thw. Each image's patches are in merge-block order (each 2 × 2 block the merger folds is consecutive), and on Qwen2.5-VL in window order inside the vision encoder.pos_embed, resampled to each image's grid, after patch_embed.vision.norm.identity vision.layers[i].input + self_attn.attention_output + mlp.mlp_output == layer_output
⚠︎ marks a value this checkpoint has only under some load: hover it for the condition.
the vision encoder · Qwen ViT
⚠︎ marks a value this checkpoint has only under some load: hover it for the condition.
the vision encoder · Qwen ViT
⚠︎ marks a value this checkpoint has only under some load: hover it for the condition.
the vision encoder · Qwen ViT
What print(model) shows on Qwen/Qwen3-VL-2B-Instruct: the native modules, standard names and every standard value with its layout.
Qwen3VLForConditionalGeneration(
(lm_head): Linear(in_features=2048, out_features=151936, bias=False)
(generator): Generator(
(streamer): Streamer()
)
(embed_tokens): Embedding(151936, 2048)
(layers): ModuleList(
(0-27): 28 x Qwen3VLTextDecoderLayer(
(self_attn): Qwen3VLTextAttention(
(q_proj): Linear(in_features=2048, out_features=2048, bias=False)
(k_proj): Linear(in_features=2048, out_features=1024, bias=False)
(v_proj): Linear(in_features=2048, out_features=1024, bias=False)
(o_proj): Linear(in_features=2048, out_features=2048, bias=False)
(q_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(k_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_output) -> Residual [batch seq hidden_size]: What the attention adds to the residual stream
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
)
(mlp): Qwen3VLTextMLP(
(gate_proj): Linear(in_features=2048, out_features=6144, bias=False)
(up_proj): Linear(in_features=2048, out_features=6144, bias=False)
(down_proj): Linear(in_features=6144, out_features=2048, bias=False)
(act_fn): SiLUActivation()
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives: act(gate)·up on a gated MLP, the activation itself otherwise
(mlp_output) -> Residual [batch seq hidden_size]: What the MLP adds to the residual stream
)
(input_layernorm): Qwen3VLTextRMSNorm((2048,), eps=1e-06)
(post_attention_layernorm): Qwen3VLTextRMSNorm((2048,), eps=1e-06)
(deepstack_output) -> ImageFeatures [image_tokens hidden_size]: The deepstack image features the text model adds at the image tokens after this block
(layer_input) -> Residual [batch seq hidden_size]: The residual stream entering the block
(attention_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the attention
(layer_mid) -> Residual [batch seq hidden_size]: The residual stream after the attention add
(mlp_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the MLP
(mlp_input) -> Residual [batch seq hidden_size]: What the MLP is called with
(layer_output) -> Residual [batch seq hidden_size]: The residual stream leaving the block, a tensor even when the block returns a tuple
)
)
(norm): Qwen3VLTextRMSNorm((2048,), eps=1e-06)
(vision): Qwen3VLVisionModel(
(patch_embed): Qwen3VLVisionPatchEmbed(
(proj): Conv3d(3, 1024, kernel_size=(2, 16, 16), stride=(2, 16, 16))
)
(pos_embed): Embedding(2304, 1024)
(rotary_pos_emb): Qwen3VLVisionRotaryEmbedding()
(layers/blocks): ModuleList(
(0-23): 24 x Qwen3VLVisionBlock(
(input_layernorm/norm1): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(post_attention_layernorm/norm2): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(self_attn/attn): Qwen3VLVisionAttention(
(qkv): Linear(in_features=1024, out_features=3072, bias=True)
(o_proj/proj): Linear(in_features=1024, out_features=1024, bias=True)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
(attention_output) -> Patches [images patches vision_hidden_size]: What the attention adds to the tower's stream
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
)
(mlp): Qwen3VLVisionMLP(
(linear_fc1): Linear(in_features=1024, out_features=4096, bias=True)
(down_proj/linear_fc2): Linear(in_features=4096, out_features=1024, bias=True)
(act_fn): GELUTanh()
(mlp_output) -> Patches [images patches vision_hidden_size]: What the MLP adds to the tower's stream
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives
)
(layer_input) -> Patches [images patches vision_hidden_size]: The tower's stream entering the block
(attention_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the attention
(layer_mid) -> Patches [images patches vision_hidden_size]: The tower's stream after the attention add
(mlp_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the MLP
(mlp_input) -> Patches [images patches vision_hidden_size]: What the MLP is called with
(layer_output) -> Patches [images patches vision_hidden_size]: The tower's stream leaving the block
)
)
(merger): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4096, out_features=4096, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4096, out_features=2048, bias=True)
)
(deepstack_merger_list): ModuleList(
(0-2): 3 x Qwen3VLVisionPatchMerger(
(norm): LayerNorm((4096,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4096, out_features=4096, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4096, out_features=2048, bias=True)
)
)
(image_token_mask) -> ImageTokenMask [batch seq]: Which positions hold image tokens: input_ids == the config's image_token_id; read-only
(patch_embeddings) -> Patches [images patches vision_hidden_size]: The patch embedding's output, one row per patch
(tower_output) -> Patches [images patches vision_hidden_size]: The last block's stream after the tower's final norm, before any pooling or adapter
(image_features) -> ImageFeatures [image_tokens hidden_size]: The image features the text model receives at the image tokens, flat over them
)
(projector): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4096, out_features=4096, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4096, out_features=2048, bias=True)
)
(logits) -> Logits [batch seq vocab_size]: The logits the model returns, after anything it does past lm_head (a softcap, a scale)
(token_embeddings) -> Residual [batch seq hidden_size]: The embedding module's output; layers[0].input is what enters the first block
(final_norm_output) -> Residual [batch seq hidden_size]: The residual stream after the final norm, what lm_head reads
(lm_head_output) -> Logits [batch seq vocab_size]: The head's logits before anything the model does past lm_head (a softcap, a scale)
(next_token_probs) -> NextTokenProbs [batch vocab_size]: The next-token distribution at the last position; derived, read-only
(input_ids) -> Tokens [batch seq]: The token ids the model was called with
(attention_mask) -> Tokens [batch seq]: The attention mask the model was called with; zeros are padding
(input_size): [batch, seq] of the current call; read-only
)What print(model) shows on Qwen/Qwen3-VL-2B-Thinking: the native modules, standard names and every standard value with its layout.
Qwen3VLForConditionalGeneration(
(lm_head): Linear(in_features=2048, out_features=151936, bias=False)
(generator): Generator(
(streamer): Streamer()
)
(embed_tokens): Embedding(151936, 2048)
(layers): ModuleList(
(0-27): 28 x Qwen3VLTextDecoderLayer(
(self_attn): Qwen3VLTextAttention(
(q_proj): Linear(in_features=2048, out_features=2048, bias=False)
(k_proj): Linear(in_features=2048, out_features=1024, bias=False)
(v_proj): Linear(in_features=2048, out_features=1024, bias=False)
(o_proj): Linear(in_features=2048, out_features=2048, bias=False)
(q_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(k_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_output) -> Residual [batch seq hidden_size]: What the attention adds to the residual stream
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
)
(mlp): Qwen3VLTextMLP(
(gate_proj): Linear(in_features=2048, out_features=6144, bias=False)
(up_proj): Linear(in_features=2048, out_features=6144, bias=False)
(down_proj): Linear(in_features=6144, out_features=2048, bias=False)
(act_fn): SiLUActivation()
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives: act(gate)·up on a gated MLP, the activation itself otherwise
(mlp_output) -> Residual [batch seq hidden_size]: What the MLP adds to the residual stream
)
(input_layernorm): Qwen3VLTextRMSNorm((2048,), eps=1e-06)
(post_attention_layernorm): Qwen3VLTextRMSNorm((2048,), eps=1e-06)
(deepstack_output) -> ImageFeatures [image_tokens hidden_size]: The deepstack image features the text model adds at the image tokens after this block
(layer_input) -> Residual [batch seq hidden_size]: The residual stream entering the block
(attention_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the attention
(layer_mid) -> Residual [batch seq hidden_size]: The residual stream after the attention add
(mlp_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the MLP
(mlp_input) -> Residual [batch seq hidden_size]: What the MLP is called with
(layer_output) -> Residual [batch seq hidden_size]: The residual stream leaving the block, a tensor even when the block returns a tuple
)
)
(norm): Qwen3VLTextRMSNorm((2048,), eps=1e-06)
(vision): Qwen3VLVisionModel(
(patch_embed): Qwen3VLVisionPatchEmbed(
(proj): Conv3d(3, 1024, kernel_size=(2, 16, 16), stride=(2, 16, 16))
)
(pos_embed): Embedding(2304, 1024)
(rotary_pos_emb): Qwen3VLVisionRotaryEmbedding()
(layers/blocks): ModuleList(
(0-23): 24 x Qwen3VLVisionBlock(
(input_layernorm/norm1): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(post_attention_layernorm/norm2): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(self_attn/attn): Qwen3VLVisionAttention(
(qkv): Linear(in_features=1024, out_features=3072, bias=True)
(o_proj/proj): Linear(in_features=1024, out_features=1024, bias=True)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
(attention_output) -> Patches [images patches vision_hidden_size]: What the attention adds to the tower's stream
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
)
(mlp): Qwen3VLVisionMLP(
(linear_fc1): Linear(in_features=1024, out_features=4096, bias=True)
(down_proj/linear_fc2): Linear(in_features=4096, out_features=1024, bias=True)
(act_fn): GELUTanh()
(mlp_output) -> Patches [images patches vision_hidden_size]: What the MLP adds to the tower's stream
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives
)
(layer_input) -> Patches [images patches vision_hidden_size]: The tower's stream entering the block
(attention_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the attention
(layer_mid) -> Patches [images patches vision_hidden_size]: The tower's stream after the attention add
(mlp_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the MLP
(mlp_input) -> Patches [images patches vision_hidden_size]: What the MLP is called with
(layer_output) -> Patches [images patches vision_hidden_size]: The tower's stream leaving the block
)
)
(merger): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4096, out_features=4096, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4096, out_features=2048, bias=True)
)
(deepstack_merger_list): ModuleList(
(0-2): 3 x Qwen3VLVisionPatchMerger(
(norm): LayerNorm((4096,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4096, out_features=4096, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4096, out_features=2048, bias=True)
)
)
(image_token_mask) -> ImageTokenMask [batch seq]: Which positions hold image tokens: input_ids == the config's image_token_id; read-only
(patch_embeddings) -> Patches [images patches vision_hidden_size]: The patch embedding's output, one row per patch
(tower_output) -> Patches [images patches vision_hidden_size]: The last block's stream after the tower's final norm, before any pooling or adapter
(image_features) -> ImageFeatures [image_tokens hidden_size]: The image features the text model receives at the image tokens, flat over them
)
(projector): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4096, out_features=4096, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4096, out_features=2048, bias=True)
)
(logits) -> Logits [batch seq vocab_size]: The logits the model returns, after anything it does past lm_head (a softcap, a scale)
(token_embeddings) -> Residual [batch seq hidden_size]: The embedding module's output; layers[0].input is what enters the first block
(final_norm_output) -> Residual [batch seq hidden_size]: The residual stream after the final norm, what lm_head reads
(lm_head_output) -> Logits [batch seq vocab_size]: The head's logits before anything the model does past lm_head (a softcap, a scale)
(next_token_probs) -> NextTokenProbs [batch vocab_size]: The next-token distribution at the last position; derived, read-only
(input_ids) -> Tokens [batch seq]: The token ids the model was called with
(attention_mask) -> Tokens [batch seq]: The attention mask the model was called with; zeros are padding
(input_size): [batch, seq] of the current call; read-only
)What print(model) shows on Qwen/Qwen3-VL-4B-Instruct: the native modules, standard names and every standard value with its layout.
Qwen3VLForConditionalGeneration(
(lm_head): Linear(in_features=2560, out_features=151936, bias=False)
(generator): Generator(
(streamer): Streamer()
)
(embed_tokens): Embedding(151936, 2560)
(layers): ModuleList(
(0-35): 36 x Qwen3VLTextDecoderLayer(
(self_attn): Qwen3VLTextAttention(
(q_proj): Linear(in_features=2560, out_features=4096, bias=False)
(k_proj): Linear(in_features=2560, out_features=1024, bias=False)
(v_proj): Linear(in_features=2560, out_features=1024, bias=False)
(o_proj): Linear(in_features=4096, out_features=2560, bias=False)
(q_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(k_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_output) -> Residual [batch seq hidden_size]: What the attention adds to the residual stream
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
)
(mlp): Qwen3VLTextMLP(
(gate_proj): Linear(in_features=2560, out_features=9728, bias=False)
(up_proj): Linear(in_features=2560, out_features=9728, bias=False)
(down_proj): Linear(in_features=9728, out_features=2560, bias=False)
(act_fn): SiLUActivation()
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives: act(gate)·up on a gated MLP, the activation itself otherwise
(mlp_output) -> Residual [batch seq hidden_size]: What the MLP adds to the residual stream
)
(input_layernorm): Qwen3VLTextRMSNorm((2560,), eps=1e-06)
(post_attention_layernorm): Qwen3VLTextRMSNorm((2560,), eps=1e-06)
(deepstack_output) -> ImageFeatures [image_tokens hidden_size]: The deepstack image features the text model adds at the image tokens after this block
(layer_input) -> Residual [batch seq hidden_size]: The residual stream entering the block
(attention_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the attention
(layer_mid) -> Residual [batch seq hidden_size]: The residual stream after the attention add
(mlp_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the MLP
(mlp_input) -> Residual [batch seq hidden_size]: What the MLP is called with
(layer_output) -> Residual [batch seq hidden_size]: The residual stream leaving the block, a tensor even when the block returns a tuple
)
)
(norm): Qwen3VLTextRMSNorm((2560,), eps=1e-06)
(vision): Qwen3VLVisionModel(
(patch_embed): Qwen3VLVisionPatchEmbed(
(proj): Conv3d(3, 1024, kernel_size=(2, 16, 16), stride=(2, 16, 16))
)
(pos_embed): Embedding(2304, 1024)
(rotary_pos_emb): Qwen3VLVisionRotaryEmbedding()
(layers/blocks): ModuleList(
(0-23): 24 x Qwen3VLVisionBlock(
(input_layernorm/norm1): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(post_attention_layernorm/norm2): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(self_attn/attn): Qwen3VLVisionAttention(
(qkv): Linear(in_features=1024, out_features=3072, bias=True)
(o_proj/proj): Linear(in_features=1024, out_features=1024, bias=True)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
(attention_output) -> Patches [images patches vision_hidden_size]: What the attention adds to the tower's stream
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
)
(mlp): Qwen3VLVisionMLP(
(linear_fc1): Linear(in_features=1024, out_features=4096, bias=True)
(down_proj/linear_fc2): Linear(in_features=4096, out_features=1024, bias=True)
(act_fn): GELUTanh()
(mlp_output) -> Patches [images patches vision_hidden_size]: What the MLP adds to the tower's stream
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives
)
(layer_input) -> Patches [images patches vision_hidden_size]: The tower's stream entering the block
(attention_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the attention
(layer_mid) -> Patches [images patches vision_hidden_size]: The tower's stream after the attention add
(mlp_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the MLP
(mlp_input) -> Patches [images patches vision_hidden_size]: What the MLP is called with
(layer_output) -> Patches [images patches vision_hidden_size]: The tower's stream leaving the block
)
)
(merger): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4096, out_features=4096, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4096, out_features=2560, bias=True)
)
(deepstack_merger_list): ModuleList(
(0-2): 3 x Qwen3VLVisionPatchMerger(
(norm): LayerNorm((4096,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4096, out_features=4096, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4096, out_features=2560, bias=True)
)
)
(image_token_mask) -> ImageTokenMask [batch seq]: Which positions hold image tokens: input_ids == the config's image_token_id; read-only
(patch_embeddings) -> Patches [images patches vision_hidden_size]: The patch embedding's output, one row per patch
(tower_output) -> Patches [images patches vision_hidden_size]: The last block's stream after the tower's final norm, before any pooling or adapter
(image_features) -> ImageFeatures [image_tokens hidden_size]: The image features the text model receives at the image tokens, flat over them
)
(projector): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4096, out_features=4096, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4096, out_features=2560, bias=True)
)
(logits) -> Logits [batch seq vocab_size]: The logits the model returns, after anything it does past lm_head (a softcap, a scale)
(token_embeddings) -> Residual [batch seq hidden_size]: The embedding module's output; layers[0].input is what enters the first block
(final_norm_output) -> Residual [batch seq hidden_size]: The residual stream after the final norm, what lm_head reads
(lm_head_output) -> Logits [batch seq vocab_size]: The head's logits before anything the model does past lm_head (a softcap, a scale)
(next_token_probs) -> NextTokenProbs [batch vocab_size]: The next-token distribution at the last position; derived, read-only
(input_ids) -> Tokens [batch seq]: The token ids the model was called with
(attention_mask) -> Tokens [batch seq]: The attention mask the model was called with; zeros are padding
(input_size): [batch, seq] of the current call; read-only
)What print(model) shows on Qwen/Qwen3-VL-4B-Thinking: the native modules, standard names and every standard value with its layout.
Qwen3VLForConditionalGeneration(
(lm_head): Linear(in_features=2560, out_features=151936, bias=False)
(generator): Generator(
(streamer): Streamer()
)
(embed_tokens): Embedding(151936, 2560)
(layers): ModuleList(
(0-35): 36 x Qwen3VLTextDecoderLayer(
(self_attn): Qwen3VLTextAttention(
(q_proj): Linear(in_features=2560, out_features=4096, bias=False)
(k_proj): Linear(in_features=2560, out_features=1024, bias=False)
(v_proj): Linear(in_features=2560, out_features=1024, bias=False)
(o_proj): Linear(in_features=4096, out_features=2560, bias=False)
(q_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(k_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_output) -> Residual [batch seq hidden_size]: What the attention adds to the residual stream
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
)
(mlp): Qwen3VLTextMLP(
(gate_proj): Linear(in_features=2560, out_features=9728, bias=False)
(up_proj): Linear(in_features=2560, out_features=9728, bias=False)
(down_proj): Linear(in_features=9728, out_features=2560, bias=False)
(act_fn): SiLUActivation()
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives: act(gate)·up on a gated MLP, the activation itself otherwise
(mlp_output) -> Residual [batch seq hidden_size]: What the MLP adds to the residual stream
)
(input_layernorm): Qwen3VLTextRMSNorm((2560,), eps=1e-06)
(post_attention_layernorm): Qwen3VLTextRMSNorm((2560,), eps=1e-06)
(deepstack_output) -> ImageFeatures [image_tokens hidden_size]: The deepstack image features the text model adds at the image tokens after this block
(layer_input) -> Residual [batch seq hidden_size]: The residual stream entering the block
(attention_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the attention
(layer_mid) -> Residual [batch seq hidden_size]: The residual stream after the attention add
(mlp_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the MLP
(mlp_input) -> Residual [batch seq hidden_size]: What the MLP is called with
(layer_output) -> Residual [batch seq hidden_size]: The residual stream leaving the block, a tensor even when the block returns a tuple
)
)
(norm): Qwen3VLTextRMSNorm((2560,), eps=1e-06)
(vision): Qwen3VLVisionModel(
(patch_embed): Qwen3VLVisionPatchEmbed(
(proj): Conv3d(3, 1024, kernel_size=(2, 16, 16), stride=(2, 16, 16))
)
(pos_embed): Embedding(2304, 1024)
(rotary_pos_emb): Qwen3VLVisionRotaryEmbedding()
(layers/blocks): ModuleList(
(0-23): 24 x Qwen3VLVisionBlock(
(input_layernorm/norm1): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(post_attention_layernorm/norm2): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(self_attn/attn): Qwen3VLVisionAttention(
(qkv): Linear(in_features=1024, out_features=3072, bias=True)
(o_proj/proj): Linear(in_features=1024, out_features=1024, bias=True)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
(attention_output) -> Patches [images patches vision_hidden_size]: What the attention adds to the tower's stream
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
)
(mlp): Qwen3VLVisionMLP(
(linear_fc1): Linear(in_features=1024, out_features=4096, bias=True)
(down_proj/linear_fc2): Linear(in_features=4096, out_features=1024, bias=True)
(act_fn): GELUTanh()
(mlp_output) -> Patches [images patches vision_hidden_size]: What the MLP adds to the tower's stream
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives
)
(layer_input) -> Patches [images patches vision_hidden_size]: The tower's stream entering the block
(attention_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the attention
(layer_mid) -> Patches [images patches vision_hidden_size]: The tower's stream after the attention add
(mlp_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the MLP
(mlp_input) -> Patches [images patches vision_hidden_size]: What the MLP is called with
(layer_output) -> Patches [images patches vision_hidden_size]: The tower's stream leaving the block
)
)
(merger): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4096, out_features=4096, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4096, out_features=2560, bias=True)
)
(deepstack_merger_list): ModuleList(
(0-2): 3 x Qwen3VLVisionPatchMerger(
(norm): LayerNorm((4096,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4096, out_features=4096, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4096, out_features=2560, bias=True)
)
)
(image_token_mask) -> ImageTokenMask [batch seq]: Which positions hold image tokens: input_ids == the config's image_token_id; read-only
(patch_embeddings) -> Patches [images patches vision_hidden_size]: The patch embedding's output, one row per patch
(tower_output) -> Patches [images patches vision_hidden_size]: The last block's stream after the tower's final norm, before any pooling or adapter
(image_features) -> ImageFeatures [image_tokens hidden_size]: The image features the text model receives at the image tokens, flat over them
)
(projector): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1024,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4096, out_features=4096, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4096, out_features=2560, bias=True)
)
(logits) -> Logits [batch seq vocab_size]: The logits the model returns, after anything it does past lm_head (a softcap, a scale)
(token_embeddings) -> Residual [batch seq hidden_size]: The embedding module's output; layers[0].input is what enters the first block
(final_norm_output) -> Residual [batch seq hidden_size]: The residual stream after the final norm, what lm_head reads
(lm_head_output) -> Logits [batch seq vocab_size]: The head's logits before anything the model does past lm_head (a softcap, a scale)
(next_token_probs) -> NextTokenProbs [batch vocab_size]: The next-token distribution at the last position; derived, read-only
(input_ids) -> Tokens [batch seq]: The token ids the model was called with
(attention_mask) -> Tokens [batch seq]: The attention mask the model was called with; zeros are padding
(input_size): [batch, seq] of the current call; read-only
)What print(model) shows on Qwen/Qwen3-VL-8B-Instruct: the native modules, standard names and every standard value with its layout.
Qwen3VLForConditionalGeneration(
(lm_head): Linear(in_features=4096, out_features=151936, bias=False)
(generator): Generator(
(streamer): Streamer()
)
(embed_tokens): Embedding(151936, 4096)
(layers): ModuleList(
(0-35): 36 x Qwen3VLTextDecoderLayer(
(self_attn): Qwen3VLTextAttention(
(q_proj): Linear(in_features=4096, out_features=4096, bias=False)
(k_proj): Linear(in_features=4096, out_features=1024, bias=False)
(v_proj): Linear(in_features=4096, out_features=1024, bias=False)
(o_proj): Linear(in_features=4096, out_features=4096, bias=False)
(q_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(k_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_output) -> Residual [batch seq hidden_size]: What the attention adds to the residual stream
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
)
(mlp): Qwen3VLTextMLP(
(gate_proj): Linear(in_features=4096, out_features=12288, bias=False)
(up_proj): Linear(in_features=4096, out_features=12288, bias=False)
(down_proj): Linear(in_features=12288, out_features=4096, bias=False)
(act_fn): SiLUActivation()
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives: act(gate)·up on a gated MLP, the activation itself otherwise
(mlp_output) -> Residual [batch seq hidden_size]: What the MLP adds to the residual stream
)
(input_layernorm): Qwen3VLTextRMSNorm((4096,), eps=1e-06)
(post_attention_layernorm): Qwen3VLTextRMSNorm((4096,), eps=1e-06)
(deepstack_output) -> ImageFeatures [image_tokens hidden_size]: The deepstack image features the text model adds at the image tokens after this block
(layer_input) -> Residual [batch seq hidden_size]: The residual stream entering the block
(attention_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the attention
(layer_mid) -> Residual [batch seq hidden_size]: The residual stream after the attention add
(mlp_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the MLP
(mlp_input) -> Residual [batch seq hidden_size]: What the MLP is called with
(layer_output) -> Residual [batch seq hidden_size]: The residual stream leaving the block, a tensor even when the block returns a tuple
)
)
(norm): Qwen3VLTextRMSNorm((4096,), eps=1e-06)
(vision): Qwen3VLVisionModel(
(patch_embed): Qwen3VLVisionPatchEmbed(
(proj): Conv3d(3, 1152, kernel_size=(2, 16, 16), stride=(2, 16, 16))
)
(pos_embed): Embedding(2304, 1152)
(rotary_pos_emb): Qwen3VLVisionRotaryEmbedding()
(layers/blocks): ModuleList(
(0-26): 27 x Qwen3VLVisionBlock(
(input_layernorm/norm1): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(post_attention_layernorm/norm2): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(self_attn/attn): Qwen3VLVisionAttention(
(qkv): Linear(in_features=1152, out_features=3456, bias=True)
(o_proj/proj): Linear(in_features=1152, out_features=1152, bias=True)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
(attention_output) -> Patches [images patches vision_hidden_size]: What the attention adds to the tower's stream
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
)
(mlp): Qwen3VLVisionMLP(
(linear_fc1): Linear(in_features=1152, out_features=4304, bias=True)
(down_proj/linear_fc2): Linear(in_features=4304, out_features=1152, bias=True)
(act_fn): GELUTanh()
(mlp_output) -> Patches [images patches vision_hidden_size]: What the MLP adds to the tower's stream
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives
)
(layer_input) -> Patches [images patches vision_hidden_size]: The tower's stream entering the block
(attention_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the attention
(layer_mid) -> Patches [images patches vision_hidden_size]: The tower's stream after the attention add
(mlp_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the MLP
(mlp_input) -> Patches [images patches vision_hidden_size]: What the MLP is called with
(layer_output) -> Patches [images patches vision_hidden_size]: The tower's stream leaving the block
)
)
(merger): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4608, out_features=4608, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4608, out_features=4096, bias=True)
)
(deepstack_merger_list): ModuleList(
(0-2): 3 x Qwen3VLVisionPatchMerger(
(norm): LayerNorm((4608,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4608, out_features=4608, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4608, out_features=4096, bias=True)
)
)
(image_token_mask) -> ImageTokenMask [batch seq]: Which positions hold image tokens: input_ids == the config's image_token_id; read-only
(patch_embeddings) -> Patches [images patches vision_hidden_size]: The patch embedding's output, one row per patch
(tower_output) -> Patches [images patches vision_hidden_size]: The last block's stream after the tower's final norm, before any pooling or adapter
(image_features) -> ImageFeatures [image_tokens hidden_size]: The image features the text model receives at the image tokens, flat over them
)
(projector): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4608, out_features=4608, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4608, out_features=4096, bias=True)
)
(logits) -> Logits [batch seq vocab_size]: The logits the model returns, after anything it does past lm_head (a softcap, a scale)
(token_embeddings) -> Residual [batch seq hidden_size]: The embedding module's output; layers[0].input is what enters the first block
(final_norm_output) -> Residual [batch seq hidden_size]: The residual stream after the final norm, what lm_head reads
(lm_head_output) -> Logits [batch seq vocab_size]: The head's logits before anything the model does past lm_head (a softcap, a scale)
(next_token_probs) -> NextTokenProbs [batch vocab_size]: The next-token distribution at the last position; derived, read-only
(input_ids) -> Tokens [batch seq]: The token ids the model was called with
(attention_mask) -> Tokens [batch seq]: The attention mask the model was called with; zeros are padding
(input_size): [batch, seq] of the current call; read-only
)What print(model) shows on Qwen/Qwen3-VL-8B-Thinking: the native modules, standard names and every standard value with its layout.
Qwen3VLForConditionalGeneration(
(lm_head): Linear(in_features=4096, out_features=151936, bias=False)
(generator): Generator(
(streamer): Streamer()
)
(embed_tokens): Embedding(151936, 4096)
(layers): ModuleList(
(0-35): 36 x Qwen3VLTextDecoderLayer(
(self_attn): Qwen3VLTextAttention(
(q_proj): Linear(in_features=4096, out_features=4096, bias=False)
(k_proj): Linear(in_features=4096, out_features=1024, bias=False)
(v_proj): Linear(in_features=4096, out_features=1024, bias=False)
(o_proj): Linear(in_features=4096, out_features=4096, bias=False)
(q_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(k_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_output) -> Residual [batch seq hidden_size]: What the attention adds to the residual stream
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
)
(mlp): Qwen3VLTextMLP(
(gate_proj): Linear(in_features=4096, out_features=12288, bias=False)
(up_proj): Linear(in_features=4096, out_features=12288, bias=False)
(down_proj): Linear(in_features=12288, out_features=4096, bias=False)
(act_fn): SiLUActivation()
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives: act(gate)·up on a gated MLP, the activation itself otherwise
(mlp_output) -> Residual [batch seq hidden_size]: What the MLP adds to the residual stream
)
(input_layernorm): Qwen3VLTextRMSNorm((4096,), eps=1e-06)
(post_attention_layernorm): Qwen3VLTextRMSNorm((4096,), eps=1e-06)
(deepstack_output) -> ImageFeatures [image_tokens hidden_size]: The deepstack image features the text model adds at the image tokens after this block
(layer_input) -> Residual [batch seq hidden_size]: The residual stream entering the block
(attention_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the attention
(layer_mid) -> Residual [batch seq hidden_size]: The residual stream after the attention add
(mlp_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the MLP
(mlp_input) -> Residual [batch seq hidden_size]: What the MLP is called with
(layer_output) -> Residual [batch seq hidden_size]: The residual stream leaving the block, a tensor even when the block returns a tuple
)
)
(norm): Qwen3VLTextRMSNorm((4096,), eps=1e-06)
(vision): Qwen3VLVisionModel(
(patch_embed): Qwen3VLVisionPatchEmbed(
(proj): Conv3d(3, 1152, kernel_size=(2, 16, 16), stride=(2, 16, 16))
)
(pos_embed): Embedding(2304, 1152)
(rotary_pos_emb): Qwen3VLVisionRotaryEmbedding()
(layers/blocks): ModuleList(
(0-26): 27 x Qwen3VLVisionBlock(
(input_layernorm/norm1): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(post_attention_layernorm/norm2): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(self_attn/attn): Qwen3VLVisionAttention(
(qkv): Linear(in_features=1152, out_features=3456, bias=True)
(o_proj/proj): Linear(in_features=1152, out_features=1152, bias=True)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
(attention_output) -> Patches [images patches vision_hidden_size]: What the attention adds to the tower's stream
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
)
(mlp): Qwen3VLVisionMLP(
(linear_fc1): Linear(in_features=1152, out_features=4304, bias=True)
(down_proj/linear_fc2): Linear(in_features=4304, out_features=1152, bias=True)
(act_fn): GELUTanh()
(mlp_output) -> Patches [images patches vision_hidden_size]: What the MLP adds to the tower's stream
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives
)
(layer_input) -> Patches [images patches vision_hidden_size]: The tower's stream entering the block
(attention_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the attention
(layer_mid) -> Patches [images patches vision_hidden_size]: The tower's stream after the attention add
(mlp_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the MLP
(mlp_input) -> Patches [images patches vision_hidden_size]: What the MLP is called with
(layer_output) -> Patches [images patches vision_hidden_size]: The tower's stream leaving the block
)
)
(merger): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4608, out_features=4608, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4608, out_features=4096, bias=True)
)
(deepstack_merger_list): ModuleList(
(0-2): 3 x Qwen3VLVisionPatchMerger(
(norm): LayerNorm((4608,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4608, out_features=4608, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4608, out_features=4096, bias=True)
)
)
(image_token_mask) -> ImageTokenMask [batch seq]: Which positions hold image tokens: input_ids == the config's image_token_id; read-only
(patch_embeddings) -> Patches [images patches vision_hidden_size]: The patch embedding's output, one row per patch
(tower_output) -> Patches [images patches vision_hidden_size]: The last block's stream after the tower's final norm, before any pooling or adapter
(image_features) -> ImageFeatures [image_tokens hidden_size]: The image features the text model receives at the image tokens, flat over them
)
(projector): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4608, out_features=4608, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4608, out_features=4096, bias=True)
)
(logits) -> Logits [batch seq vocab_size]: The logits the model returns, after anything it does past lm_head (a softcap, a scale)
(token_embeddings) -> Residual [batch seq hidden_size]: The embedding module's output; layers[0].input is what enters the first block
(final_norm_output) -> Residual [batch seq hidden_size]: The residual stream after the final norm, what lm_head reads
(lm_head_output) -> Logits [batch seq vocab_size]: The head's logits before anything the model does past lm_head (a softcap, a scale)
(next_token_probs) -> NextTokenProbs [batch vocab_size]: The next-token distribution at the last position; derived, read-only
(input_ids) -> Tokens [batch seq]: The token ids the model was called with
(attention_mask) -> Tokens [batch seq]: The attention mask the model was called with; zeros are padding
(input_size): [batch, seq] of the current call; read-only
)What print(model) shows on Qwen/Qwen3-VL-32B-Instruct: the native modules, standard names and every standard value with its layout.
Qwen3VLForConditionalGeneration(
(lm_head): Linear(in_features=5120, out_features=151936, bias=False)
(generator): Generator(
(streamer): Streamer()
)
(embed_tokens): Embedding(151936, 5120)
(layers): ModuleList(
(0-63): 64 x Qwen3VLTextDecoderLayer(
(self_attn): Qwen3VLTextAttention(
(q_proj): Linear(in_features=5120, out_features=8192, bias=False)
(k_proj): Linear(in_features=5120, out_features=1024, bias=False)
(v_proj): Linear(in_features=5120, out_features=1024, bias=False)
(o_proj): Linear(in_features=8192, out_features=5120, bias=False)
(q_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(k_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_output) -> Residual [batch seq hidden_size]: What the attention adds to the residual stream
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
)
(mlp): Qwen3VLTextMLP(
(gate_proj): Linear(in_features=5120, out_features=25600, bias=False)
(up_proj): Linear(in_features=5120, out_features=25600, bias=False)
(down_proj): Linear(in_features=25600, out_features=5120, bias=False)
(act_fn): SiLUActivation()
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives: act(gate)·up on a gated MLP, the activation itself otherwise
(mlp_output) -> Residual [batch seq hidden_size]: What the MLP adds to the residual stream
)
(input_layernorm): Qwen3VLTextRMSNorm((5120,), eps=1e-06)
(post_attention_layernorm): Qwen3VLTextRMSNorm((5120,), eps=1e-06)
(deepstack_output) -> ImageFeatures [image_tokens hidden_size]: The deepstack image features the text model adds at the image tokens after this block
(layer_input) -> Residual [batch seq hidden_size]: The residual stream entering the block
(attention_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the attention
(layer_mid) -> Residual [batch seq hidden_size]: The residual stream after the attention add
(mlp_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the MLP
(mlp_input) -> Residual [batch seq hidden_size]: What the MLP is called with
(layer_output) -> Residual [batch seq hidden_size]: The residual stream leaving the block, a tensor even when the block returns a tuple
)
)
(norm): Qwen3VLTextRMSNorm((5120,), eps=1e-06)
(vision): Qwen3VLVisionModel(
(patch_embed): Qwen3VLVisionPatchEmbed(
(proj): Conv3d(3, 1152, kernel_size=(2, 16, 16), stride=(2, 16, 16))
)
(pos_embed): Embedding(2304, 1152)
(rotary_pos_emb): Qwen3VLVisionRotaryEmbedding()
(layers/blocks): ModuleList(
(0-26): 27 x Qwen3VLVisionBlock(
(input_layernorm/norm1): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(post_attention_layernorm/norm2): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(self_attn/attn): Qwen3VLVisionAttention(
(qkv): Linear(in_features=1152, out_features=3456, bias=True)
(o_proj/proj): Linear(in_features=1152, out_features=1152, bias=True)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
(attention_output) -> Patches [images patches vision_hidden_size]: What the attention adds to the tower's stream
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
)
(mlp): Qwen3VLVisionMLP(
(linear_fc1): Linear(in_features=1152, out_features=4304, bias=True)
(down_proj/linear_fc2): Linear(in_features=4304, out_features=1152, bias=True)
(act_fn): GELUTanh()
(mlp_output) -> Patches [images patches vision_hidden_size]: What the MLP adds to the tower's stream
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives
)
(layer_input) -> Patches [images patches vision_hidden_size]: The tower's stream entering the block
(attention_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the attention
(layer_mid) -> Patches [images patches vision_hidden_size]: The tower's stream after the attention add
(mlp_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the MLP
(mlp_input) -> Patches [images patches vision_hidden_size]: What the MLP is called with
(layer_output) -> Patches [images patches vision_hidden_size]: The tower's stream leaving the block
)
)
(merger): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4608, out_features=4608, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4608, out_features=5120, bias=True)
)
(deepstack_merger_list): ModuleList(
(0-2): 3 x Qwen3VLVisionPatchMerger(
(norm): LayerNorm((4608,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4608, out_features=4608, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4608, out_features=5120, bias=True)
)
)
(image_token_mask) -> ImageTokenMask [batch seq]: Which positions hold image tokens: input_ids == the config's image_token_id; read-only
(patch_embeddings) -> Patches [images patches vision_hidden_size]: The patch embedding's output, one row per patch
(tower_output) -> Patches [images patches vision_hidden_size]: The last block's stream after the tower's final norm, before any pooling or adapter
(image_features) -> ImageFeatures [image_tokens hidden_size]: The image features the text model receives at the image tokens, flat over them
)
(projector): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4608, out_features=4608, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4608, out_features=5120, bias=True)
)
(logits) -> Logits [batch seq vocab_size]: The logits the model returns, after anything it does past lm_head (a softcap, a scale)
(token_embeddings) -> Residual [batch seq hidden_size]: The embedding module's output; layers[0].input is what enters the first block
(final_norm_output) -> Residual [batch seq hidden_size]: The residual stream after the final norm, what lm_head reads
(lm_head_output) -> Logits [batch seq vocab_size]: The head's logits before anything the model does past lm_head (a softcap, a scale)
(next_token_probs) -> NextTokenProbs [batch vocab_size]: The next-token distribution at the last position; derived, read-only
(input_ids) -> Tokens [batch seq]: The token ids the model was called with
(attention_mask) -> Tokens [batch seq]: The attention mask the model was called with; zeros are padding
(input_size): [batch, seq] of the current call; read-only
)What print(model) shows on Qwen/Qwen3-VL-32B-Thinking: the native modules, standard names and every standard value with its layout.
Qwen3VLForConditionalGeneration(
(lm_head): Linear(in_features=5120, out_features=151936, bias=False)
(generator): Generator(
(streamer): Streamer()
)
(embed_tokens): Embedding(151936, 5120)
(layers): ModuleList(
(0-63): 64 x Qwen3VLTextDecoderLayer(
(self_attn): Qwen3VLTextAttention(
(q_proj): Linear(in_features=5120, out_features=8192, bias=False)
(k_proj): Linear(in_features=5120, out_features=1024, bias=False)
(v_proj): Linear(in_features=5120, out_features=1024, bias=False)
(o_proj): Linear(in_features=8192, out_features=5120, bias=False)
(q_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(k_norm): Qwen3VLTextRMSNorm((128,), eps=1e-06)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_output) -> Residual [batch seq hidden_size]: What the attention adds to the residual stream
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
)
(mlp): Qwen3VLTextMLP(
(gate_proj): Linear(in_features=5120, out_features=25600, bias=False)
(up_proj): Linear(in_features=5120, out_features=25600, bias=False)
(down_proj): Linear(in_features=25600, out_features=5120, bias=False)
(act_fn): SiLUActivation()
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives: act(gate)·up on a gated MLP, the activation itself otherwise
(mlp_output) -> Residual [batch seq hidden_size]: What the MLP adds to the residual stream
)
(input_layernorm): Qwen3VLTextRMSNorm((5120,), eps=1e-06)
(post_attention_layernorm): Qwen3VLTextRMSNorm((5120,), eps=1e-06)
(deepstack_output) -> ImageFeatures [image_tokens hidden_size]: The deepstack image features the text model adds at the image tokens after this block
(layer_input) -> Residual [batch seq hidden_size]: The residual stream entering the block
(attention_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the attention
(layer_mid) -> Residual [batch seq hidden_size]: The residual stream after the attention add
(mlp_norm_output) -> Residual [batch seq hidden_size]: The normed stream entering the MLP
(mlp_input) -> Residual [batch seq hidden_size]: What the MLP is called with
(layer_output) -> Residual [batch seq hidden_size]: The residual stream leaving the block, a tensor even when the block returns a tuple
)
)
(norm): Qwen3VLTextRMSNorm((5120,), eps=1e-06)
(vision): Qwen3VLVisionModel(
(patch_embed): Qwen3VLVisionPatchEmbed(
(proj): Conv3d(3, 1152, kernel_size=(2, 16, 16), stride=(2, 16, 16))
)
(pos_embed): Embedding(2304, 1152)
(rotary_pos_emb): Qwen3VLVisionRotaryEmbedding()
(layers/blocks): ModuleList(
(0-26): 27 x Qwen3VLVisionBlock(
(input_layernorm/norm1): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(post_attention_layernorm/norm2): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(self_attn/attn): Qwen3VLVisionAttention(
(qkv): Linear(in_features=1152, out_features=3456, bias=True)
(o_proj/proj): Linear(in_features=1152, out_features=1152, bias=True)
(attention_queries) -> Queries [batch num_heads seq qk_head_dim]: The queries entering attention
(attention_keys) -> Keys [batch num_kv_heads seq qk_head_dim]: The keys entering attention
(attention_values) -> Values [batch num_kv_heads seq head_dim]: The values entering attention
(attention_head_outputs) -> HeadOutputs [batch seq num_heads head_dim]: The per-head outputs before the output projection
(attention_output) -> Patches [images patches vision_hidden_size]: What the attention adds to the tower's stream
(attention_premix) -> HeadOutputs [batch seq num_heads head_dim]: Every head's output side by side, as the output projection takes it
(attention_scores) -> Pattern [batch num_heads query key]: The attention scores entering the softmax, masked
(attention_probabilities) -> Pattern [batch num_heads query key]: The attention pattern the values are mixed with
)
(mlp): Qwen3VLVisionMLP(
(linear_fc1): Linear(in_features=1152, out_features=4304, bias=True)
(down_proj/linear_fc2): Linear(in_features=4304, out_features=1152, bias=True)
(act_fn): GELUTanh()
(mlp_output) -> Patches [images patches vision_hidden_size]: What the MLP adds to the tower's stream
(mlp_activation) -> Neurons [batch seq intermediate_size]: The activation function's output
(mlp_neurons) -> Neurons [batch seq intermediate_size]: What the down projection receives
)
(layer_input) -> Patches [images patches vision_hidden_size]: The tower's stream entering the block
(attention_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the attention
(layer_mid) -> Patches [images patches vision_hidden_size]: The tower's stream after the attention add
(mlp_norm_output) -> Patches [images patches vision_hidden_size]: The normed stream entering the MLP
(mlp_input) -> Patches [images patches vision_hidden_size]: What the MLP is called with
(layer_output) -> Patches [images patches vision_hidden_size]: The tower's stream leaving the block
)
)
(merger): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4608, out_features=4608, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4608, out_features=5120, bias=True)
)
(deepstack_merger_list): ModuleList(
(0-2): 3 x Qwen3VLVisionPatchMerger(
(norm): LayerNorm((4608,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4608, out_features=4608, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4608, out_features=5120, bias=True)
)
)
(image_token_mask) -> ImageTokenMask [batch seq]: Which positions hold image tokens: input_ids == the config's image_token_id; read-only
(patch_embeddings) -> Patches [images patches vision_hidden_size]: The patch embedding's output, one row per patch
(tower_output) -> Patches [images patches vision_hidden_size]: The last block's stream after the tower's final norm, before any pooling or adapter
(image_features) -> ImageFeatures [image_tokens hidden_size]: The image features the text model receives at the image tokens, flat over them
)
(projector): Qwen3VLVisionPatchMerger(
(norm): LayerNorm((1152,), eps=1e-06, elementwise_affine=True, bias=True)
(linear_fc1): Linear(in_features=4608, out_features=4608, bias=True)
(act_fn): GELU(approximate='none')
(down_proj/linear_fc2): Linear(in_features=4608, out_features=5120, bias=True)
)
(logits) -> Logits [batch seq vocab_size]: The logits the model returns, after anything it does past lm_head (a softcap, a scale)
(token_embeddings) -> Residual [batch seq hidden_size]: The embedding module's output; layers[0].input is what enters the first block
(final_norm_output) -> Residual [batch seq hidden_size]: The residual stream after the final norm, what lm_head reads
(lm_head_output) -> Logits [batch seq vocab_size]: The head's logits before anything the model does past lm_head (a softcap, a scale)
(next_token_probs) -> NextTokenProbs [batch vocab_size]: The next-token distribution at the last position; derived, read-only
(input_ids) -> Tokens [batch seq]: The token ids the model was called with
(attention_mask) -> Tokens [batch seq]: The attention mask the model was called with; zeros are padding
(input_size): [batch, seq] of the current call; read-only
)h = x + self_attn(input_layernorm(x)) # q_norm, k_norm per head, then M-RoPE
out = h + mlp(post_attention_layernorm(h)) # SwiGLU
x = out + deepstack[k] at the image tokens # the text model, after blocks 0, 1, 2 only
The block is Qwen3's, with Llama's names. The block returns a tensor and adds the attention's and the MLP's outputs to the stream, so the contribution identity is the plain sum:
with model.trace(prompt):
x = model.layers[1].input.save()
attn = model.layers[1].self_attn.attention_output.save()
mlp = model.layers[1].mlp.mlp_output.save()
out = model.layers[1].layer_output.save()
torch.testing.assert_close(x + attn + mlp, out)
The third line is DeepStack, done by the text model between the blocks, on an image prompt only.
On an image prompt the text model adds deepstack_output at the image positions between the first
blocks (the family module's docstring gives the identity); vision.image_token_mask marks them:
with model.trace(prompt, images=[red]):
mask = model.vision.image_token_mask.save()
out = model.layers[0].layer_output.save()
added = model.layers[0].deepstack_output.save()
entering = model.layers[1].input.save()
torch.equal(entering[mask], out[mask] + added) # True
torch.equal(entering[~mask], out[~mask]) # True
A patch from block 0's layer_output into block 1's input drops deepstack_output; a
residual-stream SAE or probe at layer_output on blocks 0 to 2 sees the stream before the add. Ablating
the image takes vision.image_features and all three deepstack_output (the Vision notes give the
numbers). A text prompt makes no DeepStack call, and deepstack_output is not reached.
q_norm and k_norm are RMSNorms over one head's 128 dimensions, applied before the rotary;
attention_queries and attention_keys are read at the attention interface, after both. The rotary
is M-RoPE: each token has a time, a height and a width position, and mrope_section [24, 20, 20]
gives the 64 rotary frequencies to the three, interleaved. The text model folds the three into one
cos and sin before block 0. On text tokens the three positions are equal and count up by one.
An image's tokens all take the time position the image starts at; their height and width
positions are that start plus their row and column in the merged grid. The text after the image
continues from the largest of these plus one, so after an image a token's rotary position is
smaller than its index in input_ids. 2B has 16 query heads over 8 key/value heads,
4B and 8B 32 over 8, 32B 64 over 8; the score scale is 128 ** -0.5.
Every checkpoint is Qwen3VLForConditionalGeneration (model_type qwen3_vl), whose text_config is
qwen3_vl_text. With task="image-text-to-text", as this page builds them, the wrapper loads with its
processor: the text model at model.language_model, the vision encoder at model.visual as
model.vision. Transformers has no text-only class for it. A task="text-generation" load with
dispatch=True builds the same wrapper without a processor, and there every vision value and
deepstack_output raise Unavailable. attn_implementation="eager" is needed for the six attention interior values on the text blocks; the
vision encoder's pattern is Unavailable under every load.
The tokenizer sets bos_token to None and prepends nothing. The processor's chat template puts an
image as <|vision_start|><|image_pad|><|vision_end|>, and the processor repeats <|image_pad|>
(151655) once per image token; vision.image_token_mask is input_ids == 151655. The Instruct
templates end the prompt at <|im_start|>assistant\n; the Thinking templates add <think>\n, so the
next token starts a reasoning trace, not the answer.
The dense Qwen3-VL checkpoints, 2B to 32B, Instruct and Thinking. rope_theta is 5·10⁶ and
max_position_embeddings 262144 on each. The mixture-of-experts releases, Qwen3-VL-30B-A3B and
235B-A22B, are qwen3_vl_moe_text; Qwen3.5's text model is qwen3_5_text, a hybrid without DeepStack.
The vision encoder runs on [patches, vision_hidden_size], the patches of every image in the invoke
concatenated. Its stream values (patch_embeddings, every block's layer_output,
attention_output and mlp_output, tower_output) are served [1, patches, vision_hidden_size], a view with a
leading images axis of 1, so in-place edits land; vision.layers[i].input and .output stay the
native [patches, vision_hidden_size], and the block identity holds across the two by broadcasting. The
processor's image_grid_thw ([t, h, w] per image, in patches; an image is two identical frames, so
t is 1) splits the row: image j has t * h * w patches.
inputs = model.processor(text=prompt, images=[red, wide], return_tensors="pt")
sizes = inputs["image_grid_thw"].prod(-1).tolist() # patches per image
with model.trace(prompt, images=[red, wide]):
patches = model.vision.layers[1].layer_output.save()
per_image = patches[0].split(sizes) # one [t*h*w, vision_hidden_size] each
Inside an image the patches are in merge-block order: the spatial_merge_size ×
spatial_merge_size (2 × 2) block of neighbours the merger folds into one image token is four
consecutive rows, blocks in raster order. Index 4 * b to 4 * b + 3 is block b, not a raster row.
The attention computes queries, keys and values for the whole row, then calls the attention
function once per image (once per window on Qwen2.5-VL's windowed blocks) and concatenates the
outputs. attention_queries, attention_keys and attention_values are served whole, before the
split, [1, heads, patches, head_dim], the queries and keys after the rotary embedding;
attention_head_outputs is the calls' outputs concatenated back, [1, patches, heads, head_dim]
(under any implementation but flash, which runs one call over cu_seqlens). attention_scores and
attention_probabilities are Unavailable under every implementation, eager included: "no one
tensor is the block's pattern". The split points are the attention's cu_seqlens argument,
self_attn.inputs[1]["cu_seqlens"], so a pattern per image is a softmax over each slice:
attn = model.vision.layers[1].self_attn
with model.trace(prompt, images=[red, wide]):
cu = attn.inputs[1]["cu_seqlens"].save() # read first: the module's input
q = attn.attention_queries.save()
k = attn.attention_keys.save()
patterns = []
for a, b in zip(cu[:-1].tolist(), cu[1:].tolist()):
scores = q[0, :, a:b] @ k[0, :, a:b].transpose(-1, -2) * model.vision.head_dim ** -0.5
patterns.append(scores.softmax(-1)) # [heads, patches_j, patches_j]
Each pattern times that image's values reproduces attention_head_outputs exactly (checked on
Qwen3.5's pinned tiny checkpoint).
model.projector is the vision encoder's own merger: it norms each patch, concatenates each 2 × 2
block into one 4 * vision_hidden_size vector and runs a two-layer MLP to the text model's width, so
image tokens per image are t * h * w / 4. It reads the last block's stream with no norm between
(projector.input == tower_output[0], the native [patches, vision_hidden_size]). Because the merger
runs inside the vision encoder's forward, vision.tower_output (the encoder's output) is reached after
it: read model.projector.input and .output before vision.tower_output in a trace.
image_sizemodel.vision reads its sizes off the vision config: vision_hidden_size is the encoder's width
(embed_dim on Qwen2-VL, whose config's hidden_size is the merger's output width),
num_heads, intermediate_size (embed_dim * mlp_ratio on Qwen2-VL), patch_size, and two of
its own, spatial_merge_size (2 on every line) and window_size (Qwen2.5-VL's window side in
pixels, 112; None on the others). image_size raises Unavailable: the processor cuts each
image into its own grid.
| line | block norms, merger norm | MLP | beyond the shared encoder |
|---|---|---|---|
| Qwen2-VL | LayerNorm | fc1, QuickGELU, fc2 |
none |
| Qwen2.5-VL | RMSNorm | SwiGLU, biased | windows of 112 pixels except on fullatt_block_indexes |
| Qwen3-VL, -MoE | LayerNorm | linear_fc1, GELU (tanh), linear_fc2 |
learned pos_embed; DeepStack taps |
| Qwen3.5, -MoE | LayerNorm | linear_fc1, GELU (tanh), linear_fc2 |
learned pos_embed |
On Qwen2.5-VL the encoder permutes the patches into windows at entry, so its blocks' values,
tower_output and projector.output are in window order, and it restores the merge-block order
after the merger: vision.image_features is in scatter order. On Qwen3-VL and Qwen3.5 vision.layers[0].input
is patch_embeddings plus the resampled pos_embed. On Qwen2-VL vision.layers[0].input is
patch_embeddings; on Qwen2.5-VL it is patch_embeddings permuted into window order; on both, positions
enter only through the rotary embedding.
model.projector is model.visual.merger, the vision encoder's last module: norm (a LayerNorm over
vision_hidden_size) on each patch, then each 2 × 2 block of consecutive patches concatenated into one
4 * vision_hidden_size vector, linear_fc1, GELU and linear_fc2 to the text model's width. An image of
t * h * w patches (its image_grid_thw row) is t * h * w / 4 image tokens, and
vision.image_features is model.projector.output as it is, [image_tokens, hidden_size]. A patch is 16
pixels, so an image token covers 32 × 32. On 4B a 448 × 448 image and a 320 × 256 one are 1104
patches in one row and 276 image tokens; the merger maps 4 × 1024 = 4096 to 2560.
with model.trace(prompt, images=[red, wide]):
mask = model.vision.image_token_mask.save()
fed = model.projector.input.save() # [patches, vision_hidden_size]
features = model.vision.image_features.save()
first = model.layers[0].input.save()
fed.shape[0] // 4 == mask.sum() # True: one image token per 2 x 2 block
torch.equal(first[mask], features) # True
The vision encoder adds pos_embed, a learned table of 48 × 48 positions resampled to each image's
grid, to the patch embedding before block 0, so vision.layers[0].input is not
vision.patch_embeddings. Positions also enter every vision block through the 2D rotary embedding.
vision_config.deepstack_visual_indexes names three vision blocks: 5, 11 and 17 of 24 on 2B and 4B,
8, 16 and 24 of 27 on 8B and 32B. After each, its own merger, vision.deepstack_merger_list[k], reads
that block's output and folds it to image tokens as the main merger does, with its LayerNorm after
the 2 × 2 concatenation (over 4 * vision_hidden_size) instead of before it. The mergers keep their
native path; their outputs reach the text model as model.layers[k].deepstack_output, added at the
image positions after text block k:
with model.trace(prompt, images=[red]):
tapped = model.vision.deepstack_merger_list[0].output.save()
added = model.layers[0].deepstack_output.save()
torch.equal(added, tapped) # True
vision.image_features is therefore not the only way the image reaches the text model. On
Qwen/Qwen3-VL-4B-Instruct, asked the color of a red square on white, zeroing
vision.image_features alone still answers "Red"; zeroing deepstack_output on blocks 0 to 2 as well
answers "White".
with model.trace(prompt, images=[red]):
model.vision.image_features[:] = 0
for k in range(3):
model.layers[k].deepstack_output[:] = 0
ablated = model.logits.save()
The deepstack mergers run inside the vision encoder, each right after the block it taps, and the
merger runs inside it too, before the encoder returns. In one trace, read a tapped block's
layer_output, then deepstack_merger_list[k], before any later vision block. Read
model.projector.input and .output before vision.tower_output, then vision.image_features.
Then read, per text block, layer_output, deepstack_output and the next block's input. A read
out of this order raises OutOfOrderError.
Qwen3-VL, text (Qwen3VLTextModel, model_type qwen3_vl_text).
The language model inside Qwen3VLForConditionalGeneration (model_type
qwen3_vl): Qwen3's block (q_norm/k_norm inside the attention,
head_dim the config's) under interleaved multimodal rotary embeddings
(M-RoPE), folded into one cos/sin pair before any block runs and
applied inside the attention after the q/k norms and before the shared
interface, so the base Attention holds. Every checkpoint loads as the
wrapper (task="image-text-to-text"), whose text stack sits at
model.language_model.
DeepStack. The tower taps three of its blocks
(vision_config.deepstack_visual_indexes), merges each tap with its own
deepstack_merger_list[k], and the text model adds merger k's output at
the image positions after text block k, outside the block, in its own
forward (_deepstack_process). So on blocks k < 3 (fewer on a model
with fewer blocks) the stream entering block k + 1 is not block k's
layer_output at the image positions. Layer.deepstack_output serves what
is added there, [image_tokens, hidden_size]:
layers[k+1].input[mask] == layers[k].layer_output[mask] + layers[k].deepstack_output,
the rest of the stream unchanged; on the other blocks it is Unavailable and
layers[k+1].input == layers[k].layer_output.
The wrapper's Qwen ViT model.visual is vision (a QwenVision: packed,
[1, patches, vision_hidden_size], with a learned pos_embed added after
patch_embed); its merger is projector. The deepstack mergers keep
their native path, vision.deepstack_merger_list[k].