modular_voxtral.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. # coding=utf-8
  2. # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import warnings
  16. from typing import Optional, Union
  17. import torch
  18. from torch import nn
  19. from ...activations import ACT2FN
  20. from ...cache_utils import Cache
  21. from ...generation import GenerationMixin
  22. from ...modeling_outputs import BaseModelOutput, BaseModelOutputWithPast, CausalLMOutputWithPast
  23. from ...processing_utils import Unpack
  24. from ...utils import TransformersKwargs, auto_docstring, can_return_tuple
  25. from ...utils.generic import check_model_inputs
  26. from ..auto import AutoModel, AutoModelForCausalLM
  27. from ..qwen2_audio.modeling_qwen2_audio import (
  28. Qwen2AudioAttention,
  29. Qwen2AudioEncoder,
  30. Qwen2AudioEncoderLayer,
  31. Qwen2AudioPreTrainedModel,
  32. )
  33. from .configuration_voxtral import VoxtralConfig
  34. class VoxtralAttention(Qwen2AudioAttention):
  35. pass
  36. class VoxtralEncoderLayer(Qwen2AudioEncoderLayer):
  37. pass
  38. class VoxtralPreTrainedModel(Qwen2AudioPreTrainedModel):
  39. _supports_flex_attn = True
  40. _supports_cache_class = True
  41. _supports_attention_backend = True
  42. _can_compile_fullgraph = True
  43. _supports_attention_backend = True
  44. _no_split_modules = None
  45. # TODO: @eustlb, I would really prefer to use WhisperEncoder but it's messing with modular
  46. @auto_docstring(
  47. custom_intro="""
  48. The Voxtral encoder, which is a Whisper encoder.
  49. """
  50. )
  51. class VoxtralEncoder(Qwen2AudioEncoder):
  52. _can_record_outputs = {
  53. "attentions": VoxtralAttention,
  54. "hidden_states": VoxtralEncoderLayer,
  55. }
  56. @check_model_inputs()
  57. def forward(
  58. self,
  59. input_features,
  60. attention_mask=None,
  61. **kwargs: Unpack[TransformersKwargs],
  62. ):
  63. r"""
  64. Args:
  65. input_features (`torch.LongTensor` of shape `(batch_size, feature_size, sequence_length)`):
  66. Float values of mel features extracted from the raw speech waveform. Raw speech waveform can be
  67. obtained by loading a `.flac` or `.wav` audio file into an array of type `list[float]` or a
  68. `numpy.ndarray`, *e.g.* via the soundfile library (`pip install soundfile`). To prepare the array into
  69. `input_features`, the [`AutoFeatureExtractor`] should be used for extracting the mel features, padding
  70. and conversion into a tensor of type `torch.FloatTensor`. See [`~WhisperFeatureExtractor.__call__`]
  71. attention_mask (`torch.Tensor`)`, *optional*):
  72. Voxtral does not support masking of the `input_features`, this argument is preserved for compatibility,
  73. but it is not used. By default the silence in the input log mel spectrogram are ignored.
  74. """
  75. expected_seq_length = self.config.max_source_positions * self.conv1.stride[0] * self.conv2.stride[0]
  76. if input_features.shape[-1] != expected_seq_length:
  77. raise ValueError(
  78. f"Qwen2Audio expects the mel input features to be of length {expected_seq_length}, but found {input_features.shape[-1]}. Make sure to pad the input mel features to {expected_seq_length}."
  79. )
  80. input_features = input_features.to(dtype=self.conv1.weight.dtype, device=self.conv1.weight.device)
  81. inputs_embeds = nn.functional.gelu(self.conv1(input_features))
  82. inputs_embeds = nn.functional.gelu(self.conv2(inputs_embeds))
  83. inputs_embeds = inputs_embeds.permute(0, 2, 1)
  84. embed_pos = self.embed_positions.weight
  85. hidden_states = (inputs_embeds + embed_pos).to(inputs_embeds.dtype)
  86. hidden_states = nn.functional.dropout(hidden_states, p=self.dropout, training=self.training)
  87. for idx, encoder_layer in enumerate(self.layers):
  88. layer_outputs = encoder_layer(
  89. hidden_states,
  90. attention_mask=attention_mask,
  91. layer_head_mask=None,
  92. )
  93. hidden_states = layer_outputs[0]
  94. hidden_states = self.layer_norm(hidden_states)
  95. return BaseModelOutput(
  96. last_hidden_state=hidden_states,
  97. )
  98. class VoxtralMultiModalProjector(nn.Module):
  99. def __init__(self, config: VoxtralConfig):
  100. super().__init__()
  101. self.linear_1 = nn.Linear(config.audio_config.intermediate_size, config.text_config.hidden_size, bias=False)
  102. self.act = ACT2FN[config.projector_hidden_act]
  103. self.linear_2 = nn.Linear(config.text_config.hidden_size, config.text_config.hidden_size, bias=False)
  104. def forward(self, audio_features):
  105. hidden_states = self.linear_1(audio_features)
  106. hidden_states = self.act(hidden_states)
  107. hidden_states = self.linear_2(hidden_states)
  108. return hidden_states
  109. @auto_docstring(
  110. custom_intro="""
  111. The Voxtral model, which consists of Whisper encoder, a multi-modal projector and a LLama language model.
  112. """
  113. )
  114. class VoxtralForConditionalGeneration(VoxtralPreTrainedModel, GenerationMixin):
  115. _tied_weights_keys = ["lm_head.weight"]
  116. _tp_plan = {"lm_head": "colwise_rep"}
  117. _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
  118. _keep_in_fp32_modules_strict = ["embed_positions"]
  119. def __init__(self, config):
  120. super().__init__(config)
  121. self.vocab_size = config.text_config.vocab_size
  122. self.audio_tower = AutoModel.from_config(config.audio_config)
  123. self.language_model = AutoModelForCausalLM.from_config(config.text_config)
  124. self.multi_modal_projector = VoxtralMultiModalProjector(config)
  125. # Initialize weights and apply final processing
  126. self.post_init()
  127. def get_input_embeddings(self):
  128. return self.language_model.get_input_embeddings()
  129. def set_input_embeddings(self, value):
  130. self.language_model.set_input_embeddings(value)
  131. def get_output_embeddings(self):
  132. return self.language_model.get_output_embeddings()
  133. def set_output_embeddings(self, new_embeddings):
  134. self.language_model.set_output_embeddings(new_embeddings)
  135. def set_decoder(self, decoder):
  136. self.language_model.set_decoder(decoder)
  137. def get_decoder(self):
  138. return self.language_model.get_decoder()
  139. def get_audio_features(self, input_features: torch.FloatTensor):
  140. """
  141. This method is used to get the audio embeddings from input features (a log mel spectrogram), meaning inferring the audio encoder and the multi-modal projector.
  142. Args:
  143. input_features (`torch.FloatTensor`):
  144. Float values of mel features extracted from the raw speech waveform. Raw speech waveform can be
  145. obtained by loading a `.flac` or `.wav` audio file into an array of type `list[float]` or a
  146. `numpy.ndarray`, *e.g.* via the soundfile library (`pip install soundfile`). To prepare the array into
  147. `input_features`, the [`AutoFeatureExtractor`] should be used for extracting the mel features, padding
  148. and conversion into a tensor of type `torch.FloatTensor`. See [`~WhisperFeatureExtractor.__call__`]
  149. Returns:
  150. `torch.FloatTensor`:
  151. The audio embeddings.
  152. """
  153. audio_outputs = self.audio_tower(input_features)
  154. audio_hidden_states = audio_outputs.last_hidden_state
  155. audio_hidden_states = audio_hidden_states.reshape(-1, self.config.audio_config.intermediate_size)
  156. audio_embeds = self.multi_modal_projector(audio_hidden_states)
  157. return audio_embeds
  158. def get_audio_embeds(self, input_features: torch.FloatTensor):
  159. warnings.warn(
  160. "The method `get_audio_embeds` is deprecated. Please use `get_audio_features` instead.", FutureWarning
  161. )
  162. return self.get_audio_features(input_features)
  163. @can_return_tuple
  164. @auto_docstring
  165. def forward(
  166. self,
  167. input_ids: Optional[torch.LongTensor] = None,
  168. input_features: Optional[torch.FloatTensor] = None,
  169. attention_mask: Optional[torch.Tensor] = None,
  170. position_ids: Optional[torch.LongTensor] = None,
  171. past_key_values: Optional[Cache] = None,
  172. inputs_embeds: Optional[torch.FloatTensor] = None,
  173. labels: Optional[torch.LongTensor] = None,
  174. use_cache: Optional[bool] = None,
  175. cache_position: Optional[torch.LongTensor] = None,
  176. logits_to_keep: Union[int, torch.Tensor] = 0,
  177. **kwargs: Unpack[TransformersKwargs],
  178. ) -> CausalLMOutputWithPast:
  179. r"""
  180. Example:
  181. ```python
  182. >>> from transformers import VoxtralForConditionalGeneration, AutoProcessor
  183. >>> import torch
  184. >>> device = "cuda" if torch.cuda.is_available() else "cpu"
  185. >>> repo_id = "mistralai/Voxtral-Mini-3B-2507"
  186. >>> processor = AutoProcessor.from_pretrained(repo_id)
  187. >>> model = VoxtralForConditionalGeneration.from_pretrained(repo_id, dtype=torch.bfloat16, device_map=device)
  188. >>> conversation = [
  189. {
  190. "role": "user",
  191. "content": [
  192. {
  193. "type": "audio",
  194. "url": "https://huggingface.co/datasets/hf-internal-testing/dummy-audio-samples/resolve/main/dude_where_is_my_car.wav",
  195. },
  196. {"type": "text", "text": "What can you tell me about this audio?"},
  197. ],
  198. }
  199. ]
  200. >>> inputs = processor.apply_chat_template(conversation)
  201. >>> inputs = inputs.to(device, dtype=torch.bfloat16)
  202. >>> outputs = model.generate(**inputs, max_new_tokens=30)
  203. >>> processor.batch_decode(outputs[:, inputs.input_ids.shape[1]:], skip_special_tokens=True)
  204. ["This audio is a humorous conversation between two friends, likely in English, where one of them is trying to figure out what the other's tattoo says."]
  205. ```"""
  206. if inputs_embeds is None:
  207. inputs_embeds = self.get_input_embeddings()(input_ids)
  208. if input_features is not None and input_ids is not None:
  209. audio_embeds = self.get_audio_features(input_features)
  210. # replace text-audio token placeholders with audio embeddings
  211. audio_token_mask = (input_ids == self.config.audio_token_id).unsqueeze(-1)
  212. inputs_embeds = inputs_embeds.masked_scatter(
  213. audio_token_mask.to(inputs_embeds.device), audio_embeds.to(inputs_embeds.device)
  214. )
  215. outputs: BaseModelOutputWithPast = self.language_model(
  216. attention_mask=attention_mask,
  217. position_ids=position_ids,
  218. past_key_values=past_key_values,
  219. inputs_embeds=inputs_embeds,
  220. labels=labels,
  221. use_cache=use_cache,
  222. cache_position=cache_position,
  223. logits_to_keep=logits_to_keep,
  224. **kwargs,
  225. )
  226. return outputs
  227. def prepare_inputs_for_generation(self, *args, **kwargs):
  228. # Overwritten -- we should not pass input_features when we are in cached decoding stage
  229. input_features = kwargs.pop("input_features", None)
  230. cache_position = kwargs.get("cache_position")
  231. model_inputs = super().prepare_inputs_for_generation(*args, **kwargs)
  232. if cache_position is not None and cache_position[0] == 0:
  233. # input_features should only be passed when we are not in cached decoding stage
  234. model_inputs["input_features"] = input_features
  235. return model_inputs
  236. __all__ = ["VoxtralPreTrainedModel", "VoxtralEncoder", "VoxtralForConditionalGeneration"]