Skip to content

Commit 4d6075f

Browse files
authored
fix(qwen3-asr): restore vendored generation on transformers 5 (#6368)
Signed-off-by: Chen Cui <chcui@nvidia.com>
1 parent 694200b commit 4d6075f

2 files changed

Lines changed: 422 additions & 71 deletions

File tree

‎src/megatron/bridge/models/qwen3_asr/hf_qwen3_asr/modeling_qwen3_asr.py‎

Lines changed: 60 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import torch
2121
from torch import nn
2222
from torch.nn import functional as F
23+
from transformers import initialization as init
2324
from transformers.activations import ACT2FN
2425
from transformers.cache_utils import Cache, DynamicCache
2526
from transformers.generation import GenerationMixin
@@ -42,6 +43,7 @@
4243
from .configuration_qwen3_asr import (
4344
Qwen3ASRAudioEncoderConfig,
4445
Qwen3ASRConfig,
46+
Qwen3ASRTextConfig,
4547
Qwen3ASRThinkerConfig,
4648
)
4749

@@ -293,6 +295,11 @@ class Qwen3ASRPreTrainedModel(PreTrainedModel):
293295
"attentions": Qwen3ASRTextAttention,
294296
}
295297

298+
def _init_weights(self, module: nn.Module) -> None:
299+
super()._init_weights(module)
300+
if isinstance(module, SinusoidsPositionEmbedding):
301+
init.copy_(module.positional_embedding, module.compute_default_singular_positional_embedding())
302+
296303

297304
@dataclass
298305
class Qwen3ASRThinkerCausalLMOutputWithPast(MoeCausalLMOutputWithPast):
@@ -439,10 +446,11 @@ def get_rope_index(
439446
"""
440447
mrope_position_deltas = []
441448

442-
position_ids = attention_mask.float().cumsum(-1) - 1
449+
position_ids = attention_mask.long().cumsum(-1) - 1
450+
# Padding uses a placeholder position, which must not affect the reported offset.
451+
max_position_ids = position_ids.max(-1, keepdim=True).values
443452
position_ids.masked_fill_(attention_mask == 0, 1)
444453
position_ids = position_ids.unsqueeze(0).expand(3, -1, -1).to(attention_mask.device)
445-
max_position_ids = position_ids.max(0, keepdim=False)[0].max(-1, keepdim=True)[0]
446454
mrope_position_deltas = max_position_ids + 1 - torch.sum(attention_mask, dim=-1, keepdim=True)
447455

448456
return position_ids, mrope_position_deltas
@@ -581,15 +589,20 @@ def __init__(self, length, channels, max_timescale=10000):
581589
super().__init__()
582590
if channels % 2 != 0:
583591
raise ValueError("SinusoidsPositionEmbedding needs even channels input")
584-
log_timescale_increment = np.log(max_timescale) / (channels // 2 - 1)
585-
inv_timescales = torch.exp(-log_timescale_increment * torch.arange(channels // 2).float())
586-
scaled_time = torch.arange(length)[:, np.newaxis] * inv_timescales[np.newaxis, :]
592+
self.length = length
593+
self.channels = channels
594+
self.max_timescale = max_timescale
587595
self.register_buffer(
588-
"positional_embedding",
589-
torch.cat([torch.sin(scaled_time), torch.cos(scaled_time)], dim=1),
590-
persistent=False,
596+
"positional_embedding", self.compute_default_singular_positional_embedding(), persistent=False
591597
)
592598

599+
def compute_default_singular_positional_embedding(self) -> torch.Tensor:
600+
"""Rebuild the deterministic audio positions, including after checkpoint loading."""
601+
log_timescale_increment = np.log(self.max_timescale) / (self.channels // 2 - 1)
602+
inv_timescales = torch.exp(-log_timescale_increment * torch.arange(self.channels // 2).float())
603+
scaled_time = torch.arange(self.length)[:, np.newaxis] * inv_timescales[np.newaxis, :]
604+
return torch.cat([torch.sin(scaled_time), torch.cos(scaled_time)], dim=1)
605+
593606
def forward(self, seqlen: int):
594607
return self.positional_embedding[:seqlen, :]
595608

@@ -779,25 +792,39 @@ def padded_and_mask_function(self, tensor_list, tensor_len, padding_value=0, pad
779792

780793

781794
class Qwen3ASRThinkerTextRotaryEmbedding(nn.Module):
795+
attention_scaling: float
782796
inv_freq: torch.Tensor # fix linting for `register_buffer`
783797

784798
def __init__(self, config: Qwen3ASRConfig, device=None):
785799
super().__init__()
786-
if hasattr(config, "rope_scaling") and config.rope_scaling is not None:
787-
self.rope_type = config.rope_scaling.get("rope_type", "default")
788-
else:
789-
self.rope_type = "default"
800+
self.rope_type = config.rope_parameters["rope_type"]
790801
self.max_seq_len_cached = config.max_position_embeddings
791802
self.original_max_seq_len = config.max_position_embeddings
792803

793804
self.config = config
794-
self.rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
805+
self.rope_init_fn = (
806+
self.compute_default_rope_parameters
807+
if self.rope_type == "default"
808+
else ROPE_INIT_FUNCTIONS[self.rope_type]
809+
)
795810

796811
inv_freq, self.attention_scaling = self.rope_init_fn(self.config, device)
797812
self.register_buffer("inv_freq", inv_freq, persistent=False)
798813
self.original_inv_freq = self.inv_freq
799814

800-
self.mrope_section = config.rope_scaling.get("mrope_section", [24, 20, 20])
815+
self.mrope_section = config.rope_parameters.get("mrope_section", [24, 20, 20])
816+
817+
@staticmethod
818+
def compute_default_rope_parameters(
819+
config: Qwen3ASRTextConfig,
820+
device: torch.device | None = None,
821+
seq_len: int | None = None,
822+
) -> tuple[torch.Tensor, float]:
823+
"""Return unscaled rotary frequencies for initialization and checkpoint loading."""
824+
base = config.rope_parameters["rope_theta"]
825+
head_dim = config.head_dim
826+
inv_freq = 1.0 / (base ** (torch.arange(0, head_dim, 2, dtype=torch.float32, device=device) / head_dim))
827+
return inv_freq, 1.0
801828

802829
def apply_interleaved_mrope(self, freqs, mrope_section):
803830
"""Apply interleaved MRoPE to 3D rotary embeddings.
@@ -967,7 +994,7 @@ class Qwen3ASRThinkerTextModel(Qwen3ASRPreTrainedModel):
967994

968995
def __init__(self, config: Qwen3ASRConfig):
969996
super().__init__(config)
970-
self.padding_idx = config.pad_token_id
997+
self.padding_idx = getattr(config, "pad_token_id", None)
971998
self.vocab_size = config.vocab_size
972999

9731000
self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
@@ -1027,9 +1054,8 @@ def forward(
10271054

10281055
attention_mask = create_causal_mask(
10291056
config=self.config,
1030-
input_embeds=inputs_embeds,
1057+
inputs_embeds=inputs_embeds,
10311058
attention_mask=attention_mask,
1032-
cache_position=cache_position,
10331059
past_key_values=past_key_values,
10341060
position_ids=text_position_ids,
10351061
)
@@ -1070,7 +1096,7 @@ class Qwen3ASRThinkerForConditionalGeneration(Qwen3ASRPreTrainedModelForConditio
10701096

10711097
config: Qwen3ASRThinkerConfig
10721098
base_model_prefix = "thinker"
1073-
_tied_weights_keys = ["model.embed_tokens.weight", "lm_head.weight"]
1099+
_tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
10741100
_no_split_modules = [
10751101
"Qwen3ASRAudioEncoderLayer",
10761102
"Qwen3ASRThinkerTextDecoderLayer",
@@ -1081,6 +1107,8 @@ class Qwen3ASRThinkerForConditionalGeneration(Qwen3ASRPreTrainedModelForConditio
10811107
}
10821108

10831109
def __init__(self, config):
1110+
# Transformers checks the owning model's config; Bridge uses the nested text setting.
1111+
config.tie_word_embeddings = config.text_config.tie_word_embeddings
10841112
super().__init__(config)
10851113
self.audio_tower = Qwen3ASRAudioEncoder._from_config(config.audio_config)
10861114
self.vocab_size = config.text_config.vocab_size
@@ -1089,7 +1117,8 @@ def __init__(self, config):
10891117
self.lm_head = nn.Linear(config.text_config.hidden_size, config.classify_num, bias=False)
10901118
else:
10911119
self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
1092-
self.pad_token_id = self.config.pad_token_id if self.config.pad_token_id is not None else -1
1120+
pad_token_id = getattr(config, "pad_token_id", None)
1121+
self.pad_token_id = pad_token_id if pad_token_id is not None else -1
10931122
self.rope_deltas = None
10941123
self.post_init()
10951124

@@ -1212,25 +1241,18 @@ def forward(
12121241
else:
12131242
audio_feature_lengths = None
12141243

1215-
if attention_mask is not None and position_ids is None:
1216-
if (
1217-
cache_position is None
1218-
or (cache_position is not None and cache_position[0] == 0)
1219-
or self.rope_deltas is None
1220-
):
1221-
delta0 = (1 - attention_mask).sum(dim=-1).unsqueeze(1)
1222-
position_ids, rope_deltas = self.get_rope_index(
1223-
attention_mask,
1224-
)
1225-
rope_deltas = rope_deltas - delta0
1226-
self.rope_deltas = rope_deltas
1227-
else:
1228-
batch_size, seq_length = input_ids.shape
1229-
delta = cache_position[0] + self.rope_deltas if cache_position is not None else 0
1230-
position_ids = torch.arange(seq_length, device=input_ids.device)
1231-
position_ids = position_ids.view(1, -1).expand(batch_size, -1)
1232-
position_ids = position_ids.add(delta)
1233-
position_ids = position_ids.unsqueeze(0).expand(3, -1, -1)
1244+
# Transformers 5 generation tracks the cache length instead of passing cache_position.
1245+
if cache_position is None:
1246+
past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
1247+
cache_position = torch.arange(
1248+
past_seen_tokens, past_seen_tokens + inputs_embeds.shape[1], device=inputs_embeds.device
1249+
)
1250+
1251+
if attention_mask is not None and attention_mask.ndim == 2 and position_ids is None:
1252+
# Derive positions from this request, including padding, rather than mutable model state.
1253+
position_ids, rope_deltas = self.get_rope_index(attention_mask)
1254+
self.rope_deltas = rope_deltas - (1 - attention_mask).sum(dim=-1).unsqueeze(1)
1255+
position_ids = position_ids[..., -inputs_embeds.shape[1] :]
12341256

12351257
outputs = self.model(
12361258
attention_mask=attention_mask,
@@ -1260,39 +1282,6 @@ def forward(
12601282
rope_deltas=self.rope_deltas,
12611283
)
12621284

1263-
def prepare_inputs_for_generation(
1264-
self,
1265-
input_ids,
1266-
past_key_values=None,
1267-
attention_mask=None,
1268-
inputs_embeds=None,
1269-
cache_position=None,
1270-
position_ids=None,
1271-
use_cache=True,
1272-
input_features=None,
1273-
feature_attention_mask=None,
1274-
**kwargs,
1275-
):
1276-
model_inputs = super().prepare_inputs_for_generation(
1277-
input_ids,
1278-
past_key_values=past_key_values,
1279-
attention_mask=attention_mask,
1280-
inputs_embeds=inputs_embeds,
1281-
cache_position=cache_position,
1282-
position_ids=position_ids,
1283-
use_cache=use_cache,
1284-
input_features=input_features,
1285-
feature_attention_mask=feature_attention_mask,
1286-
**kwargs,
1287-
)
1288-
1289-
model_inputs["position_ids"] = None
1290-
1291-
if cache_position[0] != 0:
1292-
model_inputs["input_features"] = None
1293-
1294-
return model_inputs
1295-
12961285

12971286
@auto_docstring
12981287
class Qwen3ASRThinkerTextPreTrainedModel(PreTrainedModel):

0 commit comments

Comments
 (0)