# ------------------------------------------------------------------------ # Copyright (c) 2023 megvii-model. All Rights Reserved. # ------------------------------------------------------------------------ # Modified by Shihao Wang # ------------------------------------------------------------------------ # flash-attention import math import torch import torch.nn as nn from torch.nn.init import ( xavier_uniform_, constant_, xavier_normal_ ) from torch.nn.functional import linear from einops import rearrange from mmcv.runner import auto_fp16 from mmcv.runner.base_module import BaseModule try: from flash_attn.flash_attn_interface import flash_attn_unpadded_kvpacked_func except ImportError: from flash_attn.flash_attn_interface import ( flash_attn_varlen_kvpacked_func as flash_attn_unpadded_kvpacked_func, ) from flash_attn.bert_padding import unpad_input, pad_input, index_first_axis def _in_projection_packed(q, k, v, w, b = None): w_q, w_k, w_v = w.chunk(3) if b is None: b_q = b_k = b_v = None else: b_q, b_k, b_v = b.chunk(3) return linear(q, w_q, b_q), linear(k, w_k, b_k), linear(v, w_v, b_v) class FlashAttention(nn.Module): """Implement the scaled dot product attention with softmax. Arguments --------- softmax_scale: The temperature to use for the softmax attention. (default: 1/sqrt(d_keys) where d_keys is computed at runtime) attention_dropout: The dropout rate to apply to the attention (default: 0.1) """ def __init__(self, softmax_scale=None, attention_dropout=0.0, device=None, dtype=None): super().__init__() self.softmax_scale = softmax_scale self.dropout_p = attention_dropout self.fp16_enabled = True @auto_fp16(apply_to=('q', 'kv'), out_fp32=True) def forward(self, q, kv, causal=False, key_padding_mask=None): """Implements the multihead softmax attention. Arguments --------- q: The tensor containing the query. (B, T, H, D) kv: The tensor containing the key, and value. (B, S, 2, H, D) key_padding_mask: a bool tensor of shape (B, S) """ assert q.dtype in [torch.float16, torch.bfloat16] and kv.dtype in [torch.float16, torch.bfloat16] assert q.is_cuda and kv.is_cuda assert q.shape[0] == kv.shape[0] and q.shape[-2] == kv.shape[-2] and q.shape[-1] == kv.shape[-1] batch_size = q.shape[0] seqlen_q, seqlen_k = q.shape[1], kv.shape[1] if key_padding_mask is None: q, kv = rearrange(q, 'b s ... -> (b s) ...'), rearrange(kv, 'b s ... -> (b s) ...') max_sq, max_sk = seqlen_q, seqlen_k cu_seqlens_q = torch.arange(0, (batch_size + 1) * seqlen_q, step=seqlen_q, dtype=torch.int32, device=q.device) cu_seqlens_k = torch.arange(0, (batch_size + 1) * seqlen_k, step=seqlen_k, dtype=torch.int32, device=kv.device) output = flash_attn_unpadded_kvpacked_func( q, kv, cu_seqlens_q, cu_seqlens_k, max_sq, max_sk, self.dropout_p if self.training else 0.0, softmax_scale=self.softmax_scale, causal=causal ) output = rearrange(output, '(b s) ... -> b s ...', b=batch_size) else: nheads = kv.shape[-2] q = rearrange(q, 'b s ... -> (b s) ...') max_sq = seqlen_q cu_seqlens_q = torch.arange(0, (batch_size + 1) * seqlen_q, step=seqlen_q, dtype=torch.int32, device=q.device) x = rearrange(kv, 'b s two h d -> b s (two h d)') x_unpad, indices, cu_seqlens_k, max_sk = unpad_input(x, key_padding_mask) x_unpad = rearrange(x_unpad, 'nnz (two h d) -> nnz two h d', two=2, h=nheads) output_unpad = flash_attn_unpadded_kvpacked_func( q, x_unpad, cu_seqlens_q, cu_seqlens_k, max_sq, max_sk, self.dropout_p if self.training else 0.0, softmax_scale=self.softmax_scale, causal=causal ) output = rearrange(output_unpad, '(b s) ... -> b s ...', b=batch_size) return output, None class FlashMHA(nn.Module): def __init__(self, embed_dim, num_heads, bias=True, batch_first=True, attention_dropout=0.0, causal=False, device=None, dtype=None, **kwargs) -> None: assert batch_first factory_kwargs = {'device': device, 'dtype': dtype} super().__init__() self.embed_dim = embed_dim self.causal = causal self.bias = bias self.num_heads = num_heads assert self.embed_dim % num_heads == 0, "self.kdim must be divisible by num_heads" self.head_dim = self.embed_dim // num_heads assert self.head_dim % 8 == 0 and self.head_dim <= 128, "Only support head_dim <= 128 and divisible by 8" self.in_proj_weight = nn.Parameter(torch.empty((3 * embed_dim, embed_dim))) if bias: self.in_proj_bias = nn.Parameter(torch.empty(3 * embed_dim)) else: self.register_parameter('in_proj_bias', None) self.inner_attn = FlashAttention(attention_dropout=attention_dropout, **factory_kwargs) self.out_proj = nn.Linear(embed_dim, embed_dim, bias=bias) self._reset_parameters() def _reset_parameters(self) -> None: xavier_uniform_(self.in_proj_weight) if self.in_proj_bias is not None: constant_(self.in_proj_bias, 0.) constant_(self.out_proj.bias, 0.) def forward(self, q, k, v, key_padding_mask=None): """x: (batch, seqlen, hidden_dim) (where hidden_dim = num heads * head dim) key_padding_mask: bool tensor of shape (batch, seqlen) """ # q, k, v = self.Wq(q), self.Wk(k), self.Wv(v) q, k, v = _in_projection_packed(q, k, v, self.in_proj_weight, self.in_proj_bias) q = rearrange(q, 'b s (h d) -> b s h d', h=self.num_heads) k = rearrange(k, 'b s (h d) -> b s h d', h=self.num_heads) v = rearrange(v, 'b s (h d) -> b s h d', h=self.num_heads) kv = torch.stack([k, v], dim=2) context, attn_weights = self.inner_attn(q, kv, key_padding_mask=key_padding_mask, causal=self.causal) return self.out_proj(rearrange(context, 'b s h d -> b s (h d)')), attn_weights