@@ -32,8 +32,14 @@ def fuse(self, normalize_node, input_name_to_nodes, output_name_to_node):
3232 [1 , None , 0 , 0 , 0 ],
3333 )
3434 if qkv_nodes is None :
35- logger .debug ("fuse_conformer_attention: failed to match qkv path" )
36- return
35+ qkv_nodes = self .model .match_parent_path (
36+ normalize_node ,
37+ ["MatMul" , "Reshape" , "Transpose" , "MatMul" ],
38+ [1 , 0 , 0 , 0 ],
39+ )
40+ if qkv_nodes is None :
41+ logger .debug ("fuse_conformer_attention: failed to match qkv path" )
42+ return
3743
3844 reshape_qkv , transpose_qkv , matmul_qkv = qkv_nodes [- 3 ], qkv_nodes [- 2 ], qkv_nodes [- 1 ]
3945
@@ -50,33 +56,50 @@ def fuse(self, normalize_node, input_name_to_nodes, output_name_to_node):
5056 [1 , 0 , 0 , 0 ],
5157 )
5258 if v_nodes is None :
53- logger .debug ("fuse_conformer_attention: failed to match v path" )
54- return
59+ v_nodes = self .model .match_parent_path (
60+ matmul_qkv ,
61+ ["Transpose" , "Reshape" , "MatMul" ],
62+ [1 , 0 , 0 ],
63+ )
64+ if v_nodes is None :
65+ logger .debug ("fuse_conformer_attention: failed to match v path" )
66+ return
5567 else :
5668 concat_v = v_nodes [0 ]
5769 concat_parent = self .model .get_parent (concat_v , 0 , None )
5870 present_v = concat_v .output [0 ]
5971 past_v = concat_parent .output [0 ]
6072
61- add_v , matmul_v = v_nodes [- 2 ], v_nodes [- 1 ]
73+ add_v = v_nodes [- 2 ] if len (v_nodes ) >= 2 and v_nodes [- 2 ].op_type == "Add" else None
74+ matmul_v = v_nodes [- 1 ]
6275
6376 attn_mask = ""
6477 qk_nodes = self .model .match_parent_path (
6578 matmul_qkv ,
6679 ["Softmax" , "Add" , "MatMul" ],
6780 [0 , 0 , 0 ],
6881 )
82+ where_qk = None
6983 if qk_nodes is None :
7084 qk_nodes = self .model .match_parent_path (
7185 matmul_qkv ,
7286 ["Where" , "Softmax" , "Where" , "Add" , "MatMul" ],
7387 [0 , 2 , 0 , 2 , 0 ],
7488 )
7589 if qk_nodes is None :
76- logger .debug ("fuse_conformer_attention: failed to match qk path" )
77- return
90+ qk_nodes = self .model .match_parent_path (
91+ matmul_qkv ,
92+ ["Where" , "Softmax" , "Where" , "Div" , "Add" , "MatMul" ],
93+ [0 , 2 , 0 , 2 , 0 , 0 ],
94+ )
95+ if qk_nodes is None :
96+ logger .debug ("fuse_conformer_attention: failed to match qk path" )
97+ return
98+ where_qk = qk_nodes [2 ]
99+ else :
100+ where_qk = qk_nodes [2 ]
78101
79- where_qk = qk_nodes [ 2 ]
102+ if where_qk is not None :
80103 mask_nodes = self .model .match_parent_path (
81104 where_qk ,
82105 ["Equal" , "Unsqueeze" , "Cast" ],
@@ -99,20 +122,46 @@ def fuse(self, normalize_node, input_name_to_nodes, output_name_to_node):
99122 [0 , 0 , 0 , 0 , 0 ],
100123 )
101124 if q_nodes is None :
102- logger .debug ("fuse_conformer_attention: failed to match q path" )
103- return
125+ q_nodes = self .model .match_parent_path (
126+ matmul_qk ,
127+ ["Transpose" , "Add" , "Reshape" , "MatMul" ],
128+ [0 , 0 , 0 , 1 ],
129+ )
130+ if q_nodes is None :
131+ q_nodes = self .model .match_parent_path (
132+ matmul_qk ,
133+ ["Transpose" , "Add" , "Reshape" , "MatMul" ],
134+ [0 , 0 , 0 , 0 ],
135+ )
136+ if q_nodes is None :
137+ logger .debug ("fuse_conformer_attention: failed to match q path" )
138+ return
104139
105- reshape_q , add_q , matmul_q = q_nodes [- 3 ], q_nodes [- 2 ], q_nodes [- 1 ]
140+ reshape_q = next ((node for node in q_nodes if node .op_type == "Reshape" ), None )
141+ add_q = next ((node for node in q_nodes if node .op_type == "Add" ), None )
142+ matmul_q = next ((node for node in reversed (q_nodes ) if node .op_type == "MatMul" ), None )
143+ if reshape_q is None or add_q is None or matmul_q is None :
144+ logger .debug ("fuse_conformer_attention: failed to identify q reshape/add/matmul nodes" )
145+ return
106146
107147 extra_q_nodes = self .model .match_parent_path (
108148 add_qk ,
109149 ["Reshape" , "Transpose" , "MatMul" , "Transpose" , "Reshape" , "Div" ],
110150 [1 , 0 , 0 , 0 , 0 , 0 ],
111151 )
112- if extra_q_nodes is not None and q_nodes [0 ] != extra_q_nodes [- 1 ]:
152+ if extra_q_nodes is not None and q_nodes [0 ]. op_type in [ "Div" , "Mul" ] and q_nodes [ 0 ] != extra_q_nodes [- 1 ]:
113153 logger .debug ("fuse_conformer_attention: failed to match extra q path" )
114154 return
115155
156+ if extra_q_nodes is None :
157+ nemotron_extra_q_nodes = self .model .match_parent_path (
158+ add_qk ,
159+ ["Slice" , "Reshape" , "Slice" , "Reshape" , "Pad" , "MatMul" , "Transpose" , "Add" ],
160+ [1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
161+ )
162+ if nemotron_extra_q_nodes is not None :
163+ extra_q_nodes = nemotron_extra_q_nodes
164+
116165 past_k , present_k = "" , ""
117166 k_nodes = self .model .match_parent_path (
118167 matmul_qk ,
@@ -132,24 +181,50 @@ def fuse(self, normalize_node, input_name_to_nodes, output_name_to_node):
132181 [1 , 0 , 0 , 0 ],
133182 )
134183 if k_nodes is None :
135- logger .debug ("fuse_conformer_attention: failed to match k path" )
136- return
184+ k_nodes = self .model .match_parent_path (
185+ matmul_qk ,
186+ ["Transpose" , "Reshape" , "MatMul" ],
187+ [1 , 0 , 0 ],
188+ )
189+ if k_nodes is None :
190+ logger .debug ("fuse_conformer_attention: failed to match k path" )
191+ return
137192 else :
138193 concat_k = k_nodes [1 ]
139194 concat_parent = self .model .get_parent (concat_k , 0 , None )
140195 past_k = concat_parent .output [0 ]
141196 present_k = concat_k .output [0 ]
142197
143- add_k , matmul_k = k_nodes [- 2 ], k_nodes [- 1 ]
198+ add_k = k_nodes [- 2 ] if len (k_nodes ) >= 2 and k_nodes [- 2 ].op_type == "Add" else None
199+ matmul_k = k_nodes [- 1 ]
144200
145201 num_heads , hidden_size = self .get_num_heads_and_hidden_size (reshape_q )
146202 if num_heads <= 0 or hidden_size <= 0 or (hidden_size % num_heads ) != 0 :
147203 logger .debug ("fuse_conformer_attention: failed to detect num_heads or hidden_size" )
148204 return
149205
206+ # Validate attention_bias: the Attention and MultiHeadAttention kernels require a 4-D
207+ # tensor with shape [batch_size or 1, num_heads or 1, sequence_length, total_sequence_length].
208+ # Scalar or 1-D initializers (e.g. a plain QK scaling constant) must not be forwarded as
209+ # attention_bias. Non-initializer values (computed positional-bias outputs) are kept as-is.
210+ attention_bias = add_qk .input [1 ]
211+ bias_init = self .model .get_initializer (attention_bias )
212+ if bias_init is not None and len (bias_init .dims ) != 4 :
213+ logger .debug (
214+ "fuse_conformer_attention: skipping attention_bias %s with dims %s (expected 4-D)" ,
215+ attention_bias ,
216+ list (bias_init .dims ),
217+ )
218+ attention_bias = ""
219+
150220 new_node = None
151221 use_packed_attention_op = (
152- matmul_q .input [0 ] == matmul_k .input [0 ] and matmul_k .input [0 ] == matmul_v .input [0 ] and extra_q_nodes is None
222+ matmul_q .input [0 ] == matmul_k .input [0 ]
223+ and matmul_k .input [0 ] == matmul_v .input [0 ]
224+ and extra_q_nodes is None
225+ and add_q is not None
226+ and add_k is not None
227+ and add_v is not None
153228 )
154229 if use_packed_attention_op :
155230 # Self-attention, use Attention op
@@ -165,7 +240,7 @@ def fuse(self, normalize_node, input_name_to_nodes, output_name_to_node):
165240 hidden_size = hidden_size ,
166241 first_input = matmul_q .input [0 ],
167242 output = reshape_qkv .output [0 ],
168- add_qk_str = add_qk . input [ 1 ] ,
243+ add_qk_str = attention_bias ,
169244 past_k = past_k ,
170245 past_v = past_v ,
171246 present_k = present_k ,
@@ -183,7 +258,7 @@ def fuse(self, normalize_node, input_name_to_nodes, output_name_to_node):
183258 hidden_size = hidden_size ,
184259 output = reshape_qkv .output [0 ],
185260 key_padding_mask = attn_mask ,
186- add_qk = add_qk . input [ 1 ] ,
261+ add_qk = attention_bias ,
187262 past_k = past_k ,
188263 past_v = past_v ,
189264 present_k = present_k ,
0 commit comments