1111
1212
1313__all__ = [
14+ "scaled_dot_product_attention" ,
1415 "dot_attention" ,
1516 "SelfAttentionBase" ,
1617 "SelfAttention" ,
2728]
2829
2930
31+ def scaled_dot_product_attention (
32+ query : Tensor ,
33+ key : Tensor ,
34+ value : Tensor ,
35+ * ,
36+ attention_mask : Optional [Tensor ] = None ,
37+ dropout : float = 0.0 ,
38+ v_embed_dim : Dim ,
39+ qk_embed_dim : Dim ,
40+ kv_spatial_dim : Dim ,
41+ query_spatial_dim : Dim ,
42+ is_causal : bool = False ,
43+ scale : Optional [float ] = None ,
44+ ):
45+ """
46+ Scaled dot-product attention.
47+
48+ :param query:
49+ :param key:
50+ :param value:
51+ :param attention_mask:
52+ :param dropout:
53+ :param v_embed_dim: Embedding dimension of value
54+ :param qk_embed_dim: Embedding dimension of key (and query)
55+ :param kv_spatial_dim: Spatial axis of key/value to attend over
56+ :param query_spatial_dim: Spatial axis of query
57+ :param is_causal: Special case when the attention mask should be causal (e.g. for auto-regressive decoding).
58+ Allows for more efficient implementation in some backends.
59+ :param scale: Scaling factor applied prior to softmax
60+ :return: attention output
61+ """
62+ return query ._raw_backend .scaled_dot_product_attention (
63+ query ,
64+ key ,
65+ value ,
66+ attention_mask = attention_mask ,
67+ dropout = dropout ,
68+ v_embed_dim = v_embed_dim ,
69+ qk_embed_dim = qk_embed_dim ,
70+ kv_spatial_dim = kv_spatial_dim ,
71+ query_spatial_dim = query_spatial_dim ,
72+ is_causal = is_causal ,
73+ scale = scale ,
74+ )
75+
76+
3077def dot_attention (
3178 query : Tensor ,
3279 keys : Tensor ,
@@ -54,17 +101,52 @@ def dot_attention(
54101 normally not wanted. disabled by default since behavior version 19.
55102 :return: like values but with axis removed, and maybe any additional axes from query
56103 """
57- query *= key_dim .dimension ** - 0.5
58- energy = rf .matmul (query , keys , reduce = key_dim )
59- att_weights = rf .softmax (energy , axis = axis )
60- if att_dropout_broadcast is None :
61- att_dropout_broadcast = _att_dropout_broadcast_default ()
62- att_weights = rf .dropout (att_weights , att_dropout , axis = att_dropout_broadcast and axis )
63- # Masking not needed because softmax should already have masked,
64- # so we have 0.0 att weights for padded frames.
65- att = rf .matmul (att_weights , values , reduce = axis , use_mask = False )
66- if values .feature_dim in att .dims :
67- att .feature_dim = values .feature_dim
104+ assert axis in keys .dims_set
105+ assert axis not in query .dims_set
106+ # print(f"keys dims: {keys.dims}, query dims: {query.dims}, values dims: {values.dims}")
107+ query_non_batch_dims = query .remaining_dims (keys .dims_set - {axis })
108+ if len (query_non_batch_dims ) == 0 :
109+ query_spatial = Dim (1 , name = "dot_att_query_spatial_dummy" )
110+ query = rf .expand_dim (query , dim = query_spatial )
111+ else :
112+ assert len (query_non_batch_dims ) == 1 , f"qspat={ query_non_batch_dims } , q={ query .dims } , k={ keys .dims } "
113+ query_spatial = query_non_batch_dims [0 ]
114+
115+ v_embed_dim = values .feature_dim
116+ if v_embed_dim is None :
117+ if key_dim in values .dims_set :
118+ v_embed_dim = key_dim
119+ else :
120+ relevant_dims = values .dims_set - keys .dims_set
121+ if len (relevant_dims ) == 1 :
122+ v_embed_dim = list (relevant_dims )[0 ]
123+ else :
124+ raise ValueError (f"Cannot infer v_embed_dim from values.dims={ values .dims } , keys.dims={ keys .dims } " )
125+
126+ att = scaled_dot_product_attention (
127+ query ,
128+ keys ,
129+ values ,
130+ dropout = att_dropout ,
131+ v_embed_dim = v_embed_dim ,
132+ qk_embed_dim = key_dim ,
133+ kv_spatial_dim = axis ,
134+ query_spatial_dim = query_spatial ,
135+ is_causal = False ,
136+ )
137+ if len (query_non_batch_dims ) == 0 :
138+ att = rf .squeeze (att , axis = query_spatial )
139+ # query *= key_dim.dimension**-0.5
140+ # energy = rf.matmul(query, keys, reduce=key_dim)
141+ # att_weights = rf.softmax(energy, axis=axis)
142+ # if att_dropout_broadcast is None:
143+ # att_dropout_broadcast = _att_dropout_broadcast_default()
144+ # att_weights = rf.dropout(att_weights, att_dropout, axis=att_dropout_broadcast and axis)
145+ # # Masking not needed because softmax should already have masked,
146+ # # so we have 0.0 att weights for padded frames.
147+ # att = rf.matmul(att_weights, values, reduce=axis, use_mask=False)
148+ # if values.feature_dim in att.dims:
149+ # att.feature_dim = values.feature_dim
68150 return att
69151
70152
@@ -149,7 +231,11 @@ def forward_qkv(self, source: Tensor) -> Tuple[Tensor, Tensor, Tensor]:
149231 q , k , v = rf .split (
150232 qkv ,
151233 axis = self .qkv_dim_per_head ,
152- out_dims = (self .key_dim_per_head , self .key_dim_per_head , self .value_dim_per_head ),
234+ out_dims = (
235+ self .key_dim_per_head ,
236+ self .key_dim_per_head ,
237+ self .value_dim_per_head ,
238+ ),
153239 )
154240 return q , k , v
155241
@@ -164,7 +250,11 @@ def attention(self, q: Tensor, k: Tensor, v: Tensor, *, kv_axis: Dim) -> Tensor:
164250 att_dropout = self .att_dropout ,
165251 att_dropout_broadcast = self .att_dropout_broadcast ,
166252 )
167- output , _ = rf .merge_dims (att , dims = (self .num_heads , self .value_dim_per_head ), out_dim = self .value_dim_total )
253+ output , _ = rf .merge_dims (
254+ att ,
255+ dims = (self .num_heads , self .value_dim_per_head ),
256+ out_dim = self .value_dim_total ,
257+ )
168258 if self .proj :
169259 output = self .proj (output )
170260 return output
@@ -257,7 +347,13 @@ class CausalSelfAttentionState(rf.State):
257347 State for :class:`StepwiseCausalSelfAttention`.
258348 """
259349
260- def __init__ (self , * _args , k_accum : Tensor = None , v_accum : Tensor = None , accum_axis : Dim = None ):
350+ def __init__ (
351+ self ,
352+ * _args ,
353+ k_accum : Tensor = None ,
354+ v_accum : Tensor = None ,
355+ accum_axis : Dim = None ,
356+ ):
261357 """
262358 :param k_accum: accumulated keys
263359 :param v_accum: accumulated values
@@ -330,7 +426,11 @@ def __call__(
330426 q = _apply_rope (
331427 q ,
332428 (
333- rf .gather (pos_enc , axis = hist_dim , indices = rf .last_frame_position_of_dim (hist_dim ))
429+ rf .gather (
430+ pos_enc ,
431+ axis = hist_dim ,
432+ indices = rf .last_frame_position_of_dim (hist_dim ),
433+ )
334434 if axis == single_step_dim
335435 else rf .replace_dim (pos_enc , in_dim = hist_dim , out_dim = axis )[0 ]
336436 ),
@@ -430,7 +530,9 @@ def __init__(
430530 self .linear_pos = None
431531 if with_linear_pos :
432532 self .linear_pos = rf .Linear (
433- self .in_dim , self .key_dim_total if separate_pos_emb_per_head else self .key_dim_per_head , with_bias = False
533+ self .in_dim ,
534+ self .key_dim_total if separate_pos_emb_per_head else self .key_dim_per_head ,
535+ with_bias = False ,
434536 )
435537 self .learned_pos_emb = None
436538 if learnable_pos_emb :
@@ -454,14 +556,21 @@ def __call__(self, source: Tensor, *, axis: Dim, **_kwargs) -> Tensor:
454556 pos_emb , pos_emb_spatial_dim = self .learned_pos_emb (query_spatial_dim = axis , key_value_spatial_dim = axis )
455557 else :
456558 pos_emb , pos_emb_spatial_dim = relative_positional_encoding (
457- query_spatial_dim = axis , key_value_spatial_dim = axis , feat_dim = self .pos_emb_feat_dim , device = source .device
559+ query_spatial_dim = axis ,
560+ key_value_spatial_dim = axis ,
561+ feat_dim = self .pos_emb_feat_dim ,
562+ device = source .device ,
458563 )
459564 if self .pos_emb_dropout :
460565 pos_emb = rf .dropout (pos_emb , self .pos_emb_dropout )
461566 if self .linear_pos is not None :
462567 pos_emb = self .linear_pos (pos_emb )
463568 if self .separate_pos_emb_per_head :
464- pos_emb = rf .split_dims (pos_emb , axis = self .key_dim_total , dims = (self .num_heads , self .key_dim_per_head ))
569+ pos_emb = rf .split_dims (
570+ pos_emb ,
571+ axis = self .key_dim_total ,
572+ dims = (self .num_heads , self .key_dim_per_head ),
573+ )
465574 # pos_emb: (head, 2*time1-1, d_k)
466575
467576 q , k , v = self .forward_qkv (source )
@@ -490,7 +599,11 @@ def __call__(self, source: Tensor, *, axis: Dim, **_kwargs) -> Tensor:
490599 # Masking not needed because softmax should already have masked,
491600 # so we have 0.0 att weights for padded frames.
492601 att = rf .matmul (att_weights , v , reduce = hist_dim , use_mask = False )
493- output , _ = rf .merge_dims (att , dims = (self .num_heads , self .value_dim_per_head ), out_dim = self .value_dim_total )
602+ output , _ = rf .merge_dims (
603+ att ,
604+ dims = (self .num_heads , self .value_dim_per_head ),
605+ out_dim = self .value_dim_total ,
606+ )
494607 if self .proj :
495608 output = self .proj (output )
496609 return output
@@ -581,7 +694,9 @@ def __init__(
581694 self .linear_pos = None
582695 if with_linear_pos :
583696 self .linear_pos = rf .Linear (
584- self .in_dim , self .key_dim_total if separate_pos_emb_per_head else self .key_dim_per_head , with_bias = False
697+ self .in_dim ,
698+ self .key_dim_total if separate_pos_emb_per_head else self .key_dim_per_head ,
699+ with_bias = False ,
585700 )
586701 self .learned_pos_emb = None
587702 if learnable_pos_emb :
@@ -600,7 +715,11 @@ def __init__(
600715 self .pos_emb_dropout = pos_emb_dropout
601716
602717 def __call__ (
603- self , source : Tensor , * , axis : Dim , state : Optional [CausalSelfAttentionState ] = None
718+ self ,
719+ source : Tensor ,
720+ * ,
721+ axis : Dim ,
722+ state : Optional [CausalSelfAttentionState ] = None ,
604723 ) -> Tuple [Tensor , CausalSelfAttentionState ]:
605724 """forward"""
606725 q , k , v = self .forward_qkv (source )
@@ -621,7 +740,11 @@ def __call__(
621740 if self .linear_pos is not None :
622741 pos_emb = self .linear_pos (pos_emb )
623742 if self .separate_pos_emb_per_head :
624- pos_emb = rf .split_dims (pos_emb , axis = self .key_dim_total , dims = (self .num_heads , self .key_dim_per_head ))
743+ pos_emb = rf .split_dims (
744+ pos_emb ,
745+ axis = self .key_dim_total ,
746+ dims = (self .num_heads , self .key_dim_per_head ),
747+ )
625748 # pos_emb: (head, 2*time1-1, d_k)
626749
627750 q_with_bias_u = (q + self .pos_bias_u ) if self .pos_bias_u is not None else q # (batch, head, time1, d_k)
@@ -645,7 +768,11 @@ def __call__(
645768 # Masking not needed because softmax should already have masked,
646769 # so we have 0.0 att weights for padded frames.
647770 att = rf .matmul (att_weights , v , reduce = hist_dim , use_mask = False )
648- output , _ = rf .merge_dims (att , dims = (self .num_heads , self .value_dim_per_head ), out_dim = self .value_dim_total )
771+ output , _ = rf .merge_dims (
772+ att ,
773+ dims = (self .num_heads , self .value_dim_per_head ),
774+ out_dim = self .value_dim_total ,
775+ )
649776 if self .proj :
650777 output = self .proj (output )
651778 return output , new_state
@@ -773,7 +900,11 @@ def attention(self, q: Tensor, k: Tensor, v: Tensor, *, kv_axis: Dim) -> Tensor:
773900 att_dropout = self .att_dropout ,
774901 att_dropout_broadcast = self .att_dropout_broadcast ,
775902 )
776- output , _ = rf .merge_dims (att , dims = (self .num_heads , self .value_dim_per_head ), out_dim = self .value_dim_total )
903+ output , _ = rf .merge_dims (
904+ att ,
905+ dims = (self .num_heads , self .value_dim_per_head ),
906+ out_dim = self .value_dim_total ,
907+ )
777908 if self .proj :
778909 output = self .proj (output )
779910 return output
@@ -788,7 +919,14 @@ class LearnedRelativePositionalEncoding(rf.Module):
788919 https://github.com/rwth-i6/returnn_common/wiki/Relative-positional-encoding
789920 """
790921
791- def __init__ (self , feat_dim : Dim , * , clipping : int = 16 , dtype : Optional [str ] = None , causal : bool = False ):
922+ def __init__ (
923+ self ,
924+ feat_dim : Dim ,
925+ * ,
926+ clipping : int = 16 ,
927+ dtype : Optional [str ] = None ,
928+ causal : bool = False ,
929+ ):
792930 """
793931 :param feat_dim: feature dim, for the emb matrix and output
794932 :param clipping: max distance to consider. emb matrix shape is [2 * clipping + 1, feat_dim] if not causal,
@@ -889,7 +1027,10 @@ def _make_indices(
8891027 # The min value is with kv_pos=0, q_pos=q_len-1: -(q_len-1)
8901028 # The max value is with kv_pos=kv_len-1, q_pos=0: k_len-1
8911029 indices , _ = rf .concat (
892- (q_pos_vec - query_spatial_dim_m1 .get_dim_value_tensor (), query_spatial_dim_m1 ),
1030+ (
1031+ q_pos_vec - query_spatial_dim_m1 .get_dim_value_tensor (),
1032+ query_spatial_dim_m1 ,
1033+ ),
8931034 (kv_pos_vec , key_value_spatial_dim ),
8941035 out_dim = out_spatial_dim ,
8951036 handle_dynamic_dims = False ,
@@ -933,9 +1074,18 @@ def relative_positional_encoding(
9331074 """
9341075 if not dtype :
9351076 dtype = rf .get_default_float_dtype ()
1077+
9361078 if not device :
9371079 device = rf .get_default_device ()
938- cache_key = (query_spatial_dim , key_value_spatial_dim , feat_dim , query_offset , dtype , device )
1080+
1081+ cache_key = (
1082+ query_spatial_dim ,
1083+ key_value_spatial_dim ,
1084+ feat_dim ,
1085+ query_offset ,
1086+ dtype ,
1087+ device ,
1088+ )
9391089 cache_entry = _relative_positional_encoding_cache .get (cache_key )
9401090 if cache_entry is not None :
9411091 return cache_entry
0 commit comments