[Perf] Remove dead audio_tower and visual from Qwen3-Omni talker stage (#3296)
Signed-off-by: Nick Cao <ncao@redhat.com> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
6be59f7d19
commit
bd4ede391b
2 files changed
+11
-145
No files matched your search
@@ -149,6 +149,7 @@ class Qwen3OmniMoeForConditionalGeneration(
|
||||
dtype=torch.long,
|
||||
)
|
||||
elif self.model_stage == "talker":
|
||||
multimodal_config.skip_mm_profiling = True
|
||||
self.has_preprocess = True
|
||||
self.has_postprocess = True
|
||||
self.set_custom_preprocess(self.talker_preprocess)
|
||||
@@ -166,7 +167,6 @@ class Qwen3OmniMoeForConditionalGeneration(
|
||||
hf_config=talker_config,
|
||||
architectures=["Qwen3OmniMoeTalkerForConditionalGeneration"],
|
||||
)
|
||||
self.talker.init_multi_modal(thinker_config)
|
||||
self.model = self.talker
|
||||
self.code2wav = None
|
||||
|
||||
@@ -188,6 +188,7 @@ class Qwen3OmniMoeForConditionalGeneration(
|
||||
}
|
||||
|
||||
elif self.model_stage == "code2wav":
|
||||
multimodal_config.skip_mm_profiling = True
|
||||
self.enable_update_additional_information = True
|
||||
self.thinker = None
|
||||
self.talker = None
|
||||
@@ -280,6 +281,8 @@ class Qwen3OmniMoeForConditionalGeneration(
|
||||
) -> torch.Tensor:
|
||||
if self.model_stage == "code2wav":
|
||||
return torch.zeros_like(input_ids).reshape(-1, 1).repeat(1, self.vllm_config.model_config.get_hidden_size())
|
||||
if self.model_stage == "talker":
|
||||
return self.model.embed_input_ids(input_ids)
|
||||
return self.model.embed_input_ids(
|
||||
input_ids=input_ids, multimodal_embeddings=multimodal_embeddings, is_multimodal=is_multimodal
|
||||
)
|
||||
|
||||
@@ -1,30 +1,21 @@
|
||||
from collections.abc import Iterable
|
||||
from typing import Any
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from transformers.models.qwen3_omni_moe.configuration_qwen3_omni_moe import (
|
||||
Qwen3OmniMoeTalkerConfig,
|
||||
)
|
||||
from transformers.models.qwen3_omni_moe.modeling_qwen3_omni_moe import (
|
||||
Qwen3OmniMoeAudioEncoder,
|
||||
)
|
||||
from vllm.config import VllmConfig
|
||||
from vllm.logger import init_logger
|
||||
from vllm.model_executor.layers.activation import _ACTIVATION_REGISTRY
|
||||
from vllm.model_executor.models.interfaces import (
|
||||
MultiModalEmbeddings,
|
||||
SupportsPP,
|
||||
)
|
||||
from vllm.model_executor.models.qwen2_5_omni_thinker import (
|
||||
Qwen2_5OmniThinkerDummyInputsBuilder,
|
||||
)
|
||||
from vllm.model_executor.models.utils import (
|
||||
AutoWeightsLoader,
|
||||
WeightsMapper,
|
||||
maybe_prefix,
|
||||
)
|
||||
from vllm.multimodal import MULTIMODAL_REGISTRY
|
||||
from vllm.sequence import IntermediateTensors
|
||||
|
||||
from vllm_omni.model_executor.models.qwen3_omni.qwen3_omni_moe_code_predictor_mtp import (
|
||||
@@ -32,34 +23,15 @@ from vllm_omni.model_executor.models.qwen3_omni.qwen3_omni_moe_code_predictor_mt
|
||||
)
|
||||
from vllm_omni.model_executor.models.qwen3_omni.qwen3_omni_moe_thinker import (
|
||||
Qwen3MoeLLMForCausalLM,
|
||||
Qwen3Omni_VisionTransformer,
|
||||
Qwen3OmniMoeConditionalGenerationMixin,
|
||||
Qwen3OmniMoeThinkerMultiModalProcessor,
|
||||
Qwen3OmniMoeThinkerProcessingInfo,
|
||||
)
|
||||
from vllm_omni.quantization.component_config import ComponentQuantizationConfig
|
||||
|
||||
try:
|
||||
import flash_attn
|
||||
except (ImportError, ModuleNotFoundError):
|
||||
flash_attn = None
|
||||
|
||||
|
||||
logger = init_logger(__name__)
|
||||
|
||||
Qwen3OmniMoeThinkerDummyInputsBuilder = Qwen2_5OmniThinkerDummyInputsBuilder
|
||||
|
||||
|
||||
@MULTIMODAL_REGISTRY.register_processor(
|
||||
Qwen3OmniMoeThinkerMultiModalProcessor,
|
||||
info=Qwen3OmniMoeThinkerProcessingInfo,
|
||||
dummy_inputs=Qwen3OmniMoeThinkerDummyInputsBuilder,
|
||||
)
|
||||
class Qwen3OmniMoeTalkerForConditionalGeneration(
|
||||
nn.Module,
|
||||
# SupportsMultiModal,
|
||||
SupportsPP,
|
||||
Qwen3OmniMoeConditionalGenerationMixin,
|
||||
):
|
||||
"""
|
||||
Qwen3 Omni MoE Talker - Converts text to audio codec codes.
|
||||
@@ -216,31 +188,6 @@ class Qwen3OmniMoeTalkerForConditionalGeneration(
|
||||
|
||||
return result_codes, summed_embeddings
|
||||
|
||||
def init_multi_modal(self, thinker_config: Any) -> None:
|
||||
"""
|
||||
Initialize multimodal components from the thinker.
|
||||
|
||||
Unlike Qwen2.5 Omni which creates audio_tower and visual encoders here,
|
||||
Qwen3 Omni MoE has a cleaner separation: the thinker is the ONLY module
|
||||
that processes raw multimodal inputs. The talker only handles text-to-audio
|
||||
conversion using pre-processed embeddings from the thinker.
|
||||
|
||||
This method exists for API compatibility and stores the thinker config
|
||||
for reference. The actual multimodal processing components (audio_tower,
|
||||
visual) are ONLY in the thinker, not duplicated in the talker.
|
||||
|
||||
Args:
|
||||
thinker_config: Configuration from the thinker model (for reference only)
|
||||
"""
|
||||
self.audio_tower = Qwen3OmniMoeAudioEncoder(thinker_config.audio_config)
|
||||
self.visual = Qwen3Omni_VisionTransformer(
|
||||
vision_config=thinker_config.vision_config,
|
||||
norm_eps=getattr(thinker_config.text_config, "rms_norm_eps", 1e-6),
|
||||
quant_config=self.quant_config,
|
||||
prefix=maybe_prefix(self.prefix, "visual"),
|
||||
# attn_backend_override=attn_backend_override,
|
||||
)
|
||||
|
||||
def project_thinker_outputs(
|
||||
self,
|
||||
thinker_embeds: torch.Tensor | None = None,
|
||||
@@ -300,11 +247,15 @@ class Qwen3OmniMoeTalkerForConditionalGeneration(
|
||||
self,
|
||||
input_ids: torch.Tensor,
|
||||
positions: torch.Tensor,
|
||||
inputs_embeds: torch.Tensor,
|
||||
inputs_embeds: torch.Tensor | None = None,
|
||||
intermediate_tensors: IntermediateTensors | None = None,
|
||||
**kwargs: object,
|
||||
) -> torch.Tensor | IntermediateTensors:
|
||||
"""Forward pass through the talker model."""
|
||||
if inputs_embeds is None and input_ids is not None:
|
||||
inputs_embeds = self.embed_input_ids(input_ids)
|
||||
input_ids = None
|
||||
|
||||
talker_hidden_states, _ = self.language_model.model(
|
||||
input_ids,
|
||||
positions,
|
||||
@@ -334,89 +285,8 @@ class Qwen3OmniMoeTalkerForConditionalGeneration(
|
||||
"""Create empty intermediate tensors for pipeline parallelism."""
|
||||
return self.language_model.make_empty_intermediate_tensors(batch_size, dtype, device)
|
||||
|
||||
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)
|
||||
if input_key in ("pixel_values_videos", "video_embeds") and "video" not in mm_input_by_modality:
|
||||
mm_input_by_modality["video"] = self._parse_and_validate_video_input(**kwargs)
|
||||
if input_key in ("input_audio_features") and "audio" not in mm_input_by_modality:
|
||||
mm_input_by_modality["audio"] = self._parse_and_validate_audio_input(**kwargs)
|
||||
return mm_input_by_modality
|
||||
|
||||
def embed_multimodal(self, **kwargs: object) -> MultiModalEmbeddings | None:
|
||||
mm_input_by_modality = self._parse_and_validate_multimodal_inputs(**kwargs)
|
||||
if not mm_input_by_modality:
|
||||
return []
|
||||
|
||||
logger.warning(
|
||||
"\n\n\n"
|
||||
"THIS FUNCTION RETURNS DUMMY MULTIMODAL EMBEDDINGS FOR PROFILE RUN, "
|
||||
"SHOULD NOT BE CALLED IN INFERENCE."
|
||||
"\n\n\n"
|
||||
)
|
||||
|
||||
# The result multimodal_embeddings is tuple of tensors, with each
|
||||
# tensor corresponding to a multimodal data item (image or video).
|
||||
dummy_multimodal_embeddings: tuple[torch.Tensor, ...] = ()
|
||||
|
||||
# NOTE: It is important to iterate over the keys in this dictionary
|
||||
# to preserve the order of the modalities.
|
||||
# TODO: do projection for all multimodel
|
||||
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)
|
||||
dummy_image_embeddings = ()
|
||||
for image_embed in image_embeddings:
|
||||
dummy_image_embeddings += (
|
||||
torch.zeros(
|
||||
image_embed.shape[0],
|
||||
self.config.text_config.hidden_size,
|
||||
device=image_embed.device,
|
||||
dtype=torch.bfloat16,
|
||||
),
|
||||
)
|
||||
dummy_multimodal_embeddings += tuple(image_embeddings)
|
||||
if modality == "video":
|
||||
video_embeddings = self._process_video_input(multimodal_input)
|
||||
dummy_video_video_embeddings = ()
|
||||
for video_embed in video_embeddings:
|
||||
dummy_video_video_embeddings += (
|
||||
torch.zeros(
|
||||
video_embed.shape[0],
|
||||
self.config.text_config.hidden_size,
|
||||
device=video_embed.device,
|
||||
dtype=torch.bfloat16,
|
||||
),
|
||||
)
|
||||
dummy_multimodal_embeddings += tuple(dummy_video_video_embeddings)
|
||||
if modality == "audio":
|
||||
audio_embeddings = self._process_audio_input(multimodal_input)
|
||||
dummy_audio_embeddings = ()
|
||||
for audio_embed in audio_embeddings:
|
||||
dummy_audio_embeddings += (
|
||||
torch.zeros(
|
||||
audio_embed.shape[0],
|
||||
self.config.text_config.hidden_size,
|
||||
device=audio_embed.device,
|
||||
dtype=torch.bfloat16,
|
||||
),
|
||||
)
|
||||
dummy_multimodal_embeddings += tuple(dummy_audio_embeddings)
|
||||
return dummy_multimodal_embeddings
|
||||
|
||||
def embed_input_ids(
|
||||
self,
|
||||
input_ids: torch.Tensor,
|
||||
multimodal_embeddings: MultiModalEmbeddings | None = None,
|
||||
is_multimodal: bool = False,
|
||||
):
|
||||
"""Get the input embedding layer (for codec tokens)."""
|
||||
def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
|
||||
"""Embed codec input IDs."""
|
||||
return self.language_model.embed_input_ids(input_ids)
|
||||
|
||||
def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
|
||||
@@ -451,13 +321,6 @@ class Qwen3OmniMoeTalkerForConditionalGeneration(
|
||||
except Exception:
|
||||
logger.error("Error logging model load summary")
|
||||
|
||||
multi_model_weights = set()
|
||||
for name, param in self.visual.named_parameters():
|
||||
multi_model_weights.add("visual." + name)
|
||||
for name, param in self.audio_tower.named_parameters():
|
||||
multi_model_weights.add("audio_tower." + name)
|
||||
loaded.update(multi_model_weights)
|
||||
|
||||
return loaded
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user