Skip to content

Commit c1b7e9f

Browse files
committed
update
1 parent c5bc2e4 commit c1b7e9f

12 files changed

Lines changed: 425 additions & 11 deletions

File tree

src/maxtext/checkpoint_conversion/utils/hf_model_configs.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1586,6 +1586,63 @@ def __init__(self, **kwargs):
15861586
olmo3_32b_config = transformers.Olmo3Config(**olmo3_32b_dict)
15871587

15881588

1589+
qwen3_vl_4b_dict = {
1590+
"architectures": ["Qwen3VLForConditionalGeneration"],
1591+
"image_token_id": 151655,
1592+
"model_type": "qwen3_vl",
1593+
"text_config": {
1594+
"attention_bias": False,
1595+
"attention_dropout": 0.0,
1596+
"bos_token_id": 151643,
1597+
"dtype": "bfloat16",
1598+
"eos_token_id": 151645,
1599+
"head_dim": 128,
1600+
"hidden_act": "silu",
1601+
"hidden_size": 2560,
1602+
"initializer_range": 0.02,
1603+
"intermediate_size": 9728,
1604+
"max_position_embeddings": 262144,
1605+
"model_type": "qwen3_vl_text",
1606+
"num_attention_heads": 32,
1607+
"num_hidden_layers": 36,
1608+
"num_key_value_heads": 8,
1609+
"pad_token_id": None,
1610+
"rms_norm_eps": 1e-06,
1611+
"rope_parameters": {
1612+
"mrope_interleaved": True,
1613+
"mrope_section": [24, 20, 20],
1614+
"rope_theta": 5000000,
1615+
"rope_type": "default",
1616+
},
1617+
"tie_word_embeddings": True,
1618+
"use_cache": True,
1619+
"vocab_size": 151936,
1620+
},
1621+
"tie_word_embeddings": True,
1622+
"transformers_version": "5.8.0",
1623+
"video_token_id": 151656,
1624+
"vision_config": {
1625+
"deepstack_visual_indexes": [5, 11, 17],
1626+
"depth": 24,
1627+
"hidden_act": "gelu_pytorch_tanh",
1628+
"hidden_size": 1024,
1629+
"in_channels": 3,
1630+
"initializer_range": 0.02,
1631+
"intermediate_size": 4096,
1632+
"model_type": "qwen3_vl_vision",
1633+
"num_heads": 16,
1634+
"num_position_embeddings": 2304,
1635+
"out_hidden_size": 2560,
1636+
"patch_size": 16,
1637+
"spatial_merge_size": 2,
1638+
"temporal_patch_size": 2,
1639+
},
1640+
"vision_end_token_id": 151653,
1641+
"vision_start_token_id": 151652,
1642+
}
1643+
qwen3_vl_4b_config = PTConfig(**qwen3_vl_4b_dict)
1644+
1645+
15891646
# {maxtext model name: hf model config}
15901647
HF_MODEL_CONFIGS = {
15911648
"gemma2-2b": gemma2_2b_config,
@@ -1612,6 +1669,7 @@ def __init__(self, **kwargs):
16121669
"qwen3-14b": qwen3_14b_config,
16131670
"qwen3-14b-base": qwen3_14b_config,
16141671
"qwen3-32b": qwen3_32b_config,
1672+
"qwen3-vl-4b": qwen3_vl_4b_config,
16151673
"llama3.1-8b": llama31_8b_config,
16161674
"llama3.1-8b-Instruct": llama31_8b_config,
16171675
"llama3.1-70b": llama31_70b_config,

src/maxtext/checkpoint_conversion/utils/hf_shape.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,62 @@ def MIXTRAL_HF_WEIGHTS_TO_SHAPE(config):
11051105
return shapes
11061106

11071107

1108+
def QWEN3_VL_HF_WEIGHTS_TO_SHAPE(config):
1109+
"""Returns mapping between HuggingFace Qwen3-VL weights path and weights shape."""
1110+
text_shapes = QWEN_HF_WEIGHTS_TO_SHAPE(config["text_config"])
1111+
vl_shapes = {}
1112+
for k, v in text_shapes.items():
1113+
if k.startswith("model."):
1114+
new_k = k.replace("model.", "model.language_model.")
1115+
vl_shapes[new_k] = v
1116+
else:
1117+
vl_shapes[k] = v
1118+
1119+
vision_config = config["vision_config"]
1120+
v_depth = vision_config["depth"]
1121+
v_hidden_size = vision_config["hidden_size"]
1122+
v_intermediate_size = vision_config["intermediate_size"]
1123+
v_out_hidden_size = vision_config["out_hidden_size"]
1124+
1125+
vl_shapes["model.visual.patch_embed.proj.weight"] = [v_hidden_size, 3, 2, 16, 16]
1126+
vl_shapes["model.visual.patch_embed.proj.bias"] = [v_hidden_size]
1127+
vl_shapes["model.visual.pos_embed.weight"] = [vision_config["num_position_embeddings"], v_hidden_size]
1128+
1129+
for i in range(v_depth):
1130+
prefix = f"model.visual.blocks.{i}"
1131+
vl_shapes[f"{prefix}.norm1.weight"] = [v_hidden_size]
1132+
vl_shapes[f"{prefix}.norm1.bias"] = [v_hidden_size]
1133+
vl_shapes[f"{prefix}.norm2.weight"] = [v_hidden_size]
1134+
vl_shapes[f"{prefix}.norm2.bias"] = [v_hidden_size]
1135+
vl_shapes[f"{prefix}.attn.qkv.weight"] = [3 * v_hidden_size, v_hidden_size]
1136+
vl_shapes[f"{prefix}.attn.qkv.bias"] = [3 * v_hidden_size]
1137+
vl_shapes[f"{prefix}.attn.proj.weight"] = [v_hidden_size, v_hidden_size]
1138+
vl_shapes[f"{prefix}.attn.proj.bias"] = [v_hidden_size]
1139+
vl_shapes[f"{prefix}.mlp.linear_fc1.weight"] = [v_intermediate_size, v_hidden_size]
1140+
vl_shapes[f"{prefix}.mlp.linear_fc1.bias"] = [v_intermediate_size]
1141+
vl_shapes[f"{prefix}.mlp.linear_fc2.weight"] = [v_hidden_size, v_intermediate_size]
1142+
vl_shapes[f"{prefix}.mlp.linear_fc2.bias"] = [v_hidden_size]
1143+
1144+
deepstack_indexes = vision_config.get("deepstack_visual_indexes", [5, 11, 17])
1145+
for merger_idx, _ in enumerate(deepstack_indexes):
1146+
prefix = f"model.visual.deepstack_merger_list.{merger_idx}"
1147+
vl_shapes[f"{prefix}.norm.weight"] = [v_intermediate_size]
1148+
vl_shapes[f"{prefix}.norm.bias"] = [v_intermediate_size]
1149+
vl_shapes[f"{prefix}.linear_fc1.weight"] = [v_intermediate_size, v_intermediate_size]
1150+
vl_shapes[f"{prefix}.linear_fc1.bias"] = [v_intermediate_size]
1151+
vl_shapes[f"{prefix}.linear_fc2.weight"] = [v_out_hidden_size, v_intermediate_size]
1152+
vl_shapes[f"{prefix}.linear_fc2.bias"] = [v_out_hidden_size]
1153+
1154+
vl_shapes["model.visual.merger.norm.weight"] = [v_hidden_size]
1155+
vl_shapes["model.visual.merger.norm.bias"] = [v_hidden_size]
1156+
vl_shapes["model.visual.merger.linear_fc1.weight"] = [v_intermediate_size, v_intermediate_size]
1157+
vl_shapes["model.visual.merger.linear_fc1.bias"] = [v_intermediate_size]
1158+
vl_shapes["model.visual.merger.linear_fc2.weight"] = [v_out_hidden_size, v_intermediate_size]
1159+
vl_shapes["model.visual.merger.linear_fc2.bias"] = [v_out_hidden_size]
1160+
1161+
return vl_shapes
1162+
1163+
11081164
# {maxtext model name: {hf weight name: hf shape}}
11091165
HF_SHAPE = {
11101166
"gemma2-2b": GEMMA2_HF_WEIGHTS_TO_SHAPE,
@@ -1126,6 +1182,7 @@ def MIXTRAL_HF_WEIGHTS_TO_SHAPE(config):
11261182
"qwen3-8b": QWEN_HF_WEIGHTS_TO_SHAPE,
11271183
"qwen3-14b": QWEN_HF_WEIGHTS_TO_SHAPE,
11281184
"qwen3-32b": QWEN_HF_WEIGHTS_TO_SHAPE,
1185+
"qwen3-vl-4b": QWEN3_VL_HF_WEIGHTS_TO_SHAPE,
11291186
"llama3.1-8b": LLAMA31_HF_WEIGHTS_TO_SHAPE,
11301187
"llama3.1-8b-Instruct": LLAMA31_HF_WEIGHTS_TO_SHAPE,
11311188
"llama3.1-70b": LLAMA31_HF_WEIGHTS_TO_SHAPE,

src/maxtext/checkpoint_conversion/utils/param_mapping.py

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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}}
36673861
PARAM_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,

src/maxtext/checkpoint_conversion/utils/utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,8 @@ def save_config_file(
372372
):
373373
"""Saves the model configuration file(config.json)."""
374374
if jax.process_index() == 0:
375-
config.architectures = [MODEL_FOR_CAUSAL_LM_MAPPING_NAMES[config.model_type]]
375+
if config.model_type in MODEL_FOR_CAUSAL_LM_MAPPING_NAMES:
376+
config.architectures = [MODEL_FOR_CAUSAL_LM_MAPPING_NAMES[config.model_type]]
376377
if output_dir_final.startswith("hf://"):
377378
max_logging.log(f" Serializing {file_name} to memory for Hugging Face Hub upload...")
378379
json_string = config.to_json_string()

0 commit comments

Comments
 (0)