1- """Qwen25-Omni audio encoder components.
1+ # Copyright (c) Microsoft Corporation.
2+ # Licensed under the MIT License.
23
3- Whisper-inspired audio encoder with 3x Conv1d,
4- sinusoidal positional embeddings, and bidirectional transformer
5- encoder layers with LayerNorm.
4+ """Qwen2.5-Omni audio encoder components.
5+
6+ Packed bidirectional transformer layers with LayerNorm.
67
78Reference: Transformers
89https://github.com/huggingface/transformers/blob/main/src/transformers/models/qwen2_5_omni/modeling_qwen2_5_omni.py
910"""
1011
1112from __future__ import annotations
1213
13- from typing import TYPE_CHECKING
14-
14+ import onnx_ir as ir
1515from onnxscript import nn
1616from onnxscript ._internal import builder
1717
18+ from mobius ._build_context import get_build_dtype
1819from mobius .components ._common import LayerNorm , Linear
1920
20- if TYPE_CHECKING :
21- import onnx_ir as ir
22-
2321
2422class Qwen25OmniAudioAttention (nn .Module ):
2523 """Bidirectional multi-head attention for Qwen2_5Omni audio encoder.
@@ -37,7 +35,12 @@ def __init__(self, d_model: int, num_heads: int):
3735 self ._num_heads = num_heads
3836 self ._head_dim = d_model // num_heads
3937
40- def forward (self , op : builder .OpBuilder , hidden_states : ir .Value ):
38+ def forward (
39+ self ,
40+ op : builder .OpBuilder ,
41+ hidden_states : ir .Value ,
42+ cu_seqlens : ir .Value ,
43+ ):
4144 """Bidirectional self-attention.
4245
4346 Args:
@@ -46,19 +49,53 @@ def forward(self, op: builder.OpBuilder, hidden_states: ir.Value):
4649 Returns:
4750 output: (batch, seq_len, d_model)
4851 """
49- q = self .q_proj (op , hidden_states )
50- k = self .k_proj (op , hidden_states )
51- v = self .v_proj (op , hidden_states )
52+ seq_len = op .Shape (hidden_states , start = 0 , end = 1 )
53+ packed_shape = op .Concat (seq_len , [self ._num_heads , self ._head_dim ], axis = 0 )
54+ q = op .Reshape (self .q_proj (op , hidden_states ), packed_shape )
55+ k = op .Reshape (self .k_proj (op , hidden_states ), packed_shape )
56+ v = op .Reshape (self .v_proj (op , hidden_states ), packed_shape )
57+
58+ # Build the block-diagonal mask represented by HF's cu_seqlens.
59+ positions = op .Range (0 , op .Squeeze (seq_len , [0 ]), 1 )
60+ segment_ids = op .Sub (
61+ op .ReduceSum (
62+ op .Cast (
63+ op .GreaterOrEqual (
64+ op .Unsqueeze (positions , [1 ]),
65+ op .Unsqueeze (op .Cast (cu_seqlens , to = 7 ), [0 ]),
66+ ),
67+ to = 7 ,
68+ ),
69+ [1 ],
70+ keepdims = False ,
71+ ),
72+ 1 ,
73+ )
74+ same_segment = op .Equal (
75+ op .Unsqueeze (segment_ids , [1 ]),
76+ op .Unsqueeze (segment_ids , [0 ]),
77+ )
78+ attention_bias = op .Where (
79+ same_segment ,
80+ op .CastLike (0.0 , q ),
81+ op .CastLike (- 1e9 , q ),
82+ )
83+ attention_bias = op .Unsqueeze (attention_bias , [0 , 1 ])
5284
53- # Use ONNX Attention op (bidirectional: no causal mask)
85+ q = op .Unsqueeze (op .Transpose (q , perm = [1 , 0 , 2 ]), [0 ])
86+ k = op .Unsqueeze (op .Transpose (k , perm = [1 , 0 , 2 ]), [0 ])
87+ v = op .Unsqueeze (op .Transpose (v , perm = [1 , 0 , 2 ]), [0 ])
5488 attn_output = op .Attention (
5589 q ,
5690 k ,
5791 v ,
92+ attention_bias ,
5893 q_num_heads = self ._num_heads ,
5994 kv_num_heads = self ._num_heads ,
6095 scale = float (self ._head_dim ** - 0.5 ),
6196 )
97+ attn_output = op .Transpose (op .Squeeze (attn_output , [0 ]), perm = [1 , 0 , 2 ])
98+ attn_output = op .Reshape (attn_output , op .Concat (seq_len , [- 1 ], axis = 0 ))
6299 return self .out_proj (op , attn_output )
63100
64101
@@ -86,7 +123,12 @@ def __init__(
86123 self .fc2 = Linear (ffn_dim , d_model , bias = True )
87124 self .final_layer_norm = LayerNorm (d_model , eps = eps )
88125
89- def forward (self , op : builder .OpBuilder , hidden_states : ir .Value ):
126+ def forward (
127+ self ,
128+ op : builder .OpBuilder ,
129+ hidden_states : ir .Value ,
130+ cu_seqlens : ir .Value ,
131+ ):
90132 """Pre-norm encoder layer with bidirectional attention.
91133
92134 Args:
@@ -98,7 +140,7 @@ def forward(self, op: builder.OpBuilder, hidden_states: ir.Value):
98140 # Self-attention with pre-norm and residual
99141 residual = hidden_states
100142 hidden_states = self .self_attn_layer_norm (op , hidden_states )
101- hidden_states = self .self_attn (op , hidden_states )
143+ hidden_states = self .self_attn (op , hidden_states , cu_seqlens )
102144 hidden_states = op .Add (residual , hidden_states )
103145
104146 # FFN with pre-norm, GELU, and residual
@@ -108,5 +150,11 @@ def forward(self, op: builder.OpBuilder, hidden_states: ir.Value):
108150 hidden_states = op .Gelu (hidden_states )
109151 hidden_states = self .fc2 (op , hidden_states )
110152 hidden_states = op .Add (residual , hidden_states )
153+ if get_build_dtype () == ir .DataType .FLOAT16 :
154+ hidden_states = op .Clip (
155+ hidden_states ,
156+ op .CastLike (- 64504.0 , hidden_states ),
157+ op .CastLike (64504.0 , hidden_states ),
158+ )
111159
112160 return hidden_states
0 commit comments