@@ -3663,6 +3663,200 @@ def pad_hf_embedding_layer(input_tensor, target_shape):
36633663 return hooks
36643664
36653665
3666+ def QWEN3_VL_MAXTEXT_TO_HF_PARAM_MAPPING (config , maxtext_config , scan_layers = False ):
3667+ """Returns mapping from MaxText to HuggingFace Qwen3-VL weight paths."""
3668+ mapping = {}
3669+
3670+ n_layers_text = config ["text_config" ]["num_hidden_layers" ]
3671+ text_mapping = QWEN_MAXTEXT_TO_HF_PARAM_MAPPING (
3672+ config = {"num_hidden_layers" : n_layers_text },
3673+ maxtext_config = maxtext_config ,
3674+ scan_layers = scan_layers ,
3675+ )
3676+
3677+ def replace_prefix (val ):
3678+ if isinstance (val , list ):
3679+ return [replace_prefix (v ) for v in val ]
3680+ elif isinstance (val , str ):
3681+ return val .replace ("model." , "model.language_model." )
3682+ return val
3683+
3684+ for key , value in text_mapping .items ():
3685+ mapping [key ] = replace_prefix (value )
3686+
3687+ vision_config = config ["vision_config" ]
3688+ n_vision_layers = vision_config ["depth" ]
3689+
3690+ mapping ["params-vision_encoder-Qwen3VLVisionEncoder_0-patch_embed-proj-kernel" ] = "model.visual.patch_embed.proj.weight"
3691+ mapping ["params-vision_encoder-Qwen3VLVisionEncoder_0-patch_embed-proj-bias" ] = "model.visual.patch_embed.proj.bias"
3692+
3693+ mapping ["params-vision_encoder-Qwen3VLVisionEncoder_0-pos_embed_interpolate-pos_embed" ] = (
3694+ "model.visual.pos_embed.weight"
3695+ )
3696+
3697+ for i in range (n_vision_layers ):
3698+ prefix = f"params-vision_encoder-Qwen3VLVisionEncoder_0-blocks_{ i } "
3699+ hf_prefix = f"model.visual.blocks.{ i } "
3700+
3701+ mapping [f"{ prefix } -ln1-scale" ] = f"{ hf_prefix } .norm1.weight"
3702+ mapping [f"{ prefix } -ln1-bias" ] = f"{ hf_prefix } .norm1.bias"
3703+ mapping [f"{ prefix } -ln2-scale" ] = f"{ hf_prefix } .norm2.weight"
3704+ mapping [f"{ prefix } -ln2-bias" ] = f"{ hf_prefix } .norm2.bias"
3705+
3706+ mapping [
3707+ (
3708+ f"{ prefix } -attn-attn-query-kernel" ,
3709+ f"{ prefix } -attn-attn-key-kernel" ,
3710+ f"{ prefix } -attn-attn-value-kernel" ,
3711+ )
3712+ ] = f"{ hf_prefix } .attn.qkv.weight"
3713+ mapping [
3714+ (
3715+ f"{ prefix } -attn-attn-query-bias" ,
3716+ f"{ prefix } -attn-attn-key-bias" ,
3717+ f"{ prefix } -attn-attn-value-bias" ,
3718+ )
3719+ ] = f"{ hf_prefix } .attn.qkv.bias"
3720+ mapping [f"{ prefix } -attn-attn-out-kernel" ] = f"{ hf_prefix } .attn.proj.weight"
3721+ mapping [f"{ prefix } -attn-attn-out-bias" ] = f"{ hf_prefix } .attn.proj.bias"
3722+
3723+ mapping [f"{ prefix } -mlp-kernel" ] = f"{ hf_prefix } .mlp.linear_fc1.weight"
3724+ mapping [f"{ prefix } -mlp-bias" ] = f"{ hf_prefix } .mlp.linear_fc1.bias"
3725+ mapping [f"{ prefix } -mlp_out-kernel" ] = f"{ hf_prefix } .mlp.linear_fc2.weight"
3726+ mapping [f"{ prefix } -mlp_out-bias" ] = f"{ hf_prefix } .mlp.linear_fc2.bias"
3727+
3728+ deepstack_indexes = vision_config .get ("deepstack_visual_indexes" , [5 , 11 , 17 ])
3729+ for merger_idx , _ in enumerate (deepstack_indexes ):
3730+ prefix = f"params-vision_encoder-Qwen3VLVisionEncoder_0-merger_{ merger_idx } "
3731+ hf_prefix = f"model.visual.deepstack_merger_list.{ merger_idx } "
3732+
3733+ mapping [f"{ prefix } -ln_q-scale" ] = f"{ hf_prefix } .norm.weight"
3734+ mapping [f"{ prefix } -ln_q-bias" ] = f"{ hf_prefix } .norm.bias"
3735+ mapping [f"{ prefix } -mlp_0-kernel" ] = f"{ hf_prefix } .linear_fc1.weight"
3736+ mapping [f"{ prefix } -mlp_0-bias" ] = f"{ hf_prefix } .linear_fc1.bias"
3737+ mapping [f"{ prefix } -mlp_2-kernel" ] = f"{ hf_prefix } .linear_fc2.weight"
3738+ mapping [f"{ prefix } -mlp_2-bias" ] = f"{ hf_prefix } .linear_fc2.bias"
3739+
3740+ mapping ["params-vision_encoder-Qwen3VLVisionProjector_0-merger-ln_q-scale" ] = "model.visual.merger.norm.weight"
3741+ mapping ["params-vision_encoder-Qwen3VLVisionProjector_0-merger-ln_q-bias" ] = "model.visual.merger.norm.bias"
3742+ mapping ["params-vision_encoder-Qwen3VLVisionProjector_0-merger-mlp_0-kernel" ] = "model.visual.merger.linear_fc1.weight"
3743+ mapping ["params-vision_encoder-Qwen3VLVisionProjector_0-merger-mlp_0-bias" ] = "model.visual.merger.linear_fc1.bias"
3744+ mapping ["params-vision_encoder-Qwen3VLVisionProjector_0-merger-mlp_2-kernel" ] = "model.visual.merger.linear_fc2.weight"
3745+ mapping ["params-vision_encoder-Qwen3VLVisionProjector_0-merger-mlp_2-bias" ] = "model.visual.merger.linear_fc2.bias"
3746+
3747+ return mapping
3748+
3749+
3750+ def QWEN3_VL_MAXTEXT_TO_HF_PARAM_HOOK_FN (config , maxtext_config , scan_layers = False , saving_to_hf = False ):
3751+ """Creates parameter transformation functions for Qwen3-VL."""
3752+ mapping = {}
3753+
3754+ n_layers_text = config ["text_config" ]["num_hidden_layers" ]
3755+ text_hooks = QWEN_MAXTEXT_TO_HF_PARAM_HOOK_FN (
3756+ config = {"num_hidden_layers" : n_layers_text },
3757+ maxtext_config = maxtext_config ,
3758+ scan_layers = scan_layers ,
3759+ saving_to_hf = saving_to_hf ,
3760+ )
3761+ mapping .update (text_hooks )
3762+
3763+ vision_config = config ["vision_config" ]
3764+ n_vision_layers = vision_config ["depth" ]
3765+ hidden_size = vision_config ["hidden_size" ]
3766+
3767+ def reshape_kernel_vision (input_tensor , target_shape ):
3768+ """Reshape kernel for vision layers."""
3769+ if saving_to_hf :
3770+ flipped_target_shape = np .flip (np .array (target_shape ))
3771+ return input_tensor .reshape (flipped_target_shape ).T
3772+ else :
3773+ return input_tensor .T .reshape (target_shape )
3774+
3775+ def reshape_conv3d_patch_embed (input_tensor , target_shape ):
3776+ """Reshape 3D conv patch embedding weight."""
3777+ if saving_to_hf :
3778+ return input_tensor .transpose (4 , 3 , 0 , 1 , 2 )
3779+ else :
3780+ return input_tensor .transpose (2 , 3 , 4 , 1 , 0 )
3781+
3782+ def process_qkv_vision (input_tensor , target_shape = None ):
3783+ """Handles composite_mt_key: maxtext (query, key, value) <-> hf (qkv)."""
3784+ if saving_to_hf :
3785+ q , k , v = input_tensor
3786+ q_hf = q .reshape (hidden_size , hidden_size ).T
3787+ k_hf = k .reshape (hidden_size , hidden_size ).T
3788+ v_hf = v .reshape (hidden_size , hidden_size ).T
3789+ return np .concatenate ([q_hf , k_hf , v_hf ], axis = 0 )
3790+ else :
3791+ q_hf = input_tensor [:hidden_size , :]
3792+ k_hf = input_tensor [hidden_size : 2 * hidden_size , :]
3793+ v_hf = input_tensor [2 * hidden_size :, :]
3794+ q_mt = q_hf .T .reshape (target_shape [0 ])
3795+ k_mt = k_hf .T .reshape (target_shape [1 ])
3796+ v_mt = v_hf .T .reshape (target_shape [2 ])
3797+ return np .stack ([q_mt , k_mt , v_mt ], axis = - 1 )
3798+
3799+ def process_qkv_bias_vision (input_tensor , target_shape = None ):
3800+ """Handles composite_mt_key: maxtext (query_bias, key_bias, value_bias) <-> hf (qkv_bias)."""
3801+ if saving_to_hf :
3802+ qb , kb , vb = input_tensor
3803+ qb_hf = qb .reshape (hidden_size )
3804+ kb_hf = kb .reshape (hidden_size )
3805+ vb_hf = vb .reshape (hidden_size )
3806+ return np .concatenate ([qb_hf , kb_hf , vb_hf ], axis = 0 )
3807+ else :
3808+ qb_hf = input_tensor [:hidden_size ]
3809+ kb_hf = input_tensor [hidden_size : 2 * hidden_size ]
3810+ vb_hf = input_tensor [2 * hidden_size :]
3811+ qb_mt = qb_hf .reshape (target_shape [0 ])
3812+ kb_mt = kb_hf .reshape (target_shape [1 ])
3813+ vb_mt = vb_hf .reshape (target_shape [2 ])
3814+ return np .stack ([qb_mt , kb_mt , vb_mt ], axis = - 1 )
3815+
3816+ def reshape_vision_attn_out (input_tensor , target_shape ):
3817+ """Reshape vision attention output projection."""
3818+ if saving_to_hf :
3819+ return input_tensor .reshape (hidden_size , hidden_size ).T
3820+ else :
3821+ return input_tensor .T .reshape (target_shape )
3822+
3823+ mapping ["params-vision_encoder-Qwen3VLVisionEncoder_0-patch_embed-proj-kernel" ] = reshape_conv3d_patch_embed
3824+
3825+ for i in range (n_vision_layers ):
3826+ prefix = f"params-vision_encoder-Qwen3VLVisionEncoder_0-blocks_{ i } "
3827+
3828+ mapping [
3829+ (
3830+ f"{ prefix } -attn-attn-query-kernel" ,
3831+ f"{ prefix } -attn-attn-key-kernel" ,
3832+ f"{ prefix } -attn-attn-value-kernel" ,
3833+ )
3834+ ] = process_qkv_vision
3835+ mapping [
3836+ (
3837+ f"{ prefix } -attn-attn-query-bias" ,
3838+ f"{ prefix } -attn-attn-key-bias" ,
3839+ f"{ prefix } -attn-attn-value-bias" ,
3840+ )
3841+ ] = process_qkv_bias_vision
3842+
3843+ mapping [f"{ prefix } -attn-attn-out-kernel" ] = reshape_vision_attn_out
3844+
3845+ mapping [f"{ prefix } -mlp-kernel" ] = reshape_kernel_vision
3846+ mapping [f"{ prefix } -mlp_out-kernel" ] = reshape_kernel_vision
3847+
3848+ deepstack_indexes = vision_config .get ("deepstack_visual_indexes" , [5 , 11 , 17 ])
3849+ for merger_idx , _ in enumerate (deepstack_indexes ):
3850+ prefix = f"params-vision_encoder-Qwen3VLVisionEncoder_0-merger_{ merger_idx } "
3851+ mapping [f"{ prefix } -mlp_0-kernel" ] = reshape_kernel_vision
3852+ mapping [f"{ prefix } -mlp_2-kernel" ] = reshape_kernel_vision
3853+
3854+ mapping ["params-vision_encoder-Qwen3VLVisionProjector_0-merger-mlp_0-kernel" ] = reshape_kernel_vision
3855+ mapping ["params-vision_encoder-Qwen3VLVisionProjector_0-merger-mlp_2-kernel" ] = reshape_kernel_vision
3856+
3857+ return mapping
3858+
3859+
36663860# {maxtext model name: {maxtext weight name: hf weight name}}
36673861PARAM_MAPPING = {
36683862 "gemma2-2b" : GEMMA2_MAXTEXT_TO_HF_PARAM_MAPPING ,
@@ -3689,6 +3883,7 @@ def pad_hf_embedding_layer(input_tensor, target_shape):
36893883 "qwen3-14b" : QWEN_MAXTEXT_TO_HF_PARAM_MAPPING ,
36903884 "qwen3-14b-base" : QWEN_MAXTEXT_TO_HF_PARAM_MAPPING ,
36913885 "qwen3-32b" : QWEN_MAXTEXT_TO_HF_PARAM_MAPPING ,
3886+ "qwen3-vl-4b" : QWEN3_VL_MAXTEXT_TO_HF_PARAM_MAPPING ,
36923887 "llama3.1-8b" : LLAMA31_MAXTEXT_TO_HF_PARAM_MAPPING ,
36933888 "llama3.1-8b-Instruct" : LLAMA31_MAXTEXT_TO_HF_PARAM_MAPPING ,
36943889 "llama3.1-70b" : LLAMA31_MAXTEXT_TO_HF_PARAM_MAPPING ,
@@ -3739,6 +3934,7 @@ def pad_hf_embedding_layer(input_tensor, target_shape):
37393934 "qwen3-14b" : QWEN_MAXTEXT_TO_HF_PARAM_HOOK_FN ,
37403935 "qwen3-14b-base" : QWEN_MAXTEXT_TO_HF_PARAM_HOOK_FN ,
37413936 "qwen3-32b" : QWEN_MAXTEXT_TO_HF_PARAM_HOOK_FN ,
3937+ "qwen3-vl-4b" : QWEN3_VL_MAXTEXT_TO_HF_PARAM_HOOK_FN ,
37423938 "llama3.1-8b" : LLAMA31_MAXTEXT_TO_HF_PARAM_HOOK_FN ,
37433939 "llama3.1-8b-Instruct" : LLAMA31_MAXTEXT_TO_HF_PARAM_HOOK_FN ,
37443940 "llama3.1-70b" : LLAMA31_MAXTEXT_TO_HF_PARAM_HOOK_FN ,
0 commit comments