Bugfix (#24027)
This commit is contained in:
@@ -499,7 +499,10 @@ class ModelConfig:
|
||||
or "LongcatFlashForCausalLMNextN" in self.hf_config.architectures
|
||||
or "DotsVLMForCausalLM" in self.hf_config.architectures
|
||||
or "MistralLarge3ForCausalLM" in self.hf_config.architectures
|
||||
or "PixtralForConditionalGeneration" in self.hf_config.architectures
|
||||
or (
|
||||
"PixtralForConditionalGeneration" in self.hf_config.architectures
|
||||
and getattr(self.hf_text_config, "kv_lora_rank", None) is not None
|
||||
)
|
||||
or "MistralLarge3ForCausalLMEagle" in self.hf_config.architectures
|
||||
or "KimiK25ForConditionalGeneration" in self.hf_config.architectures
|
||||
):
|
||||
|
||||
@@ -13,19 +13,81 @@
|
||||
# ==============================================================================
|
||||
"""Inference-only Mistral model."""
|
||||
|
||||
import logging
|
||||
from collections.abc import Iterable
|
||||
from typing import List
|
||||
|
||||
import regex as re
|
||||
import torch
|
||||
from transformers.models.mistral3.modeling_mistral3 import Mistral3MultiModalProjector
|
||||
|
||||
from sglang.srt.managers.schedule_batch import MultimodalDataItem
|
||||
from sglang.srt.models.llama import LlamaForCausalLM
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MistralForCausalLM(LlamaForCausalLM):
|
||||
pass
|
||||
|
||||
|
||||
class MistralForCausalLMMistralFormat(MistralForCausalLM):
|
||||
"""Mistral GQA model loaded from mistral native format (params.json).
|
||||
|
||||
Handles weight name remapping from mistral native format to HF/Llama
|
||||
format. This is the GQA counterpart to MistralLarge3ForCausalLM which
|
||||
handles MLA models in mistral native format.
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
remapping = {
|
||||
r"layers\.(\d+)\.attention_norm\.weight": r"model.layers.\1.input_layernorm.weight",
|
||||
r"layers\.(\d+)\.attention\.wq\.(\w+)": r"model.layers.\1.self_attn.q_proj.\2",
|
||||
r"layers\.(\d+)\.attention\.wk\.(\w+)": r"model.layers.\1.self_attn.k_proj.\2",
|
||||
r"layers\.(\d+)\.attention\.wv\.(\w+)": r"model.layers.\1.self_attn.v_proj.\2",
|
||||
r"layers\.(\d+)\.attention\.wo\.(\w+)": r"model.layers.\1.self_attn.o_proj.\2",
|
||||
r"layers\.(\d+)\.ffn_norm\.weight": r"model.layers.\1.post_attention_layernorm.weight",
|
||||
r"layers\.(\d+)\.feed_forward\.w1\.(\w+)": r"model.layers.\1.mlp.gate_proj.\2",
|
||||
r"layers\.(\d+)\.feed_forward\.w2\.(\w+)": r"model.layers.\1.mlp.down_proj.\2",
|
||||
r"layers\.(\d+)\.feed_forward\.w3\.(\w+)": r"model.layers.\1.mlp.up_proj.\2",
|
||||
r"norm\.weight": "model.norm.weight",
|
||||
r"tok_embeddings\.weight": "model.embed_tokens.weight",
|
||||
r"output\.weight": "lm_head.weight",
|
||||
}
|
||||
# fmt: on
|
||||
|
||||
def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
|
||||
return super().load_weights(self._remap_mistral_to_llama(weights))
|
||||
|
||||
def _remap_mistral_to_llama(
|
||||
self, weights: Iterable[tuple[str, torch.Tensor]]
|
||||
) -> Iterable[tuple[str, torch.Tensor]]:
|
||||
"""Remap Mistral native format weight names to HF/Llama format."""
|
||||
for name, loaded_weight in weights:
|
||||
# Pass through weights already in HF/Llama layout so this loader
|
||||
# tolerates mixed-format checkpoints (e.g. native body + HF-style
|
||||
# multi_modal_projector weights spliced in by a parent class).
|
||||
if name.startswith("model.") or name.startswith("lm_head."):
|
||||
yield name, loaded_weight
|
||||
continue
|
||||
|
||||
for k, v in self.remapping.items():
|
||||
match = re.fullmatch(k, name)
|
||||
if match:
|
||||
name = match.expand(v)
|
||||
break
|
||||
else:
|
||||
logger.warning(f"Unrecognized weight: {name}. Skipping.")
|
||||
continue
|
||||
|
||||
if name.endswith(".qscale_act"):
|
||||
name = re.sub(r"\.qscale_act$", ".input_scale", name)
|
||||
elif name.endswith(".qscale_weight"):
|
||||
name = re.sub(r"\.qscale_weight$", ".weight_scale", name)
|
||||
|
||||
yield name, loaded_weight
|
||||
|
||||
|
||||
class Mistral3ForConditionalGeneration:
|
||||
MULTIMODAL_PROJECTOR_TYPE = Mistral3MultiModalProjector
|
||||
|
||||
@@ -89,5 +151,45 @@ class Mistral3ForConditionalGeneration:
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self.inner(*args, **kwargs)
|
||||
|
||||
def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
|
||||
"""Normalize transformers v5 Mistral3 weight names for
|
||||
LlavaForConditionalGeneration.load_weights.
|
||||
|
||||
v5 checkpoints lay out Mistral3 weights as:
|
||||
model.language_model.{embed_tokens,layers.*,norm}.*
|
||||
model.vision_tower.*
|
||||
model.multi_modal_projector.*
|
||||
lm_head.*
|
||||
|
||||
The Llava loader routes by top-level `language_model.` /
|
||||
`vision_tower.` prefixes, stripping one segment before forwarding to
|
||||
the sub-module. The sub-module's own `load_weights` expects the
|
||||
standard HF layout: `model.layers.*`, `model.embed_tokens.weight`,
|
||||
`lm_head.weight` for Llama, and `vision_tower` internals at their
|
||||
top level. So we rewrite:
|
||||
model.language_model.X -> language_model.model.X
|
||||
model.vision_tower.X -> vision_tower.X
|
||||
model.multi_modal_projector.X -> multi_modal_projector.X
|
||||
lm_head.X -> language_model.lm_head.X
|
||||
"""
|
||||
|
||||
def normalize(ws):
|
||||
for name, w in ws:
|
||||
if name.startswith("model.language_model."):
|
||||
rest = name[len("model.language_model.") :]
|
||||
name = "language_model.model." + rest
|
||||
elif name.startswith("model.vision_tower."):
|
||||
name = "vision_tower." + name[len("model.vision_tower.") :]
|
||||
elif name.startswith("model.multi_modal_projector."):
|
||||
name = (
|
||||
"multi_modal_projector."
|
||||
+ name[len("model.multi_modal_projector.") :]
|
||||
)
|
||||
elif name.startswith("lm_head."):
|
||||
name = "language_model." + name
|
||||
yield name, w
|
||||
|
||||
return self.inner.load_weights(normalize(weights))
|
||||
|
||||
|
||||
EntryClass = [MistralForCausalLM, Mistral3ForConditionalGeneration]
|
||||
|
||||
@@ -45,6 +45,7 @@ from sglang.srt.managers.mm_utils import (
|
||||
)
|
||||
from sglang.srt.managers.schedule_batch import MultimodalDataItem, MultimodalInputs
|
||||
from sglang.srt.model_loader.weight_utils import default_weight_loader
|
||||
from sglang.srt.models.mistral import MistralForCausalLMMistralFormat
|
||||
from sglang.srt.models.mistral_large_3 import MistralLarge3ForCausalLM
|
||||
|
||||
USE_XFORMERS_OPS = False
|
||||
@@ -94,10 +95,21 @@ class PixtralForConditionalGeneration(nn.Module):
|
||||
|
||||
self.vision_args = VisionEncoderArgs(**vision_args)
|
||||
|
||||
self.language_model = MistralLarge3ForCausalLM(
|
||||
config=self.config.text_config,
|
||||
quant_config=kwargs.get("quant_config"),
|
||||
)
|
||||
# Choose language model based on text architecture:
|
||||
# MLA text configs use DeepSeek V3 backbone (model_type="deepseek_v3"),
|
||||
# GQA text configs use the standard Llama-style Mistral backbone.
|
||||
text_config = self.config.text_config
|
||||
is_mla = getattr(text_config, "model_type", "") == "deepseek_v3"
|
||||
if is_mla:
|
||||
self.language_model = MistralLarge3ForCausalLM(
|
||||
config=text_config,
|
||||
quant_config=kwargs.get("quant_config"),
|
||||
)
|
||||
else:
|
||||
self.language_model = MistralForCausalLMMistralFormat(
|
||||
config=text_config,
|
||||
quant_config=kwargs.get("quant_config"),
|
||||
)
|
||||
|
||||
self.vision_encoder = VisionTransformer(self.vision_args)
|
||||
|
||||
|
||||
@@ -227,6 +227,14 @@ def get_hf_text_config(config: PretrainedConfig):
|
||||
if getattr(_converted, "dtype", None) is None and parent_dtype is not None:
|
||||
_converted.dtype = parent_dtype
|
||||
setattr(config, _attr, _converted)
|
||||
elif _sub is not None and parent_dtype is not None:
|
||||
# transformers v5 multimodal configs (e.g. Mistral3Config) carry
|
||||
# `dtype` only on the top-level config, leaving the sub-configs at
|
||||
# None. Without this, _get_and_verify_dtype falls back to float32
|
||||
# and then "auto" downcasts to float16, which overflows the Pixtral
|
||||
# vision tower on real images and produces NaN features.
|
||||
if getattr(_sub, "dtype", None) is None:
|
||||
_sub.dtype = parent_dtype
|
||||
|
||||
# Priority: thinker_config > llm_config > language_config > text_config
|
||||
if hasattr(config, "thinker_config"):
|
||||
|
||||
@@ -73,6 +73,22 @@ def adapt_config_dict(
|
||||
config_dict["architectures"] = ["MixtralForCausalLM"]
|
||||
else:
|
||||
config_dict["architectures"] = ["MistralForCausalLM"]
|
||||
config_dict["model_type"] = "mistral"
|
||||
# Mistral models use non-interleaved RoPE (is_neox_style=False),
|
||||
# unlike Llama which defaults to True.
|
||||
config_dict["rope_is_neox_style"] = False
|
||||
# Remove None-valued MLA fields that would shadow defaults in
|
||||
# model_config._derive_model_shapes (getattr returns None instead
|
||||
# of the fallback when the attribute exists but is None).
|
||||
for mla_key in (
|
||||
"q_lora_rank",
|
||||
"qk_rope_head_dim",
|
||||
"qk_nope_head_dim",
|
||||
"kv_lora_rank",
|
||||
"v_head_dim",
|
||||
):
|
||||
if config_dict.get(mla_key) is None:
|
||||
config_dict.pop(mla_key, None)
|
||||
|
||||
if bool(config_dict.get("yarn")):
|
||||
config_dict = _remap_mistral_yarn_args(config_dict)
|
||||
|
||||
Reference in New Issue
Block a user