fused_attn.py 6.47 KB
Newer Older
Haotian Tang's avatar
Haotian Tang committed
1
2
3
import torch
import torch.nn as nn
import awq_inference_engine
Casper Hansen's avatar
Casper Hansen committed
4
from torch.nn import functional as F
Casper Hansen's avatar
Casper Hansen committed
5
from transformers.models.llama.modeling_llama import apply_rotary_pos_emb, LlamaRotaryEmbedding
Haotian Tang's avatar
Haotian Tang committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

class QuantLlamaRotaryEmbedding(nn.Module):
    def __init__(self, dim, max_position_embeddings=2048, base=10000, device=None):
        super().__init__()

        self.dim = dim
        self.max_position_embeddings = max_position_embeddings
        self.base = base
        inv_freq = 1.0 / (self.base ** (torch.arange(0, self.dim, 2).float().to(device) / self.dim))
        self.register_buffer("inv_freq", inv_freq)
        # Build here to make `torch.jit.trace` work.
        self._set_cos_sin_cache(
            seq_len=max_position_embeddings, device=self.inv_freq.device, dtype=torch.get_default_dtype()
        )

    def _set_cos_sin_cache(self, seq_len, device, dtype):
        self.max_seq_len_cached = seq_len
        t = torch.arange(self.max_seq_len_cached, device=device, dtype=self.inv_freq.dtype)

        freqs = torch.einsum("i,j->ij", t, self.inv_freq)
        # Different from paper, but it uses a different permutation in order to obtain the same calculation
        emb = torch.cat((freqs, freqs), dim=-1)
        
        cos = freqs.cos()
        sin = freqs.sin()
        cache = torch.cat((cos, sin), dim=-1)
        
        self.register_buffer("cos_sin_cache", cache.half(), persistent=False)
    
    def forward(
        self,
        query: torch.Tensor,
        key: torch.Tensor,
        positions: torch.Tensor,
    ):
Casper Hansen's avatar
Casper Hansen committed
41
42
43
44
45
        batch_size, seq_len, _ = query.shape
        query = query.view(batch_size * seq_len, -1)
        key = key.view(batch_size * seq_len, -1)
        positions = positions.view(-1).to(query.device)

Haotian Tang's avatar
Haotian Tang committed
46
47
48
49
        # Apply rotary embedding to the query and key before passing them
        # to the attention op.
        query = query.contiguous()
        key = key.contiguous()
Casper Hansen's avatar
Casper Hansen committed
50

Casper Hansen's avatar
Casper Hansen committed
51
        awq_inference_engine.rotary_embedding(
Haotian Tang's avatar
Haotian Tang committed
52
53
54
55
56
            positions,
            query,
            key,
            self.dim,
            self.cos_sin_cache,
Casper Hansen's avatar
Casper Hansen committed
57
            True # is_neox
Haotian Tang's avatar
Haotian Tang committed
58
        )
Casper Hansen's avatar
Casper Hansen committed
59
60
61
62

        query = query.view(batch_size, seq_len, -1)
        key = key.view(batch_size, seq_len, -1)

Haotian Tang's avatar
Haotian Tang committed
63
64
65
66
67
68
69
70
71
72
73
        return query, key

class QuantLlamaAttention(nn.Module):
    """Multi-headed attention from 'Attention Is All You Need' paper"""

    def __init__(
        self,
        hidden_size,
        num_heads,
        qkv_proj,
        o_proj,
Casper Hansen's avatar
Casper Hansen committed
74
        dev,
Casper Hansen's avatar
Casper Hansen committed
75
76
        max_new_tokens,
        use_hf_rotary=True
Haotian Tang's avatar
Haotian Tang committed
77
78
79
80
81
    ):
        super().__init__()
        self.hidden_size = hidden_size
        self.num_heads = num_heads
        self.head_dim = hidden_size // num_heads
Casper Hansen's avatar
Casper Hansen committed
82
        self.use_hf_rotary = use_hf_rotary
Haotian Tang's avatar
Haotian Tang committed
83
84
85
86
87
88

        if (self.head_dim * num_heads) != self.hidden_size:
            raise ValueError(f"hidden_size must be divisible by num_heads (got `hidden_size`: {self.hidden_size}"
                             f" and `num_heads`: {num_heads}).")
        self.qkv_proj = qkv_proj
        self.o_proj = o_proj
Casper Hansen's avatar
Casper Hansen committed
89
90
91
92
93

        if use_hf_rotary:
            self.rotary_emb = LlamaRotaryEmbedding(self.head_dim, max_new_tokens, device=dev)
        else:
            self.rotary_emb = QuantLlamaRotaryEmbedding(self.head_dim, max_position_embeddings=max_new_tokens, device = dev)
Haotian Tang's avatar
Haotian Tang committed
94
95
96
97
98
99
100

    def forward(self, hidden_states, past_key_value=None, attention_mask=None, position_ids=None, output_attentions=False, use_cache=False):
        """Input shape: Batch x Time x Channel"""

        bsz, q_len, _ = hidden_states.size()

        qkv_states = self.qkv_proj(hidden_states)
Casper Hansen's avatar
Casper Hansen committed
101
102

        if self.use_hf_rotary:
Casper Hansen's avatar
Casper Hansen committed
103
104
105
106
107
            qkv_states = qkv_states.view(bsz, q_len, 3, self.num_heads, self.head_dim)

            # This updates the query and key states in-place, saving VRAM.
            query_states, key_states, value_states = torch.split(qkv_states, 1, dim=2)
            
Casper Hansen's avatar
Casper Hansen committed
108
109
110
111
112
113
114
115
116
117
118
119
            query_states = query_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
            key_states = key_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
            value_states = value_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)

            kv_seq_len = key_states.shape[-2]
            if past_key_value is not None:
                kv_seq_len += past_key_value[0].shape[-2]
            
            cos, sin = self.rotary_emb(value_states, seq_len=kv_seq_len)
            query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin, position_ids)

        else:
Casper Hansen's avatar
Casper Hansen committed
120
            query_states, key_states, value_states = qkv_states.chunk(chunks=3, dim=-1)
Casper Hansen's avatar
Casper Hansen committed
121
            query_states, key_states = self.rotary_emb(query_states, key_states, position_ids)
Casper Hansen's avatar
Casper Hansen committed
122

Casper Hansen's avatar
Casper Hansen committed
123
124
125
            query_states = query_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
            key_states = key_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
            value_states = value_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
Casper Hansen's avatar
Casper Hansen committed
126
            
Haotian Tang's avatar
Haotian Tang committed
127
128
        
        del qkv_states
Casper Hansen's avatar
Casper Hansen committed
129
        
Haotian Tang's avatar
Haotian Tang committed
130
131
132
133
134
135
        is_causal = past_key_value is None

        kv_seq_len = q_len
        if past_key_value is not None:
            kv_seq_len += past_key_value[0].shape[-2]
        
Casper Hansen's avatar
Casper Hansen committed
136
        value_states = value_states.to(key_states.device)
Haotian Tang's avatar
Haotian Tang committed
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

        if past_key_value is not None:
            # reuse k, v, self_attention
            key_states = torch.cat([past_key_value[0], key_states], dim=2)
            value_states = torch.cat([past_key_value[1], value_states], dim=2)

        if use_cache:
            # Since qkv_proj is fused, query_states etc will hold a reference to the original qkv_states tensor
            # which can cause excessive memory usage by the cache. `contiguous` is a convenient way to workaround this.
            key_states = key_states.contiguous()
            value_states = value_states.contiguous()
            query_states = query_states.contiguous()

        past_key_value = (key_states, value_states) if use_cache else None

        # with torch.backends.cuda.sdp_kernel(enable_math=False):
        attn_output = F.scaled_dot_product_attention(query_states, key_states, value_states, is_causal=is_causal)
        del query_states, key_states, value_states

        attn_output = attn_output.transpose(1, 2).reshape(bsz, q_len, self.hidden_size)
        attn_output = self.o_proj(attn_output)

        return attn_output, None, past_key_value