-
-
Notifications
You must be signed in to change notification settings - Fork 20.1k
[Bugfix][Model] Fix audio-in-video support for Qwen2.5-Omni and Qwen3-Omni #33605
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
246e362
5ba1c59
66bbef0
c01cabf
73e73bf
b77f664
e86cd31
ae55b00
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1286,17 +1286,86 @@ def embed_input_ids( | |||||||||||||||||||||||||
| is_multimodal: torch.Tensor | None = None, | ||||||||||||||||||||||||||
| handle_oov_mm_token: bool = False, | ||||||||||||||||||||||||||
| ) -> torch.Tensor: | ||||||||||||||||||||||||||
| # This is to satisfy the type checker for each overload | ||||||||||||||||||||||||||
| from .utils import _merge_multimodal_embeddings | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if multimodal_embeddings is None or is_multimodal is None: | ||||||||||||||||||||||||||
| return super().embed_input_ids(input_ids) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return super().embed_input_ids( | ||||||||||||||||||||||||||
| inputs_embeds = self._embed_text_input_ids( | ||||||||||||||||||||||||||
| input_ids, | ||||||||||||||||||||||||||
| multimodal_embeddings=multimodal_embeddings, | ||||||||||||||||||||||||||
| self.get_language_model().embed_input_ids, | ||||||||||||||||||||||||||
| is_multimodal=is_multimodal, | ||||||||||||||||||||||||||
| handle_oov_mm_token=handle_oov_mm_token, | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if len(multimodal_embeddings) == 0: | ||||||||||||||||||||||||||
| return inputs_embeds | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Check for audio-in-video: interleaved video and audio tokens | ||||||||||||||||||||||||||
| # in the multimodal region. When use_audio_in_video=True, video | ||||||||||||||||||||||||||
| # and audio tokens are interleaved in the token sequence, but | ||||||||||||||||||||||||||
| # the embeddings are provided as separate contiguous tensors. | ||||||||||||||||||||||||||
| # A single masked_scatter_ would place them in the wrong order, | ||||||||||||||||||||||||||
| # so we scatter each modality separately using per-modality masks. | ||||||||||||||||||||||||||
| video_token_id = self.config.video_token_index | ||||||||||||||||||||||||||
| audio_token_id = self.config.audio_token_index | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| is_video = is_multimodal & (input_ids == video_token_id) | ||||||||||||||||||||||||||
| is_audio = is_multimodal & (input_ids == audio_token_id) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| num_video = is_video.sum().item() | ||||||||||||||||||||||||||
| num_audio = is_audio.sum().item() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if num_video > 0 and num_audio > 0: | ||||||||||||||||||||||||||
| # Check if video and audio positions are actually interleaved | ||||||||||||||||||||||||||
| video_pos = is_video.nonzero(as_tuple=True)[0] | ||||||||||||||||||||||||||
| audio_pos = is_audio.nonzero(as_tuple=True)[0] | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| is_interleaved = ( | ||||||||||||||||||||||||||
| video_pos[0].item() < audio_pos[-1].item() | ||||||||||||||||||||||||||
| and audio_pos[0].item() < video_pos[-1].item() | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if is_interleaved: | ||||||||||||||||||||||||||
| # Match embeddings to modalities by exact token count | ||||||||||||||||||||||||||
| video_embeds: list[torch.Tensor] = [] | ||||||||||||||||||||||||||
| audio_embeds: list[torch.Tensor] = [] | ||||||||||||||||||||||||||
| other_embeds: list[torch.Tensor] = [] | ||||||||||||||||||||||||||
| video_remaining = num_video | ||||||||||||||||||||||||||
| audio_remaining = num_audio | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| for emb in multimodal_embeddings: | ||||||||||||||||||||||||||
| n = emb.shape[0] | ||||||||||||||||||||||||||
| if video_remaining > 0 and n <= video_remaining: | ||||||||||||||||||||||||||
| video_embeds.append(emb) | ||||||||||||||||||||||||||
| video_remaining -= n | ||||||||||||||||||||||||||
| elif audio_remaining > 0 and n <= audio_remaining: | ||||||||||||||||||||||||||
| audio_embeds.append(emb) | ||||||||||||||||||||||||||
| audio_remaining -= n | ||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic for separating multimodal embeddings into However, based on the implementation of The current greedy matching logic will incorrectly classify audio embeddings as video embeddings, leading to incorrect model behavior. To fix this, the order of checks should be swapped to match the embedding order (audio then video).
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need to fix it. The embedding order is actually determined by |
||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||
| other_embeds.append(emb) | ||||||||||||||||||||||||||
|
ywang96 marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if video_embeds: | ||||||||||||||||||||||||||
| inputs_embeds = _merge_multimodal_embeddings( | ||||||||||||||||||||||||||
| inputs_embeds, video_embeds, is_video | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| if audio_embeds: | ||||||||||||||||||||||||||
| inputs_embeds = _merge_multimodal_embeddings( | ||||||||||||||||||||||||||
| inputs_embeds, audio_embeds, is_audio | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| if other_embeds: | ||||||||||||||||||||||||||
| other_mask = is_multimodal & ~is_video & ~is_audio | ||||||||||||||||||||||||||
| inputs_embeds = _merge_multimodal_embeddings( | ||||||||||||||||||||||||||
| inputs_embeds, other_embeds, other_mask | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return inputs_embeds | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Default: standard merge (no interleaving) | ||||||||||||||||||||||||||
| return _merge_multimodal_embeddings( | ||||||||||||||||||||||||||
| inputs_embeds, multimodal_embeddings, is_multimodal | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| def forward( | ||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||
| input_ids: torch.Tensor | None, | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.