Arsh9210 commited on
Commit
83537ed
·
verified ·
1 Parent(s): 9be9be6

Added checkpoint_folder_full/modeling_nemotron_h_audio.py

Browse files
checkpoint_folder_full/modeling_nemotron_h_audio.py ADDED
@@ -0,0 +1,310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Nemotron-Dense Audex audio-understanding model for HuggingFace inference."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+ import torch
8
+ from torch import nn
9
+ import torch.nn.functional as F
10
+ from transformers.cache_utils import DynamicCache
11
+ from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
12
+
13
+ from .configuration_nemotron_h_audio import NemotronDenseAudexConfig
14
+ from .modeling_nemotron_dense import NemotronDenseForCausalLM
15
+
16
+
17
+ class NemotronDenseAudexRMSNorm(nn.Module):
18
+ def __init__(self, hidden_size: int, eps: float = 1e-5):
19
+ super().__init__()
20
+ self.weight = nn.Parameter(torch.ones(hidden_size))
21
+ self.eps = eps
22
+
23
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
24
+ input_dtype = hidden_states.dtype
25
+ hidden_states = hidden_states.float()
26
+ variance = hidden_states.pow(2).mean(dim=-1, keepdim=True)
27
+ hidden_states = hidden_states * torch.rsqrt(variance + self.eps)
28
+ return (self.weight.float() * hidden_states).to(input_dtype)
29
+
30
+
31
+ class NemotronDenseAudexProjector(nn.Module):
32
+ """Megatron sound_projection equivalent for TP1 HF inference."""
33
+
34
+ def __init__(self, config: NemotronDenseAudexConfig):
35
+ super().__init__()
36
+ self.norm = NemotronDenseAudexRMSNorm(
37
+ config.audio_encoder_hidden_size,
38
+ eps=config.audio_projector_norm_eps,
39
+ )
40
+ self.fc1 = nn.Linear(
41
+ config.audio_encoder_hidden_size,
42
+ config.audio_projector_intermediate_size,
43
+ bias=False,
44
+ )
45
+ self.fc2 = nn.Linear(
46
+ config.audio_projector_intermediate_size,
47
+ config.hidden_size,
48
+ bias=False,
49
+ )
50
+ self.activation = config.audio_projector_activation
51
+
52
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
53
+ hidden_states = self.norm(hidden_states)
54
+ hidden_states = self.fc1(hidden_states)
55
+ if self.activation == "relu2":
56
+ hidden_states = F.relu(hidden_states).pow(2)
57
+ elif self.activation == "gelu":
58
+ hidden_states = F.gelu(hidden_states)
59
+ else:
60
+ raise ValueError(f"Unsupported audio projector activation: {self.activation}")
61
+ return self.fc2(hidden_states)
62
+
63
+
64
+ def _build_qwen2_audio_encoder(audio_config: dict) -> nn.Module:
65
+ try:
66
+ from transformers.models.qwen2_audio.configuration_qwen2_audio import Qwen2AudioEncoderConfig
67
+ from transformers.models.qwen2_audio.modeling_qwen2_audio import Qwen2AudioEncoder
68
+ except Exception as exc: # pragma: no cover - version/environment guard
69
+ raise ImportError(
70
+ "Qwen2-Audio support is required for NV-Whisper. "
71
+ "Install a transformers build that provides transformers.models.qwen2_audio."
72
+ ) from exc
73
+
74
+ cfg = Qwen2AudioEncoderConfig(**dict(audio_config))
75
+ return Qwen2AudioEncoder(cfg)
76
+
77
+
78
+ class NemotronDenseAudexForConditionalGeneration(NemotronDenseForCausalLM):
79
+ """Nemotron-Dense CausalLM plus NV-Whisper encoder and sound projection.
80
+
81
+ State dict layout intentionally keeps the baseline LLM key names:
82
+ `model.*` and `lm_head.*` load exactly as in the LLM-only checkpoint.
83
+ New audio tensors live under `audio_encoder.*` and `audio_projector.*`.
84
+ """
85
+
86
+ config_class = NemotronDenseAudexConfig
87
+ _tp_plan = None
88
+ _base_model_tp_plan = None
89
+ base_model_tp_plan = None
90
+
91
+ def __init__(self, config: NemotronDenseAudexConfig):
92
+ super().__init__(config)
93
+ self.audio_encoder = _build_qwen2_audio_encoder(config.audio_config)
94
+ self.audio_projector = NemotronDenseAudexProjector(config)
95
+
96
+ def encode_audio(self, input_features: torch.Tensor) -> torch.Tensor:
97
+ """Encode Whisper features into LLM hidden-space audio embeddings."""
98
+ encoder_param = next(self.audio_encoder.parameters())
99
+ input_features = input_features.to(device=encoder_param.device, dtype=encoder_param.dtype)
100
+ encoder_outputs = self.audio_encoder(input_features=input_features, return_dict=True)
101
+ audio_hidden = encoder_outputs.last_hidden_state
102
+ projector_param = next(self.audio_projector.parameters())
103
+ audio_hidden = audio_hidden.to(device=projector_param.device, dtype=projector_param.dtype)
104
+ return self.audio_projector(audio_hidden)
105
+
106
+ def _audio_embeddings_by_sample(
107
+ self,
108
+ input_features: Optional[torch.Tensor],
109
+ audio_embeddings: Optional[torch.Tensor],
110
+ batch_size: int,
111
+ ) -> list[torch.Tensor]:
112
+ if audio_embeddings is None:
113
+ if input_features is None:
114
+ raise ValueError("input_features or audio_embeddings must be provided for audio injection")
115
+ if input_features.ndim == 3:
116
+ projected = self.encode_audio(input_features)
117
+ if batch_size != 1:
118
+ raise ValueError(
119
+ "3D input_features represent a single sample. "
120
+ "Use 4D (batch, clips, mel_bins, frames) features for batched audio."
121
+ )
122
+ return [projected.reshape(-1, projected.shape[-1])]
123
+ if input_features.ndim == 4:
124
+ bsz, clips, mel_bins, frames = input_features.shape
125
+ if bsz != batch_size:
126
+ raise ValueError(f"input_features batch {bsz} != input_ids batch {batch_size}")
127
+ flat_features = input_features.reshape(bsz * clips, mel_bins, frames)
128
+ projected = self.encode_audio(flat_features)
129
+ projected = projected.reshape(bsz, clips * projected.shape[1], projected.shape[-1])
130
+ return [projected[idx] for idx in range(bsz)]
131
+ raise ValueError(f"Expected 3D or 4D input_features, got {tuple(input_features.shape)}")
132
+
133
+ if audio_embeddings.ndim == 2:
134
+ if batch_size != 1:
135
+ raise ValueError("2D audio_embeddings only support batch_size=1")
136
+ return [audio_embeddings.to(device=self.device)]
137
+ if audio_embeddings.ndim == 3:
138
+ if audio_embeddings.shape[0] != batch_size:
139
+ raise ValueError(f"audio_embeddings batch {audio_embeddings.shape[0]} != input_ids batch {batch_size}")
140
+ return [audio_embeddings[idx].to(device=self.device) for idx in range(batch_size)]
141
+ raise ValueError(f"Expected 2D or 3D audio_embeddings, got {tuple(audio_embeddings.shape)}")
142
+
143
+ def prepare_inputs_embeds(
144
+ self,
145
+ input_ids: torch.LongTensor,
146
+ input_features: Optional[torch.Tensor] = None,
147
+ audio_embeddings: Optional[torch.Tensor] = None,
148
+ ) -> torch.Tensor:
149
+ if input_ids is None:
150
+ raise ValueError("input_ids are required when injecting audio embeddings")
151
+ if self.config.sound_token_id is None:
152
+ raise ValueError("config.sound_token_id is required for audio embedding injection")
153
+
154
+ embed_device = self.model.embed_tokens.weight.device
155
+ input_ids = input_ids.to(embed_device)
156
+ inputs_embeds = self.model.embed_tokens(input_ids).clone()
157
+ audio_by_sample = self._audio_embeddings_by_sample(
158
+ input_features=input_features,
159
+ audio_embeddings=audio_embeddings,
160
+ batch_size=input_ids.shape[0],
161
+ )
162
+
163
+ for batch_idx, audio in enumerate(audio_by_sample):
164
+ mask = input_ids[batch_idx].to(self.device) == self.config.sound_token_id
165
+ mask = mask.to(embed_device)
166
+ expected = int(mask.sum().item())
167
+ if expected != audio.shape[0]:
168
+ raise ValueError(
169
+ "Mismatch between <so_embedding> token count and projected audio tokens: "
170
+ f"sample={batch_idx} placeholders={expected} audio_tokens={audio.shape[0]}"
171
+ )
172
+ inputs_embeds[batch_idx, mask] = audio.to(device=embed_device, dtype=inputs_embeds.dtype)
173
+ return inputs_embeds
174
+
175
+ @staticmethod
176
+ def _is_prefill(past_key_values) -> bool:
177
+ if past_key_values is None:
178
+ return True
179
+ get_len = getattr(past_key_values, "get_seq_length", None)
180
+ if callable(get_len):
181
+ return get_len() == 0
182
+ return len(past_key_values) == 0
183
+
184
+ def prepare_inputs_for_generation(
185
+ self,
186
+ input_ids,
187
+ past_key_values=None,
188
+ attention_mask=None,
189
+ inputs_embeds=None,
190
+ input_features=None,
191
+ audio_embeddings=None,
192
+ cache_position=None,
193
+ position_ids=None,
194
+ use_cache=True,
195
+ **kwargs,
196
+ ):
197
+ if self._is_prefill(past_key_values) and inputs_embeds is None and (
198
+ input_features is not None or audio_embeddings is not None
199
+ ):
200
+ inputs_embeds = self.prepare_inputs_embeds(
201
+ input_ids=input_ids,
202
+ input_features=input_features,
203
+ audio_embeddings=audio_embeddings,
204
+ )
205
+ return super().prepare_inputs_for_generation(
206
+ input_ids=input_ids,
207
+ past_key_values=past_key_values,
208
+ attention_mask=attention_mask,
209
+ inputs_embeds=inputs_embeds,
210
+ cache_position=cache_position,
211
+ position_ids=position_ids,
212
+ use_cache=use_cache,
213
+ **kwargs,
214
+ )
215
+
216
+ def _dense_forward_from_embeds(
217
+ self,
218
+ inputs_embeds: torch.Tensor,
219
+ attention_mask: Optional[torch.Tensor] = None,
220
+ position_ids: Optional[torch.LongTensor] = None,
221
+ past_key_values=None,
222
+ use_cache: Optional[bool] = None,
223
+ return_dict: Optional[bool] = None,
224
+ ):
225
+ use_cache = use_cache if use_cache is not None else self.config.use_cache
226
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
227
+ if use_cache and past_key_values is None:
228
+ past_key_values = DynamicCache()
229
+
230
+ hidden_states = inputs_embeds
231
+ for decoder_layer in self.model.layers:
232
+ hidden_states = decoder_layer(
233
+ hidden_states,
234
+ attention_mask=attention_mask,
235
+ position_ids=position_ids,
236
+ past_key_values=past_key_values,
237
+ use_cache=use_cache,
238
+ )
239
+ hidden_states = self.model.norm(hidden_states)
240
+
241
+ if not return_dict:
242
+ return tuple(v for v in [hidden_states, past_key_values] if v is not None)
243
+ return BaseModelOutputWithPast(
244
+ last_hidden_state=hidden_states,
245
+ past_key_values=past_key_values,
246
+ )
247
+
248
+ def forward(
249
+ self,
250
+ input_ids: Optional[torch.LongTensor] = None,
251
+ inputs_embeds: Optional[torch.FloatTensor] = None,
252
+ input_features: Optional[torch.Tensor] = None,
253
+ audio_embeddings: Optional[torch.Tensor] = None,
254
+ attention_mask: Optional[torch.Tensor] = None,
255
+ position_ids: Optional[torch.LongTensor] = None,
256
+ past_key_values=None,
257
+ labels: Optional[torch.LongTensor] = None,
258
+ use_cache: Optional[bool] = None,
259
+ return_dict: Optional[bool] = None,
260
+ **kwargs,
261
+ ):
262
+ if inputs_embeds is None and self._is_prefill(past_key_values) and (
263
+ input_features is not None or audio_embeddings is not None
264
+ ):
265
+ inputs_embeds = self.prepare_inputs_embeds(
266
+ input_ids=input_ids,
267
+ input_features=input_features,
268
+ audio_embeddings=audio_embeddings,
269
+ )
270
+ input_ids = None
271
+ if inputs_embeds is None:
272
+ return super().forward(
273
+ input_ids=input_ids,
274
+ attention_mask=attention_mask,
275
+ position_ids=position_ids,
276
+ past_key_values=past_key_values,
277
+ labels=labels,
278
+ use_cache=use_cache,
279
+ return_dict=return_dict,
280
+ **kwargs,
281
+ )
282
+
283
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
284
+ outputs = self._dense_forward_from_embeds(
285
+ inputs_embeds=inputs_embeds,
286
+ attention_mask=attention_mask,
287
+ position_ids=position_ids,
288
+ past_key_values=past_key_values,
289
+ use_cache=use_cache,
290
+ return_dict=return_dict,
291
+ )
292
+ hidden_states = outputs[0]
293
+ logits = self.lm_head(hidden_states)
294
+
295
+ loss = None
296
+ if labels is not None:
297
+ labels = labels.to(logits.device)
298
+ shift_logits = logits[..., :-1, :].contiguous()
299
+ shift_labels = labels[..., 1:].contiguous()
300
+ loss_fct = nn.CrossEntropyLoss()
301
+ loss = loss_fct(shift_logits.view(-1, self.config.vocab_size), shift_labels.view(-1))
302
+
303
+ if not return_dict:
304
+ output = (logits,) + outputs[1:]
305
+ return ((loss,) + output) if loss is not None else output
306
+ return CausalLMOutputWithPast(
307
+ loss=loss,
308
+ logits=logits,
309
+ past_key_values=outputs.past_key_values,
310
+ )