@@ -84,6 +84,7 @@ def __init__(
8484 spatial_dims : int = 3 ,
8585 downsample : str | nn .Module = "merging" ,
8686 use_v2 : bool = False ,
87+ use_flash_attention : bool = False ,
8788 ) -> None :
8889 """
8990 Args:
@@ -110,6 +111,7 @@ def __init__(
110111 user-specified `nn.Module` following the API defined in :py:class:`monai.networks.nets.PatchMerging`.
111112 The default is currently `"merging"` (the original version defined in v0.9.0).
112113 use_v2: using swinunetr_v2, which adds a residual convolution block at the beggining of each swin stage.
114+ use_flash_attention: use flash attention (scaled dot product attention) at inference.
113115
114116 Examples::
115117
@@ -170,6 +172,7 @@ def __init__(
170172 spatial_dims = spatial_dims ,
171173 downsample = look_up_option (downsample , MERGING_MODE ) if isinstance (downsample , str ) else downsample ,
172174 use_v2 = use_v2 ,
175+ use_flash_attention = use_flash_attention ,
173176 )
174177
175178 self .encoder1 = UnetrBasicBlock (
@@ -457,6 +460,7 @@ def __init__(
457460 qkv_bias : bool = False ,
458461 attn_drop : float = 0.0 ,
459462 proj_drop : float = 0.0 ,
463+ use_flash_attention : bool = False ,
460464 ) -> None :
461465 """
462466 Args:
@@ -466,12 +470,16 @@ def __init__(
466470 qkv_bias: add a learnable bias to query, key, value.
467471 attn_drop: attention dropout rate.
468472 proj_drop: dropout rate of output.
473+ use_flash_attention: if True, use ``torch.nn.functional.scaled_dot_product_attention`` for the
474+ windowed attention. Equivalent to the default path but faster at inference; only used when
475+ autograd is disabled and the module is not scripted.
469476 """
470477
471478 super ().__init__ ()
472479 self .dim = dim
473480 self .window_size = window_size
474481 self .num_heads = num_heads
482+ self .use_flash_attention = use_flash_attention
475483 head_dim = dim // num_heads
476484 self .scale = head_dim ** - 0.5
477485 mesh_args = torch .meshgrid .__kwdefaults__
@@ -528,12 +536,26 @@ def forward(self, x, mask):
528536 b , n , c = x .shape
529537 qkv = self .qkv (x ).reshape (b , n , 3 , self .num_heads , c // self .num_heads ).permute (2 , 0 , 3 , 1 , 4 )
530538 q , k , v = qkv [0 ], qkv [1 ], qkv [2 ]
531- q = q * self .scale
532- attn = q @ k .transpose (- 2 , - 1 )
533539 relative_position_bias = self .relative_position_bias_table [
534540 self .relative_position_index .clone ()[:n , :n ].reshape (- 1 ) # type: ignore[operator]
535541 ].reshape (n , n , - 1 )
536542 relative_position_bias = relative_position_bias .permute (2 , 0 , 1 ).contiguous ()
543+ if self .use_flash_attention and not torch .jit .is_scripting () and not torch .is_grad_enabled ():
544+ # additive bias combines the relative position bias and, for shifted windows, the attention mask
545+ if mask is not None :
546+ nw = mask .shape [0 ]
547+ bias = relative_position_bias .view (1 , 1 , self .num_heads , n , n ) + mask .view (1 , nw , 1 , n , n )
548+ bias = bias .expand (b // nw , nw , self .num_heads , n , n ).reshape (b , self .num_heads , n , n )
549+ else :
550+ bias = relative_position_bias .unsqueeze (0 )
551+ x = torch .nn .functional .scaled_dot_product_attention (
552+ q , k , v , attn_mask = bias .to (q .dtype ), dropout_p = 0.0 , scale = self .scale
553+ )
554+ x = x .transpose (1 , 2 ).reshape (b , n , c )
555+ return self .proj_drop (self .proj (x ))
556+
557+ q = q * self .scale
558+ attn = q @ k .transpose (- 2 , - 1 )
537559 attn = attn + relative_position_bias .unsqueeze (0 )
538560 if mask is not None :
539561 nw = mask .shape [0 ]
@@ -572,6 +594,7 @@ def __init__(
572594 act_layer : str = "GELU" ,
573595 norm_layer : type [LayerNorm ] = nn .LayerNorm ,
574596 use_checkpoint : bool = False ,
597+ use_flash_attention : bool = False ,
575598 ) -> None :
576599 """
577600 Args:
@@ -587,6 +610,7 @@ def __init__(
587610 act_layer: activation layer.
588611 norm_layer: normalization layer.
589612 use_checkpoint: use gradient checkpointing for reduced memory usage.
613+ use_flash_attention: use flash attention (scaled dot product attention) at inference.
590614 """
591615
592616 super ().__init__ ()
@@ -604,6 +628,7 @@ def __init__(
604628 qkv_bias = qkv_bias ,
605629 attn_drop = attn_drop ,
606630 proj_drop = drop ,
631+ use_flash_attention = use_flash_attention ,
607632 )
608633
609634 self .drop_path = DropPath (drop_path ) if drop_path > 0.0 else nn .Identity ()
@@ -856,6 +881,7 @@ def __init__(
856881 norm_layer : type [LayerNorm ] = nn .LayerNorm ,
857882 downsample : nn .Module | None = None ,
858883 use_checkpoint : bool = False ,
884+ use_flash_attention : bool = False ,
859885 ) -> None :
860886 """
861887 Args:
@@ -871,6 +897,7 @@ def __init__(
871897 norm_layer: normalization layer.
872898 downsample: an optional downsampling layer at the end of the layer.
873899 use_checkpoint: use gradient checkpointing for reduced memory usage.
900+ use_flash_attention: use flash attention (scaled dot product attention) at inference.
874901 """
875902
876903 super ().__init__ ()
@@ -893,6 +920,7 @@ def __init__(
893920 drop_path = drop_path [i ] if isinstance (drop_path , list ) else drop_path ,
894921 norm_layer = norm_layer ,
895922 use_checkpoint = use_checkpoint ,
923+ use_flash_attention = use_flash_attention ,
896924 )
897925 for i in range (depth )
898926 ]
@@ -961,6 +989,7 @@ def __init__(
961989 spatial_dims : int = 3 ,
962990 downsample = "merging" ,
963991 use_v2 = False ,
992+ use_flash_attention : bool = False ,
964993 ) -> None :
965994 """
966995 Args:
@@ -983,6 +1012,7 @@ def __init__(
9831012 user-specified `nn.Module` following the API defined in :py:class:`monai.networks.nets.PatchMerging`.
9841013 The default is currently `"merging"` (the original version defined in v0.9.0).
9851014 use_v2: using swinunetr_v2, which adds a residual convolution block at the beginning of each swin stage.
1015+ use_flash_attention: use flash attention (scaled dot product attention) at inference.
9861016 """
9871017
9881018 super ().__init__ ()
@@ -1025,6 +1055,7 @@ def __init__(
10251055 norm_layer = norm_layer ,
10261056 downsample = down_sample_mod ,
10271057 use_checkpoint = use_checkpoint ,
1058+ use_flash_attention = use_flash_attention ,
10281059 )
10291060 if i_layer == 0 :
10301061 self .layers1 .append (layer )
0 commit comments