diff --git a/docs/models/supported_models.md b/docs/models/supported_models.md index f9c70adec7..85efca1e9b 100644 --- a/docs/models/supported_models.md +++ b/docs/models/supported_models.md @@ -409,8 +409,6 @@ th { | `GraniteSWAForCausalLM` | Granite SWA | `ibm-granite/granite-swash-2b` | ✅︎ | ✅︎ | | `GritLM` | GritLM | `parasail-ai/GritLM-7B-vllm`. | ✅︎ | ✅︎ | | `HrmTextForCausalLM` | HRM-Text | `sapientinc/HRM-Text-1B`, etc. | | | -| `HunYuanDenseV1ForCausalLM` | Hunyuan Dense | `tencent/Hunyuan-7B-Instruct` | ✅︎ | ✅︎ | -| `HunYuanMoEV1ForCausalLM` | Hunyuan-A13B | `tencent/Hunyuan-A13B-Instruct`, `tencent/Hunyuan-A13B-Pretrain`, `tencent/Hunyuan-A13B-Instruct-FP8`, etc. | ✅︎ | ✅︎ | | `HYV3ForCausalLM` | HY3 | `tencent/Hy3-preview-Base`, `tencent/Hy3-preview` | ✅︎ | ✅︎ | | `HyperCLOVAXForCausalLM` | HyperCLOVAX-SEED-Think-14B | `naver-hyperclovax/HyperCLOVAX-SEED-Think-14B` | ✅︎ | ✅︎ | | `InternLM2ForCausalLM` | InternLM2 | `internlm/internlm2-7b`, `internlm/internlm2-chat-7b`, etc. | ✅︎ | ✅︎ | @@ -478,6 +476,8 @@ Some models are supported only via the [Transformers modeling backend](#transfor | Architecture | Models | Example HF Models | [LoRA](../features/lora.md) | [PP](../serving/parallelism_scaling.md) | | ------------ | ------ | ----------------- | -------------------- | ------------------------- | | `GPTBigCodeForCausalLM` | StarCoder, SantaCoder, WizardCoder | `bigcode/starcoder`, `bigcode/gpt_bigcode-santacoder`, `WizardLM/WizardCoder-15B-V1.0`, etc. | ✅︎ | | +| `HunYuanDenseV1ForCausalLM` | Hunyuan Dense | `tencent/Hunyuan-7B-Instruct` | ✅︎ | ✅︎ | +| `HunYuanMoEV1ForCausalLM` | Hunyuan-A13B | `tencent/Hunyuan-A13B-Instruct`, `tencent/Hunyuan-A13B-Pretrain`, `tencent/Hunyuan-A13B-Instruct-FP8`, etc. | ✅︎ | ✅︎ | | `OlmoForCausalLM` | OLMo | `allenai/OLMo-1B-hf`, `allenai/OLMo-7B-hf`, etc. | ✅︎ | ✅︎ | | `Olmo2ForCausalLM` | OLMo2 | `allenai/OLMo-2-0425-1B`, etc. | ✅︎ | ✅︎ | | `SmolLM3ForCausalLM` | SmolLM3 | `HuggingFaceTB/SmolLM3-3B` | ✅︎ | ✅︎ | @@ -552,7 +552,6 @@ These models primarily accept the [`LLM.generate`](./generative_models.md#llmgen | `HCXVisionForCausalLM` | HyperCLOVAX-SEED-Vision-Instruct-3B | T + I+ + V+ | `naver-hyperclovax/HyperCLOVAX-SEED-Vision-Instruct-3B` | | | | `HCXVisionV2ForCausalLM` | HyperCLOVAX-SEED-Think-32B | T + I+ + V+ | `naver-hyperclovax/HyperCLOVAX-SEED-Think-32B` | | | | `H2OVLChatModel` | H2OVL | T + IE+ | `h2oai/h2ovl-mississippi-800m`, `h2oai/h2ovl-mississippi-2b`, etc. | ✅︎ | ✅︎ | -| `HunYuanVLForConditionalGeneration` | HunyuanOCR | T + IE+ | `tencent/HunyuanOCR`, etc. | ✅︎ | ✅︎ | | `Idefics3ForConditionalGeneration` | Idefics3 | T + I | `HuggingFaceM4/Idefics3-8B-Llama3`, etc. | ✅︎ | | | `IsaacForConditionalGeneration` | Isaac | T + I+ | `PerceptronAI/Isaac-0.1` | ✅︎ | ✅︎ | | `InternS1ForConditionalGeneration` | Intern-S1 | T + IE+ + VE+ | `internlm/Intern-S1`, `internlm/Intern-S1-mini`, etc. | ✅︎ | ✅︎ | @@ -629,6 +628,7 @@ Some models are supported only via the [Transformers modeling backend](#transfor | Architecture | Models | Inputs | Example HF Models | [LoRA](../features/lora.md) | [PP](../serving/parallelism_scaling.md) | | ------------ | ------ | ------ | ----------------- | --------------------------- | --------------------------------------- | | `Emu3ForConditionalGeneration` | Emu3 | T + I+ | `BAAI/Emu3-Chat-hf` | ✅︎ | ✅︎ | +| `HunYuanVLForConditionalGeneration` | HunyuanOCR | T + IE+ | `tencent/HunyuanOCR`, etc. | ✅︎ | ✅︎ | | `VibeVoiceAsrForConditionalGeneration` | VibeVoice-ASR | T + A+ | `microsoft/VibeVoice-ASR-HF` | ✅︎ | ✅︎ | ^ You need to set the architecture name via `--hf-overrides` to match the one in vLLM.
diff --git a/vllm/model_executor/models/hunyuan_v1.py b/vllm/model_executor/models/hunyuan_v1.py deleted file mode 100644 index cf1933bd7d..0000000000 --- a/vllm/model_executor/models/hunyuan_v1.py +++ /dev/null @@ -1,1038 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# SPDX-FileCopyrightText: Copyright contributors to the vLLM project - -# coding=utf-8 -# Copyright 2024 The HunYuan team. -# Copyright 2023 The vLLM team. -# Copyright 2022 EleutherAI and the HuggingFace Inc. team. All rights reserved. -# -# This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX -# and OPT implementations in this library. It has been modified from its -# original forms to accommodate minor architectural differences compared -# to GPT-NeoX and OPT used by the Meta AI team that trained the model. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Inference-only HunYuan model compatible with HuggingFace weights.""" - -import typing -from collections.abc import Callable, Iterable -from itertools import islice - -import regex as re -import torch -from torch import nn -from transformers import PretrainedConfig - -from vllm.compilation.decorators import support_torch_compile -from vllm.config import CacheConfig, VllmConfig, get_current_vllm_config -from vllm.distributed import ( - get_ep_group, - get_pp_group, - get_tensor_model_parallel_world_size, -) -from vllm.model_executor.layers.activation import SiluAndMul -from vllm.model_executor.layers.attention import Attention -from vllm.model_executor.layers.fused_moe import ( - FusedMoEFactory, - fused_moe_make_expert_params_mapping, -) -from vllm.model_executor.layers.layernorm import RMSNorm -from vllm.model_executor.layers.linear import ( - ColumnParallelLinear, - MergedColumnParallelLinear, - QKVParallelLinear, - ReplicatedLinear, - RowParallelLinear, -) -from vllm.model_executor.layers.logits_processor import LogitsProcessor -from vllm.model_executor.layers.quantization import QuantizationConfig -from vllm.model_executor.layers.rotary_embedding import get_rope -from vllm.model_executor.layers.vocab_parallel_embedding import ( - ParallelLMHead, - VocabParallelEmbedding, -) -from vllm.model_executor.model_loader.weight_utils import ( - default_weight_loader, - maybe_remap_kv_scale_name, -) -from vllm.sequence import IntermediateTensors -from vllm.v1.attention.backend import AttentionType - -from .interfaces import ( - EagleModelMixin, - MixtureOfExperts, - SupportsEagle, - SupportsEagle3, - SupportsLoRA, - SupportsPP, -) -from .utils import ( - AutoWeightsLoader, - PPMissingLayer, - is_pp_missing_parameter, - make_layers, - maybe_prefix, -) - - -def _is_moe(config: PretrainedConfig) -> bool: - num_experts = getattr(config, "num_experts", None) - if isinstance(num_experts, int): - return num_experts > 1 - if isinstance(num_experts, list) and num_experts: - # Ensure all elements are integers before calling max. - if all(isinstance(e, int) for e in num_experts): - return max(num_experts) > 1 - else: - return False - return False - - -def _get_cla_factor(config: PretrainedConfig) -> int: - if not getattr(config, "use_cla", False): - return 1 - return getattr(config, "cla_share_factor", 1) - - -class HunYuanMLP(nn.Module): - def __init__( - self, - hidden_size: int, - intermediate_size: int, - hidden_act: str, - quant_config: QuantizationConfig | None = None, - bias: bool = False, - prefix: str = "", - reduce_results: bool = True, - ) -> None: - super().__init__() - self.gate_up_proj = MergedColumnParallelLinear( - input_size=hidden_size, - output_sizes=[intermediate_size] * 2, - bias=bias, - quant_config=quant_config, - prefix=f"{prefix}.gate_up_proj", - ) - self.down_proj = RowParallelLinear( - input_size=intermediate_size, - output_size=hidden_size, - bias=bias, - quant_config=quant_config, - prefix=f"{prefix}.down_proj", - reduce_results=reduce_results, - ) - if hidden_act != "silu": - raise ValueError( - f"Unsupported activation: {hidden_act}. Only silu is supported for now." - ) - self.act_fn = SiluAndMul() - - def forward(self, x): - gate_up, _ = self.gate_up_proj(x) - x = self.act_fn(gate_up) - x, _ = self.down_proj(x) - return x - - -class HunYuanAttention(nn.Module): - def __init__( - self, - config: PretrainedConfig, - hidden_size: int, - num_heads: int, - num_kv_heads: int, - max_position_embeddings: int = 8192, - quant_config: QuantizationConfig | None = None, - bias: bool = False, - cache_config: CacheConfig | None = None, - prefix: str = "", - layer_id: int = -1, - ) -> None: - super().__init__() - self.hidden_size = hidden_size - tp_size = get_tensor_model_parallel_world_size() - self.total_num_heads = num_heads - assert self.total_num_heads % tp_size == 0 - self.num_heads = self.total_num_heads // tp_size - self.total_num_kv_heads = num_kv_heads - if self.total_num_kv_heads >= tp_size: - # Number of KV heads is greater than TP size, so we partition - # the KV heads across multiple tensor parallel GPUs. - assert self.total_num_kv_heads % tp_size == 0 - else: - # Number of KV heads is less than TP size, so we replicate - # the KV heads across multiple tensor parallel GPUs. - assert tp_size % self.total_num_kv_heads == 0 - self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size) - - if hasattr(config, "head_dim") and config.head_dim: - self.head_dim = config.head_dim - elif hasattr(config, "attention_head_dim"): - self.head_dim = config.attention_head_dim - else: - self.head_dim = self.hidden_size // self.total_num_heads - self.q_size = self.num_heads * self.head_dim - self.kv_size = self.num_kv_heads * self.head_dim - self.scaling = self.head_dim**-0.5 - self.max_position_embeddings = max_position_embeddings - self.use_qk_norm = getattr(config, "use_qk_norm", False) - self.layer_id = layer_id - - self.qkv_proj = QKVParallelLinear( - hidden_size=hidden_size, - head_size=self.head_dim, - total_num_heads=self.total_num_heads, - total_num_kv_heads=self.total_num_kv_heads, - bias=bias, - quant_config=quant_config, - prefix=f"{prefix}.qkv_proj", - ) - - self.o_proj = RowParallelLinear( - input_size=self.total_num_heads * self.head_dim, - output_size=hidden_size, - bias=bias, - quant_config=quant_config, - prefix=f"{prefix}.o_proj", - ) - - self.rotary_emb = get_rope( - self.head_dim, - max_position=max_position_embeddings, - rope_parameters=config.rope_parameters, - is_neox_style=True, - ) - self.attn = Attention( - self.num_heads, - self.head_dim, - self.scaling, - num_kv_heads=self.num_kv_heads, - cache_config=cache_config, - quant_config=quant_config, - prefix=f"{prefix}.attn", - ) - - if self.use_qk_norm: - self.query_layernorm = RMSNorm(self.head_dim, eps=config.rms_norm_eps) - self.key_layernorm = RMSNorm(self.head_dim, eps=config.rms_norm_eps) - - def forward( - self, - positions: torch.Tensor, - hidden_states: torch.Tensor, - kv_states: tuple[torch.Tensor] | None = None, - ) -> torch.Tensor: - qkv, _ = self.qkv_proj(hidden_states) - q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1) - q, k = self.rotary_emb(positions, q, k) - ori_k = k - if self.use_qk_norm: - q = self.query_layernorm( - q.view(-1, self.num_heads, self.head_dim), - ) - k = self.key_layernorm( - k.view(-1, self.num_kv_heads, self.head_dim), - ) - - attn_output = self.attn(q, k, v) - # For o_proj - attn_output = attn_output.view(q.shape[0], -1) - output, _ = self.o_proj(attn_output) - return output, (ori_k, v) - - -class HunYuanCrossAttention(nn.Module): - def __init__( - self, - config: PretrainedConfig, - hidden_size: int, - num_heads: int, - num_kv_heads: int, - max_position_embeddings: int = 8192, - quant_config: QuantizationConfig | None = None, - bias: bool = False, - cache_config: CacheConfig | None = None, - prefix: str = "", - layer_id: int = -1, - ) -> None: - super().__init__() - self.hidden_size = hidden_size - tp_size = get_tensor_model_parallel_world_size() - self.total_num_heads = num_heads - assert self.total_num_heads % tp_size == 0 - self.num_heads = self.total_num_heads // tp_size - self.total_num_kv_heads = num_kv_heads - if self.total_num_kv_heads >= tp_size: - # Number of KV heads is greater than TP size, so we partition - # the KV heads across multiple tensor parallel GPUs. - assert self.total_num_kv_heads % tp_size == 0 - else: - # Number of KV heads is less than TP size, so we replicate - # the KV heads across multiple tensor parallel GPUs. - assert tp_size % self.total_num_kv_heads == 0 - self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size) - # MistralConfig has an optional head_dim introduced by Mistral-Nemo - if hasattr(config, "head_dim"): - self.head_dim = config.head_dim - elif hasattr(config, "attention_head_dim"): - self.head_dim = config.attention_head_dim - else: - self.head_dim = self.hidden_size // self.total_num_heads - self.q_size = self.num_heads * self.head_dim - self.kv_size = self.num_kv_heads * self.head_dim - self.scaling = self.head_dim**-0.5 - self.max_position_embeddings = max_position_embeddings - self.use_qk_norm = getattr(config, "use_qk_norm", False) - self.layer_id = layer_id - - self.q_proj = ColumnParallelLinear( - hidden_size, - hidden_size, - bias=bias, - quant_config=quant_config, - prefix=f"{prefix}.q_proj", - ) - - self.o_proj = RowParallelLinear( - input_size=self.total_num_heads * self.head_dim, - output_size=hidden_size, - bias=bias, - quant_config=quant_config, - prefix=f"{prefix}.o_proj", - ) - - self.rotary_emb = get_rope( - self.head_dim, - max_position=max_position_embeddings, - rope_parameters=config.rope_parameters, - is_neox_style=True, - ) - self.attn = Attention( - self.num_heads, - self.head_dim, - self.scaling, - num_kv_heads=self.num_kv_heads, - cache_config=cache_config, - quant_config=quant_config, - prefix=f"{prefix}.attn", - attn_type=AttentionType.ENCODER_DECODER, - ) - - if self.use_qk_norm: - self.query_layernorm = RMSNorm(self.head_dim, eps=config.rms_norm_eps) - self.key_layernorm = RMSNorm(self.head_dim, eps=config.rms_norm_eps) - - def forward( - self, - positions: torch.Tensor, - hidden_states: torch.Tensor, - kv_states: tuple[torch.Tensor] | None = None, - ) -> torch.Tensor: - assert kv_states is not None - ori_k, v = kv_states # use last layer kv, - k = ori_k - q, _ = self.q_proj(hidden_states) - k_tmp = torch.empty_like(k) # Todo: reduant rotary embedding - q, _ = self.rotary_emb(positions, q, k_tmp) - if self.use_qk_norm: - q = self.query_layernorm( - q.view(-1, self.num_heads, self.head_dim), - ) - k = self.key_layernorm( - k.view(-1, self.num_kv_heads, self.head_dim), - ) - - attn_output = self.attn(q, k, v) - # For o_proj - attn_output = attn_output.view(q.shape[0], -1) - output, _ = self.o_proj(attn_output) - return output, (ori_k, v) - - -class HunYuanSparseMoeBlock(nn.Module): - def __init__( - self, - config: PretrainedConfig, - quant_config: QuantizationConfig | None = None, - layer_id: int = -1, - prefix: str = "", - enable_eplb: bool = False, - ): - super().__init__() - self.tp_size = get_tensor_model_parallel_world_size() - - self.ep_group = get_ep_group().device_group - self.ep_size = self.ep_group.size() - self.n_routed_experts = config.num_experts - - if self.tp_size > config.num_experts: - raise ValueError( - f"Tensor parallel size {self.tp_size} is greater than " - f"the number of experts {config.num_experts}." - ) - - # Get layer_id topk if config.moe_topk is a list - if isinstance(config.moe_topk, list): - assert layer_id >= 0 - assert len(config.moe_topk) > layer_id - top_k = config.moe_topk[layer_id] - else: - top_k = config.moe_topk - - # If it is moe, moe_intermediate_size is preferred - intermediate_size = config.intermediate_size - if config.moe_intermediate_size is not None: - intermediate_size = ( - config.moe_intermediate_size - if isinstance(config.moe_intermediate_size, int) - else config.moe_intermediate_size[layer_id] - ) - - # Load balancing settings. - vllm_config = get_current_vllm_config() - eplb_config = vllm_config.parallel_config.eplb_config - self.enable_eplb = enable_eplb - - self.n_logical_experts = self.n_routed_experts - self.n_redundant_experts = eplb_config.num_redundant_experts - self.n_physical_experts = self.n_logical_experts + self.n_redundant_experts - self.n_local_physical_experts = self.n_physical_experts // self.ep_size - self.gate = ReplicatedLinear( - config.hidden_size, - config.num_experts, - bias=False, - quant_config=None, - prefix=f"{prefix}.gate", - ) - if config.use_mixed_mlp_moe > 0: - # Get layer_id num_shared_expert if config.num_shared_expert is - # a list. - if isinstance(config.num_shared_expert, list): - assert layer_id >= 0 - assert len(config.num_shared_expert) > layer_id - num_shared_expert = config.num_shared_expert[layer_id] - else: - num_shared_expert = config.num_shared_expert - - self.shared_mlp = HunYuanMLP( - hidden_size=config.hidden_size, - intermediate_size=config.intermediate_size * num_shared_expert, - hidden_act=config.hidden_act, - quant_config=quant_config, - reduce_results=False, - prefix=f"{prefix}.shared_mlp", - ) - else: - self.shared_mlp = None - - self.experts = FusedMoEFactory( - shared_experts=self.shared_mlp, - num_experts=self.n_routed_experts, - top_k=top_k, - hidden_size=config.hidden_size, - intermediate_size=intermediate_size, - renormalize=top_k > 1, - quant_config=quant_config, - prefix=f"{prefix}.experts", - enable_eplb=self.enable_eplb, - num_redundant_experts=self.n_redundant_experts, - ) - - def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: - # NOTE: hidden_states can have either 1D or 2D shape. - orig_shape = hidden_states.shape - hidden_dim = hidden_states.shape[-1] - hidden_states = hidden_states.view(-1, hidden_dim) - - # router_logits: (num_tokens, n_experts) - router_logits, _ = self.gate(hidden_states) - final_hidden_states = self.experts( - hidden_states=hidden_states, router_logits=router_logits - ) - - return final_hidden_states.view(orig_shape) - - -class HunYuanDecoderLayer(nn.Module): - def __init__( - self, - config: PretrainedConfig, - cache_config: CacheConfig | None = None, - quant_config: QuantizationConfig | None = None, - prefix: str = "", - layer_id: int = -1, - enable_eplb: bool = False, - ) -> None: - super().__init__() - assert layer_id >= 0 - self.layer_id = layer_id - self.hidden_size = config.hidden_size - self.intermediate_size = ( - config.intermediate_size - if isinstance(config.intermediate_size, int) - else config.intermediate_size[layer_id] - ) - max_position_embeddings = getattr(config, "max_position_embeddings", 8192) - attention_bias = getattr(config, "attention_bias", False) or getattr( - config, "bias", False - ) - cla_factor = _get_cla_factor(config) - attention_type = ( - AttentionType.ENCODER_DECODER - if layer_id >= 0 and layer_id % cla_factor != 0 - else AttentionType.DECODER - ) - if attention_type == AttentionType.DECODER: - self.self_attn = HunYuanAttention( - config=config, - hidden_size=self.hidden_size, - num_heads=config.num_attention_heads, - num_kv_heads=getattr( - config, "num_key_value_heads", config.num_attention_heads - ), - max_position_embeddings=max_position_embeddings, - quant_config=quant_config, - bias=attention_bias, - cache_config=cache_config, - prefix=f"{prefix}.self_attn", - layer_id=layer_id, - ) - elif attention_type == AttentionType.ENCODER_DECODER: - self.self_attn = HunYuanCrossAttention( - config=config, - hidden_size=self.hidden_size, - num_heads=config.num_attention_heads, - num_kv_heads=getattr( - config, "num_key_value_heads", config.num_attention_heads - ), - max_position_embeddings=max_position_embeddings, - quant_config=quant_config, - bias=attention_bias, - cache_config=cache_config, - prefix=f"{prefix}.self_attn", - layer_id=layer_id, - ) - else: - raise RuntimeError(f"Unsupported attention type: {attention_type}") - - if _is_moe(config): - self.mlp = HunYuanSparseMoeBlock( - config=config, - quant_config=quant_config, - layer_id=layer_id, - prefix=f"{prefix}.mlp", - enable_eplb=enable_eplb, - ) - else: - self.mlp = HunYuanMLP( - hidden_size=self.hidden_size, - intermediate_size=self.intermediate_size, - hidden_act=config.hidden_act, - quant_config=quant_config, - bias=getattr(config, "mlp_bias", False), - prefix=f"{prefix}.mlp", - ) - - self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps) - self.post_attention_layernorm = RMSNorm( - config.hidden_size, eps=config.rms_norm_eps - ) - - def forward( - self, - positions: torch.Tensor, - hidden_states: torch.Tensor, - residual: torch.Tensor | None, - kv_states: tuple[torch.Tensor] | None = None, - ) -> tuple[torch.Tensor, torch.Tensor]: - # Self Attention - if residual is None: - residual = hidden_states - hidden_states = self.input_layernorm(hidden_states) - else: - hidden_states, residual = self.input_layernorm(hidden_states, residual) - hidden_states, ori_kv_states = self.self_attn( - positions=positions, - hidden_states=hidden_states, - kv_states=kv_states, - ) - - # Fully Connected - hidden_states, residual = self.post_attention_layernorm(hidden_states, residual) - hidden_states = self.mlp(hidden_states) - return hidden_states, residual, ori_kv_states - - -@support_torch_compile( - dynamic_arg_dims={ - "input_ids": 0, - # positions is of shape (xd, seq_len) if xdrope is enabled for hunyuan-vl, - # otherwise (seq_len, ). - "positions": -1, - "intermediate_tensors": 0, - "inputs_embeds": 0, - } -) -class HunYuanModel(nn.Module, EagleModelMixin): - def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): - super().__init__() - - config = vllm_config.model_config.hf_config - cache_config = vllm_config.cache_config - quant_config = vllm_config.quant_config - - eplb_config = vllm_config.parallel_config.eplb_config - enable_eplb = vllm_config.parallel_config.enable_eplb - self.num_redundant_experts = eplb_config.num_redundant_experts - - self.config = config - self.quant_config = quant_config - - self.vocab_size = config.vocab_size - - if get_pp_group().is_first_rank or ( - config.tie_word_embeddings and get_pp_group().is_last_rank - ): - self.embed_tokens = VocabParallelEmbedding( - self.vocab_size, - config.hidden_size, - quant_config=quant_config, - ) - else: - self.embed_tokens = PPMissingLayer() - self.start_layer, self.end_layer, self.layers = make_layers( - config.num_hidden_layers, - lambda prefix: HunYuanDecoderLayer( - config=config, - layer_id=int(prefix.split(".")[-1]), - cache_config=cache_config, - quant_config=quant_config, - prefix=prefix, - enable_eplb=enable_eplb, - ), - prefix=f"{prefix}.layers", - ) - if get_pp_group().is_last_rank: - self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps) - else: - self.norm = PPMissingLayer() - - def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor: - return self.embed_tokens(input_ids) - - def forward( - self, - input_ids: torch.Tensor | None, - positions: torch.Tensor, - intermediate_tensors: IntermediateTensors | None, - inputs_embeds: torch.Tensor | None = None, - ) -> torch.Tensor | IntermediateTensors: - if get_pp_group().is_first_rank: - if inputs_embeds is not None: - hidden_states = inputs_embeds - else: - hidden_states = self.embed_input_ids(input_ids) - residual = None - else: - assert intermediate_tensors is not None - hidden_states = intermediate_tensors["hidden_states"] - residual = intermediate_tensors["residual"] - - cla_factor = _get_cla_factor(self.config) - prev_kv_states = None - aux_hidden_states = self._maybe_add_hidden_state([], 0, hidden_states, residual) - for i, layer in enumerate( - islice(self.layers, self.start_layer, self.end_layer) - ): - hidden_states, residual, kv_states = layer( - positions, - hidden_states, - residual, - prev_kv_states, - ) - - if getattr(self.config, "use_cla", False) and i % cla_factor == 0: - prev_kv_states = kv_states - else: - prev_kv_states = None - - self._maybe_add_hidden_state( - aux_hidden_states, i + 1, hidden_states, residual - ) - - if not get_pp_group().is_last_rank: - return IntermediateTensors( - {"hidden_states": hidden_states, "residual": residual} - ) - - hidden_states, _ = self.norm(hidden_states, residual) - - if len(aux_hidden_states) > 0: - return hidden_states, aux_hidden_states - return hidden_states - - def _split_qkv_weight(self, qkv: torch.Tensor): - num_attention_heads = self.config.num_attention_heads - num_kv_heads = getattr( - self.config, "num_key_value_heads", self.config.num_attention_heads - ) - num_key_value_groups = num_attention_heads // num_kv_heads - hidden_size = self.config.hidden_size - - if hasattr(self.config, "head_dim"): - attention_head_dim = self.config.head_dim - elif hasattr(self.config, "attention_head_dim"): - attention_head_dim = self.config.attention_head_dim - else: - attention_head_dim = self.config.hidden_size // num_attention_heads - - qkv = qkv.reshape( - num_kv_heads, num_key_value_groups + 2, attention_head_dim, hidden_size - ) - q, k, v = torch.split(qkv, (num_key_value_groups, 1, 1), dim=1) - q = q.reshape(-1, hidden_size) - k = k.reshape(-1, hidden_size) - v = v.reshape(-1, hidden_size) - return torch.concat((q, k, v)) - - def get_expert_mapping(self) -> list[tuple[str, str, int, str]]: - if _is_moe(self.config): - # Params for weights, fp8 weight scales, fp8 activation scales - # (param_name, weight_name, expert_id, shard_id) - return fused_moe_make_expert_params_mapping( - self, - ckpt_gate_proj_name="gate_proj", - ckpt_down_proj_name="down_proj", - ckpt_up_proj_name="up_proj", - num_experts=self.config.num_experts, - num_redundant_experts=self.num_redundant_experts, - ) - else: - return [] - - def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]): - cla_factor = _get_cla_factor(self.config) - stacked_params_mapping = [ - # (param_name, shard_name, shard_id) - (".qkv_proj", ".q_proj", "q"), - (".qkv_proj", ".k_proj", "k"), - (".qkv_proj", ".v_proj", "v"), - (".gate_up_proj", ".gate_proj", 0), - (".gate_up_proj", ".up_proj", 1), - ] - - num_attention_heads = self.config.num_attention_heads - num_kv_heads = getattr( - self.config, "num_key_value_heads", self.config.num_attention_heads - ) - split_params_mapping = [ - (".gate_up_proj", ".gate_and_up_proj", 2, [(1, 1), (0, 1)], None), - ( - ".qkv_proj", - ".qkv_proj", - num_attention_heads + num_kv_heads * 2, - [("q", num_attention_heads), ("k", num_kv_heads), ("v", num_kv_heads)], - self._split_qkv_weight, - ), - ] - - params_dict = dict(self.named_parameters()) - loaded_params: set[str] = set() - expert_params_mapping = self.get_expert_mapping() - for name, loaded_weight in weights: - if "rotary_emb.inv_freq" in name: - continue - if "gate_proj_bias" in name: - name = name.replace("gate_proj_bias", "gate_proj.bias") - if "up_proj_bias" in name: - name = name.replace("up_proj_bias", "up_proj.bias") - if "rotary_emb.cos_cached" in name or "rotary_emb.sin_cached" in name: - # Models trained using ColossalAI may include these tensors in - # the checkpoint. Skip them. - continue - # With tie_word_embeddings, we can skip lm_head.weight - # The weight might appear unnecessarily in the files if the model is - # processed with quantization, LoRA, fine-tuning, etc. - if self.config.tie_word_embeddings and "lm_head.weight" in name: - continue - - is_found = False - for param_name, weight_name, shard_id in stacked_params_mapping: - if weight_name not in name: - continue - if "mlp.experts" in name: - continue - # cross layer only have q_proj, skip qkv pack - if weight_name == ".q_proj": - match = re.search(r"layers\.\d+", name) - if match: - layer_id = int(match.group(0).split(".")[-1]) - if cla_factor > 1 and layer_id % cla_factor != 0: - continue - name = name.replace(weight_name, param_name) - # Skip loading extra bias for GPTQ models. - if name.endswith(".bias") and name not in params_dict: - continue - - if is_pp_missing_parameter(name, self): - continue - - param = params_dict[name] - weight_loader = param.weight_loader - weight_loader(param, loaded_weight, shard_id) - loaded_params.add(name) - is_found = True - break - if is_found: - continue - - for ( - param_name, - weight_name, - den, - split_param, - func, - ) in split_params_mapping: - if weight_name not in name: - continue - name = name.replace(weight_name, param_name) - # Skip loading extra bias for GPTQ models. - if name.endswith(".bias") and name not in params_dict: - continue - - if is_pp_missing_parameter(name, self): - continue - - assert loaded_weight.shape[0] % den == 0 - units = loaded_weight.shape[0] // den - - param = params_dict[name] - weight_loader = param.weight_loader - offset = 0 - for shard_id, num in split_param: - new_offset = offset + num * units - if func: - weight_loader( - param, func(loaded_weight)[offset:new_offset], shard_id - ) - else: - weight_loader(param, loaded_weight[offset:new_offset], shard_id) - offset = new_offset - - break - else: - # Skip loading extra bias for GPTQ models. - if name.endswith(".bias") and name not in params_dict: - continue - is_expert_weight = False - for mapping in expert_params_mapping: - param_name, weight_name, expert_id, shard_id = mapping - if weight_name not in name: - continue - # this is an expert weight and should not be - # attempted to load as other weights later - is_expert_weight = True - - # Do not modify `name` since the loop may continue here - # Instead, create a new variable - name_mapped = name.replace(weight_name, param_name) - if is_pp_missing_parameter(name_mapped, self): - continue - param = params_dict[name_mapped] - # We should ask the weight loader to return success or not - # here since otherwise we may skip experts with other - # available replicas. - weight_loader = typing.cast( - Callable[..., bool], param.weight_loader - ) - success = weight_loader( - param, - loaded_weight, - name_mapped, - shard_id=shard_id, - expert_id=expert_id, - return_success=True, - ) - if success: - name = name_mapped - break - else: - if is_expert_weight: - # We've checked that this is an expert weight - # However it's not mapped locally to this rank - # So we simply skip it - continue - # Remapping the name of FP8 kv-scale. - name = maybe_remap_kv_scale_name(name, params_dict) - if name is None: - continue - - if is_pp_missing_parameter(name, self): - continue - - if "mlp.gate.wg." in name: - name = name.replace("wg.", "") - - param = params_dict[name] - weight_loader = getattr( - param, "weight_loader", default_weight_loader - ) - weight_loader(param, loaded_weight) - loaded_params.add(name) - return loaded_params - - -class HunyuanV1ModelBase( - nn.Module, SupportsLoRA, SupportsPP, SupportsEagle, SupportsEagle3 -): - packed_modules_mapping = { - "qkv_proj": [ - "q_proj", - "k_proj", - "v_proj", - ], - "gate_up_proj": [ - "gate_proj", - "up_proj", - ], - } - - def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): - super().__init__() - - config = vllm_config.model_config.hf_config - quant_config = vllm_config.quant_config - self.config = config - self.quant_config = quant_config - - self.model = HunYuanModel( - vllm_config=vllm_config, - prefix=maybe_prefix(prefix, "model"), - ) - if get_pp_group().is_last_rank: - self.lm_head = ParallelLMHead( - config.vocab_size, - config.hidden_size, - quant_config=quant_config, - prefix=maybe_prefix(prefix, "lm_head"), - ) - if config.tie_word_embeddings: - self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens) - - logit_scale = getattr(config, "logit_scale", 1.0) - self.logits_processor = LogitsProcessor( - config.vocab_size, scale=logit_scale - ) - else: - self.lm_head = PPMissingLayer() - - def forward( - self, - input_ids: torch.Tensor | None, - positions: torch.Tensor, - intermediate_tensors: IntermediateTensors | None = None, - inputs_embeds: torch.Tensor | None = None, - ) -> torch.Tensor | IntermediateTensors: - model_output = self.model( - input_ids, positions, intermediate_tensors, inputs_embeds - ) - return model_output - - def compute_logits( - self, - hidden_states: torch.Tensor, - ) -> torch.Tensor | None: - logits = self.logits_processor(self.lm_head, hidden_states) - return logits - - def make_empty_intermediate_tensors( - self, batch_size: int, dtype: torch.dtype, device: torch.device - ) -> IntermediateTensors: - return IntermediateTensors( - { - "hidden_states": torch.zeros( - (batch_size, self.config.hidden_size), dtype=dtype, device=device - ), - "residual": torch.zeros( - (batch_size, self.config.hidden_size), dtype=dtype, device=device - ), - } - ) - - def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: - loader = AutoWeightsLoader(self) - return loader.load_weights(weights) - - def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor: - return self.model.embed_input_ids(input_ids) - - -class HunYuanMoEV1Base(HunyuanV1ModelBase, MixtureOfExperts): - def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): - super().__init__(vllm_config=vllm_config, prefix=prefix) - - # Set MoE hyperparameters - self.num_expert_groups = 1 - self.moe_layers = [] - example_layer = None - for layer in self.model.layers: - if isinstance(layer, PPMissingLayer): - continue - - assert isinstance(layer, HunYuanDecoderLayer) - if isinstance(layer.mlp, HunYuanSparseMoeBlock): - example_layer = layer.mlp - self.moe_layers.append(layer.mlp.experts) - - if example_layer is None: - raise RuntimeError("No HunYuanMoE layer found in model.layers.") - - self.num_moe_layers = len(self.moe_layers) - self.num_logical_experts = example_layer.n_logical_experts - self.num_physical_experts = example_layer.n_physical_experts - self.num_local_physical_experts = example_layer.n_local_physical_experts - self.num_routed_experts = example_layer.n_routed_experts - self.num_redundant_experts = example_layer.n_redundant_experts - - def update_physical_experts_metadata( - self, - num_physical_experts: int, - num_local_physical_experts: int, - ) -> None: - assert self.num_local_physical_experts == num_local_physical_experts - self.num_physical_experts = num_physical_experts - self.num_local_physical_experts = num_local_physical_experts - self.num_redundant_experts = num_physical_experts - self.num_logical_experts - for layer in self.model.layers: - if isinstance(layer.mlp, HunYuanSparseMoeBlock): - moe = layer.mlp - moe.n_local_physical_experts = num_local_physical_experts - moe.n_physical_experts = num_physical_experts - moe.n_redundant_experts = self.num_redundant_experts - moe.experts.update_expert_map() - - def get_expert_mapping(self) -> list[tuple[str, str, int, str]]: - return self.model.get_expert_mapping() - - -class HunYuanDenseV1Base(HunyuanV1ModelBase): - def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): - super().__init__(vllm_config=vllm_config, prefix=prefix) - - -class HunYuanDenseV1ForCausalLM(HunYuanDenseV1Base): - pass - - -class HunYuanMoEV1ForCausalLM(HunYuanMoEV1Base): - pass diff --git a/vllm/model_executor/models/hunyuan_vision.py b/vllm/model_executor/models/hunyuan_vision.py deleted file mode 100644 index 4454ce47c1..0000000000 --- a/vllm/model_executor/models/hunyuan_vision.py +++ /dev/null @@ -1,1047 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# SPDX-FileCopyrightText: Copyright contributors to the vLLM project - -# coding=utf-8 -# Copyright 2025 The HunYuan team. -# Copyright 2025 The vLLM team. -# Copyright 2025 EleutherAI and the HuggingFace Inc. team. All rights reserved. -# -# This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX -# and OPT implementations in this library. It has been modified from its -# original forms to accommodate minor architectural differences compared -# to GPT-NeoX and OPT used by the Meta AI team that trained the model. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Inference-only HunYuan-VL model compatible with HuggingFace weights.""" - -from collections.abc import Callable, Iterable, Mapping, Sequence -from functools import partial -from typing import Annotated, Any, Literal, TypeAlias - -import torch -import torch.nn as nn -import torch.nn.functional as F -from transformers import BatchFeature, HunYuanVLProcessor -from transformers.models.hunyuan_vl.image_processing_hunyuan_vl import ( - HunYuanVLImageProcessor, - smart_resize, -) - -from vllm.config import VllmConfig -from vllm.config.multimodal import BaseDummyOptions -from vllm.distributed import parallel_state -from vllm.distributed import utils as dist_utils -from vllm.inputs import ModalityData, MultiModalDataDict -from vllm.logger import init_logger -from vllm.model_executor.layers.activation import get_act_fn -from vllm.model_executor.layers.attention import MMEncoderAttention -from vllm.model_executor.layers.layernorm import RMSNorm -from vllm.model_executor.layers.linear import ( - ColumnParallelLinear, - QKVParallelLinear, - RowParallelLinear, -) -from vllm.model_executor.layers.quantization import QuantizationConfig -from vllm.model_executor.models.module_mapping import MultiModelKeys -from vllm.multimodal import MULTIMODAL_REGISTRY -from vllm.multimodal.inputs import ( - ImageItem, - MultiModalFeatureSpec, - MultiModalFieldConfig, - MultiModalKwargsItems, -) -from vllm.multimodal.parse import ( - DictEmbeddingItems, - ImageSize, - ModalityDataItems, - MultiModalDataItems, - MultiModalDataParser, -) -from vllm.multimodal.processing import ( - BaseDummyInputsBuilder, - BaseMultiModalProcessor, - BaseProcessingInfo, - PromptReplacement, - PromptUpdate, - PromptUpdateDetails, -) -from vllm.sequence import IntermediateTensors -from vllm.transformers_utils.configs.hunyuan_vl import ( - HunYuanVLConfig, - HunYuanVLVisionConfig, -) -from vllm.utils.tensor_schema import TensorSchema, TensorShape - -from .interfaces import ( - MultiModalEmbeddings, - SupportsEagle, - SupportsEagle3, - SupportsLoRA, - SupportsMultiModal, - SupportsPP, - SupportsQuant, - SupportsXDRoPE, -) -from .utils import ( - AutoWeightsLoader, - WeightsMapper, - init_vllm_registered_model, - maybe_prefix, -) -from .vision import is_vit_use_data_parallel - -logger = init_logger(__name__) - -# === Vision Inputs === # - - -class HunYuanVLImagePixelInputs(TensorSchema): - """ - Dimensions: - - np: Number of patches - - ni: Number of images - - cps: Number of channels * patch_size * patch_size - """ - - type: Literal["pixel_values"] - - pixel_values: Annotated[ - torch.Tensor, - TensorShape("np", "cps"), - ] - - image_grid_thw: Annotated[ - torch.Tensor, - TensorShape("ni", 3), - ] - - -class HunYuanVLImageEmbeddingInputs(TensorSchema): - """ - Dimensions: - - nf: Number of image features - - hs: Hidden size - - ni: Number of images - """ - - type: Literal["image_embeds"] - - image_embeds: Annotated[ - torch.Tensor, - TensorShape("nf", "hs"), - ] - - image_grid_thw: Annotated[ - torch.Tensor, - TensorShape("ni", 3), - ] - - -HunYuanVLImageInputs: TypeAlias = ( - HunYuanVLImagePixelInputs | HunYuanVLImageEmbeddingInputs -) - -# === Vision Encoder === # - - -class HunYuanVisionMLP(nn.Module): - def __init__( - self, - in_features: int, - hidden_features: int, - bias: bool = True, - act_fn: Callable[[torch.Tensor], torch.Tensor] = F.gelu, - quant_config: QuantizationConfig | None = None, - prefix: str = "", - ): - super().__init__() - use_data_parallel = is_vit_use_data_parallel() - self.dense_h_to_4h = ColumnParallelLinear( - in_features, - hidden_features, - bias=bias, - quant_config=quant_config, - prefix=f"{prefix}.dense_h_to_4h", - disable_tp=use_data_parallel, - ) - self.dense_4h_to_h = RowParallelLinear( - hidden_features, - in_features, - bias=bias, - quant_config=quant_config, - prefix=f"{prefix}.dense_4h_to_h", - disable_tp=use_data_parallel, - ) - self.act_fn = act_fn - - def forward(self, x: torch.Tensor): - x_up, _ = self.dense_h_to_4h(x) - x_down, _ = self.dense_4h_to_h(self.act_fn(x_up)) - return x_down - - -class HunYuanVisionAttention(nn.Module): - def __init__( - self, - embed_dim: int, - num_heads: int, - projection_size: int, - quant_config: QuantizationConfig | None = None, - prefix: str = "", - ) -> None: - super().__init__() - # Per attention head and per partition values. - use_data_parallel = is_vit_use_data_parallel() - self.tp_size = ( - 1 - if use_data_parallel - else parallel_state.get_tensor_model_parallel_world_size() - ) - self.hidden_size_per_attention_head = dist_utils.divide( - projection_size, num_heads - ) - self.num_attention_heads_per_partition = dist_utils.divide( - num_heads, self.tp_size - ) - - self.qkv = QKVParallelLinear( - hidden_size=embed_dim, - head_size=self.hidden_size_per_attention_head, - total_num_heads=num_heads, - total_num_kv_heads=num_heads, - bias=True, - quant_config=quant_config, - prefix=f"{prefix}.qkv", - disable_tp=use_data_parallel, - ) - - self.o_proj = RowParallelLinear( - input_size=projection_size, - output_size=embed_dim, - quant_config=quant_config, - prefix=f"{prefix}.o_proj", - disable_tp=use_data_parallel, - ) - - self.scale = self.hidden_size_per_attention_head**-0.5 - self.attn = MMEncoderAttention( - self.num_attention_heads_per_partition, - self.hidden_size_per_attention_head, - self.scale, - prefix=f"{prefix}.attn", - ) - - def forward( - self, - x: torch.Tensor, - ) -> torch.Tensor: - qkv, _ = self.qkv(x) - q, k, v = qkv.chunk(3, dim=-1) - out = self.attn(q, k, v) - output, _ = self.o_proj(out) - return output - - -class HunYuanVisionBlock(nn.Module): - def __init__( - self, - dim: int, - num_heads: int, - mlp_hidden_dim: int, - act_fn: Callable[[torch.Tensor], torch.Tensor] = F.gelu, - norm_layer: Callable[[int], nn.Module] | None = None, - quant_config: QuantizationConfig | None = None, - prefix: str = "", - ) -> None: - super().__init__() - if norm_layer is None: - norm_layer = partial(nn.LayerNorm, eps=1e-6) - self.input_layernorm = norm_layer(dim) - self.post_attention_layernorm = norm_layer(dim) - self.self_attn = HunYuanVisionAttention( - embed_dim=dim, - num_heads=num_heads, - projection_size=dim, - quant_config=quant_config, - prefix=f"{prefix}.self_attn", - ) - self.mlp = HunYuanVisionMLP( - dim, - mlp_hidden_dim, - act_fn=act_fn, - bias=True, - quant_config=quant_config, - prefix=f"{prefix}.mlp", - ) - - def forward( - self, - x: torch.Tensor, - ) -> torch.Tensor: - x = x + self.self_attn(self.input_layernorm(x)) - x = x + self.mlp(self.post_attention_layernorm(x)) - return x - - -class HunYuanVisionPatchEmbed(nn.Module): - def __init__(self, config: HunYuanVLVisionConfig): - super().__init__() - - self.config = config - self.embed_dim = config.hidden_size - self.patch_size = config.patch_size - self.num_channels = config.num_channels - self.spatial_merge_size = config.spatial_merge_size - self.interpolate_mode = config.interpolate_mode - - self.patch_embedding = nn.Conv2d( - in_channels=config.num_channels, - out_channels=self.embed_dim, - kernel_size=self.patch_size, - stride=self.patch_size, - bias=True, - ) - - self.max_num_patches = (config.max_image_size // self.patch_size) ** 2 - - self.num_positions = self.max_num_patches + 1 - self.position_edge = int(self.num_positions**0.5) - # first token is cls token, skip it - self.position_embedding = nn.Embedding(self.num_positions, self.embed_dim) - - self.patch_pos_embed = None - - def forward( - self, pixel_values: torch.Tensor, grid_thw: list[list[int]] - ) -> torch.Tensor: - num_patches = pixel_values.size(0) - pixel_values = pixel_values.reshape( - num_patches, self.num_channels, self.patch_size, self.patch_size - ) - - patch_embeds = self.patch_embedding(pixel_values) - patch_embeds = patch_embeds.squeeze(-1).squeeze(-1).unsqueeze(0) - - if self.patch_pos_embed is None: - patch_pos_shape = ( - 1, - self.position_edge, - self.position_edge, - self.embed_dim, - ) - self.patch_pos_embed = ( - self.position_embedding.weight[1:, :] - .reshape(patch_pos_shape) - .permute(0, 3, 1, 2) - .float() - ) - - patch_pos_embed_list = [] - for grid in grid_thw: - _, h0, w0 = grid - # we add a small number to avoid floating point error in the interpolation - # see discussion at https://github.com/facebookresearch/dino/issues/8 - h0, w0 = h0 + 0.1, w0 + 0.1 - patch_pos_embed = nn.functional.interpolate( - self.patch_pos_embed, - scale_factor=(h0 / self.position_edge, w0 / self.position_edge), - mode=self.interpolate_mode, - align_corners=False, - ) - - patch_pos_embed = ( - patch_pos_embed.reshape(self.embed_dim, -1) - .transpose(0, 1) - .unsqueeze(0) - .to(patch_embeds.dtype) - ) - patch_pos_embed_list.append(patch_pos_embed) - - patch_pos_embed = torch.cat(patch_pos_embed_list, dim=1) - embeddings = patch_embeds + patch_pos_embed - - return embeddings - - -class HunYuanVisionPatchMerger(nn.Module): - def __init__( - self, - in_channels, - out_channels, - spatial_merge_size=2, - rms_norm_eps=1e-5, - prefix="", - ): - super().__init__() - self.spatial_merge_size = spatial_merge_size - embed_std = out_channels**-0.5 - - self.proj = nn.Sequential( - nn.Conv2d( - in_channels, - in_channels * 2, - kernel_size=spatial_merge_size, - stride=spatial_merge_size, - ), - nn.GELU(), - nn.Conv2d(in_channels * 2, in_channels * 4, kernel_size=1), - ) - self.mlp = nn.Linear(in_channels * 4, out_channels) - - self.image_newline = nn.Parameter(torch.randn(in_channels * 4) * embed_std) - self.image_begin = nn.Parameter(torch.randn(out_channels) * embed_std) - self.image_end = nn.Parameter(torch.randn(out_channels) * embed_std) - self.image_sep = nn.Parameter(torch.randn(out_channels) * embed_std) - - self.before_rms = RMSNorm(in_channels, eps=rms_norm_eps) - self.after_rms = RMSNorm(out_channels, eps=rms_norm_eps) - - def forward(self, x, size=(16, 16)): - x = self.before_rms(x) - - h, w = size - dtype = x.dtype - x = x.permute(0, 2, 1).reshape(x.shape[0], -1, h, w) - - x = self.proj(x) # b,c,h,w - b, c, h, w = x.shape - x = torch.cat( - [x, self.image_newline.reshape(1, c, 1, 1).expand(b, c, h, 1).to(dtype)], - dim=-1, - ) - x = x.reshape(b, c, -1).permute(0, 2, 1) - x = self.mlp(x) - - begin = self.image_begin.reshape(1, 1, -1).expand(b, 1, x.shape[-1]).to(dtype) - end = self.image_end.reshape(1, 1, -1).expand(b, 1, x.shape[-1]).to(dtype) - x = torch.cat([begin, x, end], dim=1) - - return self.after_rms(x) - - -class HunYuanVisionTransformer(nn.Module): - hf_to_vllm_mapper = WeightsMapper( - orig_to_new_stacked={ - ".q_proj": (".qkv", "q"), - ".k_proj": (".qkv", "k"), - ".v_proj": (".qkv", "v"), - } - ) - - def __init__( - self, - vision_config: HunYuanVLVisionConfig, - quant_config: QuantizationConfig | None = None, - prefix: str = "", - ) -> None: - super().__init__() - - num_hidden_layers = vision_config.num_hidden_layers - self.hidden_size = vision_config.hidden_size - self.num_heads = vision_config.num_attention_heads - self.spatial_merge_size = vision_config.spatial_merge_size - - from vllm.compilation.backends import set_model_tag - - with set_model_tag("HunYuanVisionPatchEmbed"): - self.embeddings = HunYuanVisionPatchEmbed(vision_config) - - norm_layer = partial(nn.LayerNorm, eps=vision_config.rms_norm_eps) - - with set_model_tag("HunYuanVisionBlock"): - self.layers = nn.ModuleList( - [ - HunYuanVisionBlock( - dim=vision_config.hidden_size, - num_heads=vision_config.num_attention_heads, - mlp_hidden_dim=vision_config.intermediate_size, - act_fn=get_act_fn(vision_config.hidden_act), - norm_layer=norm_layer, - quant_config=quant_config, - prefix=f"{prefix}.layers.{layer_idx}", - ) - for layer_idx in range(num_hidden_layers) - ] - ) - - with set_model_tag("HunYuanVisionPatchMerger"): - self.perceive = HunYuanVisionPatchMerger( - vision_config.hidden_size, - vision_config.out_hidden_size, - spatial_merge_size=vision_config.spatial_merge_size, - rms_norm_eps=vision_config.rms_norm_eps, - prefix=f"{prefix}.perceive", - ) - - @property - def dtype(self) -> torch.dtype: - return self.embeddings.patch_embedding.weight.dtype - - @property - def device(self) -> torch.device: - return self.embeddings.patch_embedding.weight.device - - def forward( - self, - x: torch.Tensor, - grid_thw: list[list[int]], - ) -> torch.Tensor: - # patchify - seq_len = x.size(0) - cu_seqlens: list = [0] - - hidden_states = x.to(device=self.device, dtype=self.dtype) - # embeddings = patch_embeds + patch_pos_embed - hidden_states = self.embeddings(hidden_states, grid_thw) - - for t, h, w in grid_thw: - t, h, w = int(t), int(h), int(w) - cu_seqlens.append(h * w) - - cu_seqlens = torch.tensor(cu_seqlens, dtype=torch.int32) - cu_seqlens = torch.cumsum(cu_seqlens, dim=0, dtype=torch.int32) - - cu_seqlens = cu_seqlens.to(device=self.device, non_blocking=True) - - hidden_states = hidden_states.reshape(seq_len, -1) - hidden_states = hidden_states.unsqueeze(0) - - # build per-image lengths once - split_lengths = [int(h) * int(w) for (_, h, w) in grid_thw] - for layer in self.layers: - # hidden_states: (1, T_total, D) - parts = hidden_states.split(split_lengths, dim=1) # list of (1, L_i, D) - parts = [layer(p) for p in parts] - hidden_states = torch.cat(parts, dim=1) - - # adapter - split_lengths = (cu_seqlens[1:] - cu_seqlens[:-1]).tolist() - split_items = hidden_states.split(split_lengths, dim=1) - image_embeds_list = [] - for grid, split_item in zip(grid_thw, split_items): - image_embeds_list.append( - self.perceive(split_item.contiguous(), size=grid[1:]).squeeze(0) - ) - - return image_embeds_list - - def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: - loader = AutoWeightsLoader(self) - return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper) - - -def _hunyuan_vl_field_config(hf_inputs: Mapping[str, torch.Tensor]): - image_grid_thw = hf_inputs.get("image_grid_thw", torch.empty((0, 3))) - image_grid_sizes = image_grid_thw.prod(-1) - return dict( - pixel_values=MultiModalFieldConfig.flat_from_sizes("image", image_grid_sizes), - image_embeds=MultiModalFieldConfig.flat_from_sizes("image", image_grid_sizes), - image_grid_thw=MultiModalFieldConfig.batched("image", keep_on_cpu=True), - ) - - -class HunYuanVLMultiModalDataParser(MultiModalDataParser): - # The patch grid is what sizes the placeholder range. - embedding_fields = { - "image": {"image_embeds": "values", "image_grid_thw": "metadata"}, - } - - def _parse_image_data( - self, - data: dict[str, torch.Tensor] | ModalityData[ImageItem], - ) -> ModalityDataItems[Any, Any] | None: - if isinstance(data, dict): - required, optional = self.embedding_field_sets("image") - return DictEmbeddingItems( - data, - modality="image", - required_fields=required, - optional_fields=optional, - fields_factory=_hunyuan_vl_field_config, - ) - - return super()._parse_image_data(data) - - -class HunYuanVLProcessingInfo(BaseProcessingInfo): - def get_hf_config(self): - return self.ctx.get_hf_config(HunYuanVLConfig) - - def get_hf_processor( - self, - **kwargs: object, - ) -> HunYuanVLProcessor: - # transformers>=5.13 replaced `use_fast` with `backend`; pin the - # PIL backend to match the released HunyuanOCR checkpoint packing. - kwargs.pop("use_fast", None) - kwargs.setdefault("backend", "pil") - return self.ctx.get_hf_processor( - HunYuanVLProcessor, - **kwargs, - ) - - def get_image_processor( - self, - **kwargs: object, - ) -> HunYuanVLImageProcessor: - return self.get_hf_processor(**kwargs).image_processor - - def get_data_parser(self): - return HunYuanVLMultiModalDataParser( - expected_hidden_size=self._get_expected_hidden_size(), - allow_missing_mm_embeddings=self.allow_missing_mm_embeddings, - ) - - def get_supported_mm_limits(self) -> Mapping[str, int | None]: - return {"image": None} - - def get_mm_max_tokens_per_item( - self, - seq_len: int, - mm_counts: Mapping[str, int], - ) -> Mapping[str, int]: - max_image_tokens = self.get_max_image_tokens() - # TODO: support video - max_video_tokens = 0 - return {"image": max_image_tokens, "video": max_video_tokens} - - def _get_vision_info( - self, - *, - image_width: int, - image_height: int, - num_frames: int = 1, - do_resize: bool = True, - image_processor: HunYuanVLImageProcessor, - mm_kwargs: Mapping[str, object], - ) -> tuple[ImageSize, int]: - hf_config = self.get_hf_config() - vision_config = hf_config.vision_config - patch_size = vision_config.patch_size - spatial_merge_size = vision_config.spatial_merge_size - - mm_kwargs = self.ctx.get_merged_mm_kwargs(mm_kwargs) - size = image_processor.size - if override_size := mm_kwargs.get("size"): - size = size | override_size - if (override_min_pixels := mm_kwargs.get("min_pixels")) is not None: - size = size | {"shortest_edge": override_min_pixels} - if (override_max_pixels := mm_kwargs.get("max_pixels")) is not None: - size = size | {"longest_edge": override_max_pixels} - - if do_resize: - resized_height, resized_width = smart_resize( - height=image_height, - width=image_width, - factor=patch_size * spatial_merge_size, - min_pixels=size["shortest_edge"], - max_pixels=size["longest_edge"], - ) - preprocessed_size = ImageSize(width=resized_width, height=resized_height) - else: - preprocessed_size = ImageSize(width=image_width, height=image_height) - - grid_t = 1 - grid_h = preprocessed_size.height // patch_size - grid_w = preprocessed_size.width // patch_size - - num_vision_tokens = ( - grid_t * grid_h // spatial_merge_size * (grid_w // spatial_merge_size + 1) - + 2 - ) - - return preprocessed_size, num_vision_tokens - - def get_num_image_tokens( - self, - *, - image_width: int, - image_height: int, - image_processor: HunYuanVLImageProcessor, - mm_kwargs: Mapping[str, object], - ) -> int: - _, num_image_tokens = self._get_vision_info( - image_width=image_width, - image_height=image_height, - image_processor=image_processor, - mm_kwargs=mm_kwargs, - ) - return num_image_tokens - - def get_image_size_with_most_features(self) -> ImageSize: - image_processor = self.get_image_processor() - - max_image_size, _ = self._get_vision_info( - image_width=512, - image_height=8192, - image_processor=image_processor, - mm_kwargs={}, - ) - return max_image_size - - def get_max_image_tokens(self) -> int: - image_processor = self.get_image_processor() - target_width, target_height = self.get_image_size_with_most_features() - - return self.get_num_image_tokens( - image_width=target_width, - image_height=target_height, - image_processor=image_processor, - mm_kwargs={}, - ) - - -class HunYuanVLDummyInputsBuilder(BaseDummyInputsBuilder[HunYuanVLProcessingInfo]): - def get_dummy_text(self, mm_counts: Mapping[str, int]) -> str: - num_images = mm_counts.get("image", 0) - - hf_processor = self.info.get_hf_processor(typ=HunYuanVLProcessor) - image_placeholder = ( - f"{hf_processor.image_start_token}{hf_processor.image_token}" - f"{hf_processor.image_end_token}" - ) - - return image_placeholder * num_images - - def get_dummy_mm_data( - self, - seq_len: int, - mm_counts: Mapping[str, int], - mm_options: Mapping[str, BaseDummyOptions], - ) -> MultiModalDataDict: - num_images = mm_counts.get("image", 1) - - target_width, target_height = self.info.get_image_size_with_most_features() - - return { - "image": self._get_dummy_images( - width=target_width, height=target_height, num_images=num_images - ), - } - - -class HunYuanVLMultiModalProcessor(BaseMultiModalProcessor[HunYuanVLProcessingInfo]): - def _call_hf_processor( - self, - prompt: str, - mm_data: Mapping[str, object], - mm_kwargs: Mapping[str, object], - ) -> BatchFeature: - hf_processor = self.info.get_hf_processor(**mm_kwargs) - # HunYuanVLProcessor requires image placeholders wrapped with start/end tokens. - if mm_data.get("images") is not None and prompt: - img_tok = hf_processor.image_token - wrapped = ( - f"{hf_processor.image_start_token}{img_tok}" - f"{hf_processor.image_end_token}" - ) - if img_tok in prompt and wrapped not in prompt: - prompt = prompt.replace(img_tok, wrapped) - return self.info.ctx.call_hf_processor( - hf_processor, - dict(text=prompt, **mm_data), - mm_kwargs, - ) - - def _get_prompt_updates( - self, - mm_items: MultiModalDataItems, - hf_processor_mm_kwargs: Mapping[str, Any], - out_mm_kwargs: MultiModalKwargsItems, - ) -> Sequence[PromptUpdate]: - hf_processor = self.info.get_hf_processor(**hf_processor_mm_kwargs) - image_processor = self.info.get_image_processor(**hf_processor_mm_kwargs) - - token_ids = { - "image": hf_processor.image_token_id, - "image_start": hf_processor.image_start_token_id, - "image_end": hf_processor.image_end_token_id, - } - - merge_size = image_processor.merge_size - - def get_replacement_hunyuan_vl(item_idx: int, modality: str): - out_item = out_mm_kwargs[modality][item_idx] - grid_thw = out_item[f"{modality}_grid_thw"].data - assert isinstance(grid_thw, torch.Tensor) - - _, grid_h, grid_w = grid_thw - num_tokens = (int(grid_h) // merge_size) * ( - int(grid_w) // merge_size + 1 - ) + 2 - tokens = ( - [token_ids[f"{modality}_start"]] - + [token_ids[modality]] * num_tokens - + [token_ids[f"{modality}_end"]] - ) - return PromptUpdateDetails.select_token_id(tokens, token_ids[modality]) - - return [ - PromptReplacement( - modality=modality, - target=[ - token_ids[f"{modality}_start"], - token_ids[modality], - token_ids[f"{modality}_end"], - ], - replacement=partial(get_replacement_hunyuan_vl, modality=modality), - ) - for modality in ("image",) - ] - - def _get_mm_fields_config( - self, - hf_inputs: BatchFeature, - hf_processor_mm_kwargs: Mapping[str, object], - ) -> Mapping[str, MultiModalFieldConfig]: - return _hunyuan_vl_field_config(hf_inputs) - - -@MULTIMODAL_REGISTRY.register_processor( - HunYuanVLMultiModalProcessor, - info=HunYuanVLProcessingInfo, - dummy_inputs=HunYuanVLDummyInputsBuilder, -) -class HunYuanVLForConditionalGeneration( - nn.Module, - SupportsMultiModal, - SupportsLoRA, - SupportsPP, - SupportsQuant, - SupportsXDRoPE, - SupportsEagle, - SupportsEagle3, -): - # To ensure correct weight loading and mapping. - hf_to_vllm_mapper = WeightsMapper( - orig_to_new_prefix={ - # mapping for new names in checkpoint saved after transformers v4.52 - "vit.vit.": "visual.", - "vit.": "visual.", - "model.": "language_model.model.", - } - ) - - supports_encoder_tp_data = True - - def get_xdrope_input_positions( - self, - input_tokens: list[int], - mm_features: list[MultiModalFeatureSpec], - ) -> torch.Tensor: - kwargs = MultiModalFeatureSpec.gather_kwargs( - mm_features, - {"image_grid_thw"}, - ) - image_grid_thw = [item.tolist() for item in kwargs.get("image_grid_thw", [])] - - hf_config = self.config - image_start_token_id = hf_config.image_start_token_id - spatial_merge_size = hf_config.vision_config.spatial_merge_size - xd_num = len(hf_config.rope_scaling["xdrope_section"]) - - input_tokens_tensor = torch.tensor(input_tokens) - image_start_indices = torch.argwhere( - input_tokens_tensor == image_start_token_id - ).squeeze(1) - - p_index = torch.arange(len(input_tokens_tensor)) - w_index = torch.arange(len(input_tokens_tensor)) - h_index = torch.arange(len(input_tokens_tensor)) - t_index = torch.arange(len(input_tokens_tensor)) - for image_index in range(len(image_start_indices)): - # +1 : first image_token, +2: for xdrope positions - pos = image_start_indices[image_index] + 2 - t, h, w = image_grid_thw[image_index] - _, llm_grid_h, llm_grid_w = ( - t, - h // spatial_merge_size, - w // spatial_merge_size, - ) - - token_num = (llm_grid_w + 1) * llm_grid_h - w_index[pos : pos + token_num].copy_( - torch.arange(0, llm_grid_w + 1) - .reshape(1, -1) - .expand(llm_grid_h, -1) - .reshape(-1) - ) - h_index[pos : pos + token_num].copy_( - torch.arange(0, llm_grid_h) - .reshape(-1, 1) - .expand(-1, llm_grid_w + 1) - .reshape(-1) - ) - t_index[pos : pos + token_num] = image_index - - if xd_num == 4: - llm_positions = torch.stack([p_index, w_index, h_index, t_index]) - elif xd_num == 3: - llm_positions = torch.stack([w_index, h_index, t_index]) - - return llm_positions - - @classmethod - def get_placeholder_str(cls, modality: str, i: int) -> str | None: - if modality.startswith("image"): - return "<|hy_place▁holder▁no▁100|><|hy_place▁holder▁no▁102|><|hy_place▁holder▁no▁101|>" # noqa: E501 - - raise ValueError("Only image modality is supported") - - def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): - super().__init__() - config: HunYuanVLConfig = vllm_config.model_config.hf_config - - self.config = config - - with self._mark_tower_model(vllm_config, {"image"}): - self.visual = HunYuanVisionTransformer( - config.vision_config, - quant_config=vllm_config.quant_config, - prefix=maybe_prefix(prefix, "visual"), - ) - - with self._mark_language_model(vllm_config): - self.language_model = init_vllm_registered_model( - vllm_config=vllm_config, - prefix=maybe_prefix(prefix, "language_model.model"), - architectures=[ - "HunYuanDenseV1ForCausalLM", - "HunYuanMoEV1ForCausalLM", - ], - ) - - self.make_empty_intermediate_tensors = ( - self.language_model.make_empty_intermediate_tensors - ) - - def _parse_and_validate_image_input( - self, **kwargs: object - ) -> HunYuanVLImageInputs | None: - pixel_values = kwargs.pop("pixel_values", None) - image_embeds = kwargs.pop("image_embeds", None) - image_grid_thw = kwargs.pop("image_grid_thw", None) - - if pixel_values is None and image_embeds is None: - return None - - # TODO: refine - if isinstance(pixel_values, list): - pixel_values = torch.cat(pixel_values, dim=0) - if len(pixel_values.shape) == 3: - last_dim = pixel_values.shape[-1] - pixel_values = pixel_values.reshape(-1, last_dim) - image_grid_thw = image_grid_thw.reshape(-1, 3) - - if pixel_values is not None: - return HunYuanVLImagePixelInputs( - type="pixel_values", - pixel_values=pixel_values, - image_grid_thw=image_grid_thw, - ) - - if image_embeds is not None: - return HunYuanVLImageEmbeddingInputs( - type="image_embeds", - image_embeds=image_embeds, - image_grid_thw=image_grid_thw, - ) - - def _process_image_input( - self, image_input: HunYuanVLImageInputs - ) -> tuple[torch.Tensor, ...]: - grid_thw = image_input["image_grid_thw"] - assert grid_thw.ndim == 2 - grid_thw_list = grid_thw.tolist() - - if image_input["type"] == "image_embeds": - image_embeds = image_input["image_embeds"].type(self.visual.dtype) - else: - pixel_values = image_input["pixel_values"] - - # TODO: use_data_parallel (split image_embeds in visual) - image_embeds = self.visual(pixel_values, grid_thw=grid_thw_list) - - return image_embeds - - def _parse_and_validate_multimodal_inputs(self, **kwargs: object) -> dict: - mm_input_by_modality = {} - - # Preserve the order of modalities if there are multiple of them - # from the order of kwargs. - for input_key in kwargs: - if ( - input_key in ("pixel_values", "image_embeds") - and "image" not in mm_input_by_modality - ): - mm_input_by_modality["image"] = self._parse_and_validate_image_input( - **kwargs - ) - return mm_input_by_modality - - def embed_multimodal(self, **kwargs: object) -> MultiModalEmbeddings: - mm_input_by_modality = self._parse_and_validate_multimodal_inputs(**kwargs) - if not mm_input_by_modality: - return [] - - # The result multimodal_embeddings is tuple of tensors, with each - # tensor correspoending to a multimodal data item (image or video). - multimodal_embeddings: tuple[torch.Tensor, ...] = () - - # NOTE: It is important to iterate over the keys in this dictionary - # to preserve the order of the modalities. - for modality in mm_input_by_modality: - multimodal_input = mm_input_by_modality[modality] - if modality == "image": - image_embeddings = self._process_image_input(multimodal_input) - multimodal_embeddings += tuple(image_embeddings) - return multimodal_embeddings - - def forward( - self, - input_ids: torch.Tensor | None, - positions: torch.Tensor, - intermediate_tensors: IntermediateTensors | None, - inputs_embeds: torch.Tensor | None, - **kwargs: object, - ) -> torch.Tensor | IntermediateTensors: - if intermediate_tensors is not None: - inputs_embeds = None - - hidden_states = self.language_model( - input_ids=input_ids, - positions=positions, - intermediate_tensors=intermediate_tensors, - inputs_embeds=inputs_embeds, - ) - return hidden_states - - def compute_logits( - self, - hidden_states: torch.Tensor, - ) -> torch.Tensor | None: - return self.language_model.compute_logits(hidden_states) - - def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: - loader = AutoWeightsLoader(self) - return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper) - - def get_mm_mapping(self) -> MultiModelKeys: - """ - Get the module prefix in multimodal models - """ - return MultiModelKeys.from_string_field( - language_model="language_model.model", - connector="visual.perceive", - tower_model="visual", - ) diff --git a/vllm/model_executor/models/registry.py b/vllm/model_executor/models/registry.py index 24fc66a089..d8e10ba75f 100644 --- a/vllm/model_executor/models/registry.py +++ b/vllm/model_executor/models/registry.py @@ -131,8 +131,6 @@ _TEXT_GENERATION_MODELS = { "GraniteSWAForCausalLM": ("granite", "GraniteForCausalLM"), "GritLM": ("gritlm", "GritLM"), "HrmTextForCausalLM": ("hrm_text", "HrmTextForCausalLM"), - "HunYuanMoEV1ForCausalLM": ("hunyuan_v1", "HunYuanMoEV1ForCausalLM"), - "HunYuanDenseV1ForCausalLM": ("hunyuan_v1", "HunYuanDenseV1ForCausalLM"), "HYV3ForCausalLM": ("hy_v3", "HYV3ForCausalLM"), "HCXVisionForCausalLM": ("hyperclovax_vision", "HCXVisionForCausalLM"), "HCXVisionV2ForCausalLM": ("hyperclovax_vision_v2", "HCXVisionV2ForCausalLM"), @@ -441,10 +439,6 @@ _MULTIMODAL_MODELS = { "Granite4VisionForConditionalGeneration", ), "H2OVLChatModel": ("h2ovl", "H2OVLChatModel"), - "HunYuanVLForConditionalGeneration": ( - "hunyuan_vision", - "HunYuanVLForConditionalGeneration", - ), "InternVLChatModel": ("internvl", "InternVLChatModel"), "InternS1ForConditionalGeneration": ( "interns1", @@ -700,6 +694,8 @@ _SPECULATIVE_DECODING_MODELS = { _TRANSFORMERS_SUPPORTED_MODELS = { # Text generation models "GPTBigCodeForCausalLM": ("transformers", "TransformersForCausalLM"), + "HunYuanDenseV1ForCausalLM": ("transformers", "TransformersForCausalLM"), + "HunYuanMoEV1ForCausalLM": ("transformers", "TransformersMoEForCausalLM"), "OlmoForCausalLM": ("transformers", "TransformersForCausalLM"), "Olmo2ForCausalLM": ("transformers", "TransformersForCausalLM"), "SmolLM3ForCausalLM": ("transformers", "TransformersForCausalLM"), @@ -710,6 +706,10 @@ _TRANSFORMERS_SUPPORTED_MODELS = { "transformers", "TransformersMultiModalForCausalLM", ), + "HunYuanVLForConditionalGeneration": ( + "transformers", + "TransformersMultiModalForCausalLM", + ), "VibeVoiceAsrForConditionalGeneration": ( "transformers", "TransformersMultiModalForCausalLM", diff --git a/vllm/transformers_utils/config.py b/vllm/transformers_utils/config.py index 26d4bd6e1b..d58a386f58 100644 --- a/vllm/transformers_utils/config.py +++ b/vllm/transformers_utils/config.py @@ -95,7 +95,6 @@ _CONFIG_REGISTRY: dict[str, type[PretrainedConfig]] = LazyConfigDict( granite4_vision="Granite4VisionConfig", hyperclovax="HyperCLOVAXConfig", hyperclovax_vlm="HCXVisionConfig", - hunyuan_vl="HunYuanVLConfig", hy_v3="HYV3Config", isaac="IsaacConfig", kimi_k2="DeepseekV3Config", # Kimi K2 uses same architecture as DeepSeek V3 diff --git a/vllm/transformers_utils/configs/__init__.py b/vllm/transformers_utils/configs/__init__.py index c38cc4b4b8..d0b3b962e4 100644 --- a/vllm/transformers_utils/configs/__init__.py +++ b/vllm/transformers_utils/configs/__init__.py @@ -44,9 +44,6 @@ _CLASS_TO_MODULE: dict[str, str] = { "FunAudioChatConfig": "vllm.transformers_utils.configs.funaudiochat", "FunAudioChatAudioEncoderConfig": "vllm.transformers_utils.configs.funaudiochat", "Granite4VisionConfig": "vllm.transformers_utils.configs.granite4_vision", - "HunYuanVLConfig": "vllm.transformers_utils.configs.hunyuan_vl", - "HunYuanVLTextConfig": "vllm.transformers_utils.configs.hunyuan_vl", - "HunYuanVLVisionConfig": "vllm.transformers_utils.configs.hunyuan_vl", "HCXVisionConfig": "vllm.transformers_utils.configs.hyperclovax", "HYV3Config": "vllm.transformers_utils.configs.hy_v3", "HyperCLOVAXConfig": "vllm.transformers_utils.configs.hyperclovax", @@ -141,9 +138,6 @@ __all__ = [ "FunAudioChatConfig", "FunAudioChatAudioEncoderConfig", "Granite4VisionConfig", - "HunYuanVLConfig", - "HunYuanVLTextConfig", - "HunYuanVLVisionConfig", "HCXVisionConfig", "HYV3Config", "HyperCLOVAXConfig", diff --git a/vllm/transformers_utils/configs/hunyuan_vl.py b/vllm/transformers_utils/configs/hunyuan_vl.py deleted file mode 100644 index 548dcfefca..0000000000 --- a/vllm/transformers_utils/configs/hunyuan_vl.py +++ /dev/null @@ -1,281 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# SPDX-FileCopyrightText: Copyright contributors to the vLLM project - -# adapted from https://github.com/ManaEstras/transformers/blob/v4.57.1.hyvl/src/transformers/models/hunyuan_vl/configuration_hunyuan_vl.py - -from transformers import PretrainedConfig - - -class HunYuanVLVisionConfig(PretrainedConfig): - model_type = "hunyuan_vl" - base_config_key = "vision_config" - - def __init__( - self, - hidden_act="gelu", - hidden_size=1152, - intermediate_size=4304, - interpolate_mode="bilinear", - rms_norm_eps=1e-05, - learnable_mlp_pooling_size=0, - num_attention_heads=16, - num_key_value_heads=None, - num_channels=3, - num_hidden_layers=27, - out_hidden_size=4096, - patch_size=16, - remove_prenorm=True, - spatial_merge_size=2, - temporal_patch_size=1, - resize_resolution=2048, - img_max_token_num=4096, - max_image_size=2048, - video_max_image_size=768, - video_min_image_size=256, - min_image_size=512, - anyres_vit_max_image_size=2048, - max_vit_seq_len=16384, - text_hidden_size=3072, - **kwargs, - ): - super().__init__(**kwargs) - - self.hidden_act = hidden_act - self.hidden_size = hidden_size - self.intermediate_size = intermediate_size - self.interpolate_mode = interpolate_mode - self.learnable_mlp_pooling_size = learnable_mlp_pooling_size - self.num_attention_heads = num_attention_heads - if not num_key_value_heads: - self.num_key_value_heads = num_attention_heads - else: - self.num_key_value_heads = num_key_value_heads - self.num_channels = num_channels - self.num_hidden_layers = num_hidden_layers - self.out_hidden_size = out_hidden_size - self.patch_size = patch_size - self.remove_prenorm = remove_prenorm - self.spatial_merge_size = spatial_merge_size - self.temporal_patch_size = temporal_patch_size - self.rms_norm_eps = rms_norm_eps - - self.resize_resolution = resize_resolution - self.img_max_token_num = img_max_token_num - self.max_image_size = max_image_size - self.min_image_size = min_image_size - self.video_max_image_size = video_max_image_size - self.video_min_image_size = video_min_image_size - self.anyres_vit_max_image_size = anyres_vit_max_image_size - self.max_vit_seq_len = max_vit_seq_len - self.text_hidden_size = text_hidden_size - - -class HunYuanVLTextConfig(PretrainedConfig): - r""" - This is the configuration class to store the configuration of a [`HunYuanVLTextConfig`]. It is used to instantiate an - HunYuan model according to the specified arguments, defining the model architecture. Instantiating a configuration - with the defaults will yield a similar configuration to that of the HunYuan-7B. - Hunyuan-7B-Instruct [tencent/Hunyuan-7B-Instruct](https://huggingface.co/tencent/Hunyuan-7B-Instruct). - - Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the - documentation from [`PretrainedConfig`] for more information. - - - Args: - vocab_size (`int`, *optional*, defaults to 290943): - Vocabulary size of the HunYuan model. Defines the number of different tokens that can be represented by the - `inputs_ids` passed when calling [`HunYuanVLTextConfig`] - hidden_size (`int`, *optional*, defaults to 4096): - Dimension of the hidden representations. - intermediate_size (`int`, *optional*, defaults to 11008): - Dimension of the MLP representations or shared MLP representations. - num_hidden_layers (`int`, *optional*, defaults to 32): - Number of hidden layers in the Transformer decoder. - num_attention_heads (`int`, *optional*, defaults to 32): - Number of attention heads for each attention layer in the Transformer decoder. - num_key_value_heads (`int`, *optional*): - This is the number of key_value heads that should be used to implement Grouped Query Attention. If - `num_key_value_heads=num_attention_heads`, the model will use Multi Head Attention (MHA), if - `num_key_value_heads=1 the model will use Multi Query Attention (MQA) otherwise GQA is used. When - converting a multi-head checkpoint to a GQA checkpoint, each group key and value head should be constructed - by meanpooling all the original heads within that group. For more details checkout [this - paper](https://huggingface.co/papers/2305.13245). If it is not specified, will default to - `num_attention_heads`. - hidden_act (`str` or `function`, *optional*, defaults to `"silu"`): - The non-linear activation function (function or string) in the decoder. - max_position_embeddings (`int`, *optional*, defaults to 2048): - The maximum sequence length that this model might ever be used with. - initializer_range (`float`, *optional*, defaults to 0.02): - The standard deviation of the truncated_normal_initializer for initializing all weight matrices. - rms_norm_eps (`float`, *optional*, defaults to 1e-05): - The epsilon used by the rms normalization layers. - use_cache (`bool`, *optional*, defaults to `True`): - Whether or not the model should return the last key/values attentions (not used by all models). Only - relevant if `config.is_decoder=True`. - pad_token_id (`int`, *optional*, defaults to 0): - Padding token id. - bos_token_id (`int`, *optional*, defaults to 1): - Beginning of stream token id. - eos_token_id (`int`, *optional*, defaults to 2): - End of stream token id. - eod_token_id (int, *optional*, defaults to 3): - Token ID representing the end-of-document marker. Used to indicate the termination of a text sequence. - Example: In multi-document processing, this token helps the model distinguish between separate documents. - pretraining_tp (`int`, *optional*, defaults to 1): - Experimental feature. Tensor parallelism rank used during pretraining. Please refer to [this - document](https://huggingface.co/docs/transformers/parallelism) to understand more about it. This value is - necessary to ensure exact reproducibility of the pretraining results. Please refer to [this - issue](https://github.com/pytorch/pytorch/issues/76232). - tie_word_embeddings (`bool`, *optional*, defaults to `False`): - Whether to tie weight embeddings - rope_theta (`float`, *optional*, defaults to 10000.0): - The base period of the RoPE embeddings. - rope_scaling (`Dict`, *optional*): - Dictionary containing the scaling configuration for the RoPE embeddings. Currently supports two scaling - strategies: linear and dynamic. Their scaling factor must be a float greater than 1. The expected format is - `{"type": strategy name, "factor": scaling factor}`. When using this flag, don't update - `max_position_embeddings` to the expected new maximum. See the following thread for more information on how - these scaling strategies behave: - https://www.reddit.com/r/LocalLLaMA/comments/14mrgpr/dynamically_scaled_rope_further_increases/. This is an - experimental feature, subject to breaking API changes in future versions. - attention_bias (`bool`, defaults to `False`, *optional*, defaults to `False`): - Whether to use a bias in the query, key, value and output projection layers during self-attention. - attention_dropout (`float`, *optional*, defaults to 0.0): - The dropout ratio for the attention probabilities. - head_dim (`int`, *optional*, defaults to 128): - The attention head dimension. - """ # noqa: E501 - - model_type = "hunyuan_vl_text" - keys_to_ignore_at_inference = ["past_key_values"] - - def __init__( - self, - vocab_size=290943, - hidden_size=4096, - intermediate_size: int = 11008, - num_hidden_layers=32, - num_attention_heads=32, - num_key_value_heads=None, - hidden_act="silu", - max_position_embeddings=2048, - initializer_range=0.02, - rms_norm_eps=1e-5, - use_cache=True, - pad_token_id=0, - bos_token_id=1, - eos_token_id=2, - eod_token_id=3, - pretraining_tp=1, - tie_word_embeddings=False, - rope_theta=10000.0, - rope_scaling=None, - attention_bias=False, - attention_dropout=0.0, - head_dim=None, - **kwargs, - ): - self.vocab_size = vocab_size - self.max_position_embeddings = max_position_embeddings - self.hidden_size = hidden_size - self.intermediate_size = intermediate_size - self.num_hidden_layers = num_hidden_layers - self.num_attention_heads = num_attention_heads - self.head_dim = head_dim - # for backward compatibility - if num_key_value_heads is None: - num_key_value_heads = num_attention_heads - - self.num_key_value_heads = num_key_value_heads - self.hidden_act = hidden_act - self.initializer_range = initializer_range - self.rms_norm_eps = rms_norm_eps - self.pretraining_tp = pretraining_tp - self.use_cache = use_cache - self.rope_theta = rope_theta - self.rope_scaling = rope_scaling - self.attention_bias = attention_bias - self.attention_dropout = attention_dropout - - super().__init__( - pad_token_id=pad_token_id, - bos_token_id=bos_token_id, - eos_token_id=eos_token_id, - tie_word_embeddings=tie_word_embeddings, - **kwargs, - ) - - -class HunYuanVLConfig(PretrainedConfig): - model_type = "hunyuan_vl" - sub_configs = { - "vision_config": HunYuanVLVisionConfig, - "text_config": HunYuanVLTextConfig, - } - keys_to_ignore_at_inference = ["past_key_values"] - - def __init__( - self, - text_config=None, - vision_config=None, - im_start_id=120118, - im_end_id=120119, - image_token_id=120120, - im_newline_id=120121, - video_start_id=120122, - video_end_id=120123, - **kwargs, - ): - # We need to init super() here so that it does not reset values - # that are in text config to the BaseClass defaults. The Base - # config has many text related defaults and not all defaults are - # same as for `HunYuanVLTextConfig`. - super().__init__(**kwargs) - - if isinstance(vision_config, dict): - self.vision_config = self.sub_configs["vision_config"](**vision_config) - elif vision_config is None: - self.vision_config = self.sub_configs["vision_config"]() - - if isinstance(text_config, dict): - self.text_config = self.sub_configs["text_config"](**text_config) - elif text_config is None: - # For BC use all kwargs to init `TextConfig` - self.text_config = self.sub_configs["text_config"](**kwargs) - - self.image_token_id = image_token_id - self.im_start_id = im_start_id - self.im_end_id = im_end_id - self.im_newline_id = im_newline_id - self.video_start_id = video_start_id - self.video_end_id = video_end_id - - self.vision_config.text_hidden_size = self.text_config.hidden_size - - # Attention implementation to use. It sets it recursively on sub-configs - # so we call it again in the end. - self._attn_implementation = kwargs.pop("attn_implementation", None) - - def __setattr__(self, key, value): - if ( - (text_config := super().__getattribute__("__dict__").get("text_config")) - is not None - and key not in ["dtype", "_attn_implementation_internal"] - and key in text_config.__dict__ - ): - setattr(text_config, key, value) - else: - super().__setattr__(key, value) - - def __getattribute__(self, key): - if "text_config" in super().__getattribute__("__dict__") and key not in [ - "_name_or_path", - "model_type", - "dtype", - "_attn_implementation_internal", - ]: - text_config = super().__getattribute__("text_config") - if key in text_config.__dict__: - return getattr(text_config, key) - - return super().__getattribute__(key)