transformer-lib 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- transformer_lib/__init__.py +21 -0
- transformer_lib/attention/__init__.py +19 -0
- transformer_lib/attention/causal_attention.py +60 -0
- transformer_lib/attention/cross_attention.py +29 -0
- transformer_lib/attention/global_attention.py +45 -0
- transformer_lib/attention/grouped_q_attention.py +65 -0
- transformer_lib/attention/linear_attention.py +33 -0
- transformer_lib/attention/multi_head_attention.py +46 -0
- transformer_lib/attention/multi_head_latent_attention.py +48 -0
- transformer_lib/attention/multi_q_attention.py +49 -0
- transformer_lib/attention/self_attention.py +35 -0
- transformer_lib-0.1.0.dist-info/METADATA +47 -0
- transformer_lib-0.1.0.dist-info/RECORD +14 -0
- transformer_lib-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .attention import (
|
|
2
|
+
SelfAttention,
|
|
3
|
+
MultiHeadAttention,
|
|
4
|
+
CausalAttention,
|
|
5
|
+
CrossAttention,
|
|
6
|
+
GlobalAttention,
|
|
7
|
+
MultiQueryAttention,
|
|
8
|
+
MultiHeadLatentAttention,
|
|
9
|
+
GroupedQueryAttention,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"SelfAttention",
|
|
14
|
+
"MultiHeadAttention",
|
|
15
|
+
"CausalAttention",
|
|
16
|
+
"CrossAttention",
|
|
17
|
+
"GlobalAttention",
|
|
18
|
+
"MultiQueryAttention",
|
|
19
|
+
"MultiHeadLatentAttention",
|
|
20
|
+
"GroupedQueryAttention",
|
|
21
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .self_attention import SelfAttention
|
|
2
|
+
from .multi_head_attention import MultiHeadAttention
|
|
3
|
+
from .causal_attention import CausalAttention
|
|
4
|
+
from .cross_attention import CrossAttention
|
|
5
|
+
from .global_attention import GlobalAttention
|
|
6
|
+
from .multi_q_attention import MultiQueryAttention
|
|
7
|
+
from .multi_head_latent_attention import MultiHeadLatentAttention
|
|
8
|
+
from .grouped_q_attention import GroupedQueryAttention
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"SelfAttention",
|
|
12
|
+
"MultiHeadAttention",
|
|
13
|
+
"CausalAttention",
|
|
14
|
+
"CrossAttention",
|
|
15
|
+
"GlobalAttention",
|
|
16
|
+
"MultiQueryAttention",
|
|
17
|
+
"MultiHeadLatentAttention",
|
|
18
|
+
"GroupedQueryAttention",
|
|
19
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
import torch.nn.functional as F
|
|
4
|
+
|
|
5
|
+
class CausalAttention(nn.Module):
|
|
6
|
+
def __init__(self, embed_dim, num_heads, max_seq_len, dropout=0.1):
|
|
7
|
+
super().__init__()
|
|
8
|
+
assert embed_dim % num_heads == 0, "embed_dim must be divisible by num_heads"
|
|
9
|
+
self.d_k = embed_dim // num_heads
|
|
10
|
+
self.num_heads = num_heads
|
|
11
|
+
|
|
12
|
+
# Linear layers for query, key, and value
|
|
13
|
+
self.query = nn.Linear(embed_dim, embed_dim)
|
|
14
|
+
self.key = nn.Linear(embed_dim, embed_dim)
|
|
15
|
+
self.value = nn.Linear(embed_dim, embed_dim)
|
|
16
|
+
self.out = nn.Linear(embed_dim, embed_dim)
|
|
17
|
+
|
|
18
|
+
self.dropout = nn.Dropout(dropout)
|
|
19
|
+
|
|
20
|
+
# Register a buffer for the causal mask(lower triangular matrix)
|
|
21
|
+
# shape: (1, 1, max_seq_len, max_seq_len)
|
|
22
|
+
self.register_buffer("mask", torch.tril(torch.ones(max_seq_len, max_seq_len)).view(1, 1, max_seq_len, max_seq_len))
|
|
23
|
+
|
|
24
|
+
def forward(self, x):
|
|
25
|
+
batch_size, seq_len, embed_dim = x.size()
|
|
26
|
+
|
|
27
|
+
# Project inputs to query, key, and value
|
|
28
|
+
Q = self.query(x) # (batch_size, seq_len, embed_dim)
|
|
29
|
+
K = self.key(x) # (batch_size, seq_len, embed_dim)
|
|
30
|
+
V = self.value(x) # (batch_size, seq_len, embed_dim)
|
|
31
|
+
|
|
32
|
+
# Reshape for multi-head attention
|
|
33
|
+
Q = Q.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2) # (batch_size, num_heads, seq_len, d_k)
|
|
34
|
+
K = K.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2) # (batch_size, num_heads, seq_len, d_k)
|
|
35
|
+
V = V.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2) # (batch_size, num_heads, seq_len, d_k)
|
|
36
|
+
|
|
37
|
+
# Scaled dot-product attention
|
|
38
|
+
scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.d_k ** 0.5) # (batch_size, num_heads, seq_len, seq_len)
|
|
39
|
+
|
|
40
|
+
# Apply causal mask
|
|
41
|
+
scores = scores.masked_fill(self.mask[:, :, :seq_len, :seq_len] == 0, float('-inf'))
|
|
42
|
+
|
|
43
|
+
# Softmax to get attention weights
|
|
44
|
+
attn_weights = F.softmax(scores, dim=-1) # (batch_size, num_heads, seq_len, seq_len)
|
|
45
|
+
attn_weights = self.dropout(attn_weights)
|
|
46
|
+
|
|
47
|
+
# Compute the output
|
|
48
|
+
output = torch.matmul(attn_weights, V) # (batch_size, num_heads, seq_len, d_k)
|
|
49
|
+
|
|
50
|
+
# Concatenate heads and project to output dimension
|
|
51
|
+
output = output.transpose(1, 2).contiguous().view(batch_size, seq_len, embed_dim) # (batch_size, seq_len, embed_dim)
|
|
52
|
+
output = self.out(output) # (batch_size, seq_len, embed_dim)
|
|
53
|
+
|
|
54
|
+
return output
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
"""The Causal Attention mechanism is a variant of multi-head attention that restricts a model
|
|
58
|
+
from attending to future tokens, ensuring strict autoregressive generation. This is implemented by applying a
|
|
59
|
+
lower-triangular mask to the attention scores before the softmax function, effectively setting future positions to negative
|
|
60
|
+
infinity."""
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
|
|
4
|
+
class CrossAttention(nn.Module):
|
|
5
|
+
def __init__(self, embed_dim, num_heads):
|
|
6
|
+
super().__init__()
|
|
7
|
+
self.self_attn = nn.MultiheadAttention(embed_dim, num_heads, batch_first=True)
|
|
8
|
+
self.cross_attn = nn.MultiheadAttention(embed_dim, num_heads, batch_first=True)
|
|
9
|
+
self.norm1 = nn.LayerNorm(embed_dim)
|
|
10
|
+
self.norm2 = nn.LayerNorm(embed_dim)
|
|
11
|
+
|
|
12
|
+
def forward(self, target, source, tgt_mask=None, src_mask=None):
|
|
13
|
+
# Self-attention on the target sequence
|
|
14
|
+
attn_output, _ = self.self_attn(target, target, target, attn_mask=tgt_mask)
|
|
15
|
+
target = self.norm1(target + attn_output)
|
|
16
|
+
|
|
17
|
+
# Cross-attention with the souce sequence
|
|
18
|
+
cross_output, _ = self.cross_attn(target, source, source, attn_mask=src_mask)
|
|
19
|
+
output = self.norm2(target + cross_output)
|
|
20
|
+
|
|
21
|
+
return output
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
"""Cross-attention
|
|
26
|
+
|
|
27
|
+
Allows tokens from one sequence attend to a different sequence, bringing information from different sources or
|
|
28
|
+
modalities together (for example, description and image). It uses queries from one module – usually encoder,
|
|
29
|
+
and keys and values from the other one (decoder)."""
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
import torch.nn.functional as F
|
|
4
|
+
|
|
5
|
+
class GlobalAttention(nn.Module):
|
|
6
|
+
def __init__(self, encoder_dim, decoder_dim):
|
|
7
|
+
super(GlobalAttention, self).__init__()
|
|
8
|
+
# Linear layers to project encoder and decoder states
|
|
9
|
+
self.encoder_linear = nn.Linear(encoder_dim, decoder_dim)
|
|
10
|
+
self.decoder_linear = nn.Linear(decoder_dim, decoder_dim)
|
|
11
|
+
self.attn_linear = nn.Linear(decoder_dim, 1)
|
|
12
|
+
|
|
13
|
+
def forward(self, encoder_outputs, decoder_hidden):
|
|
14
|
+
"""
|
|
15
|
+
encoder_outputs: (batch_size, seq_len, encoder_dim)
|
|
16
|
+
decoder_hidden: (batch_size, decoder_dim)
|
|
17
|
+
"""
|
|
18
|
+
# Project encoder outputs
|
|
19
|
+
# energy: (batch_size, seq_len, decoder_dim)
|
|
20
|
+
energy = self.encoder_linear(encoder_outputs)
|
|
21
|
+
|
|
22
|
+
# Project decoder hidden state and expand to match sequence length
|
|
23
|
+
# decoder_hidden_expanded: (batch_size, 1, decoder_dim) -> (batch_size, seq_len, decoder_dim)
|
|
24
|
+
decoder_hidden_expanded = self.decoder_linear(decoder_hidden).unsqueeze(1)
|
|
25
|
+
|
|
26
|
+
# Compute alignment scores
|
|
27
|
+
# attn_energies: (batch_size, seq_len, 1)
|
|
28
|
+
attn_energies = self.attn_linear(torch.tanh(energy + decoder_hidden_expanded))
|
|
29
|
+
|
|
30
|
+
# Softmax to get attention weights
|
|
31
|
+
# attn_weights: (batch_size, seq_len, 1)
|
|
32
|
+
attn_weights = F.softmax(attn_energies, dim=1)
|
|
33
|
+
|
|
34
|
+
# Compute context vector as weighted sum of encoder outputs
|
|
35
|
+
# context: (batch_size, encoder_dim)
|
|
36
|
+
context = torch.bmm(attn_weights.transpose(1, 2), encoder_outputs).squeeze(1)
|
|
37
|
+
|
|
38
|
+
return context, attn_weights
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
"""Global attention
|
|
42
|
+
|
|
43
|
+
It is also a type of sparse attention that allows some tokens to attend to every token in a sequence,
|
|
44
|
+
while others have limited attention. Every token can also attend to these special tokens.
|
|
45
|
+
It is often used with local attention to preserve the entire context handling. """
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
import math
|
|
4
|
+
|
|
5
|
+
class GroupedQueryAttention(nn.Module):
|
|
6
|
+
def __init__(self, d_model, num_query_heads, num_kv_heads):
|
|
7
|
+
super().__init__()
|
|
8
|
+
assert d_model % num_query_heads == 0, "d_model must be divisible by num_query_heads"
|
|
9
|
+
assert num_query_heads % num_kv_heads == 0, "num_query_heads must be divisible by num_kv_heads"
|
|
10
|
+
|
|
11
|
+
self.d_model = d_model
|
|
12
|
+
self.num_query_heads = num_query_heads
|
|
13
|
+
self.num_kv_heads = num_kv_heads
|
|
14
|
+
self.num_queries_per_kv = num_query_heads // num_kv_heads
|
|
15
|
+
self.head_dim = d_model // num_query_heads
|
|
16
|
+
|
|
17
|
+
# Query projection: standard size
|
|
18
|
+
self.q_proj = nn.Linear(d_model, d_model)
|
|
19
|
+
|
|
20
|
+
# Key/Value projections: reduced size (num_kv_heads * head_dim)
|
|
21
|
+
self.kv_dim = num_kv_heads * self.head_dim
|
|
22
|
+
self.k_proj = nn.Linear(d_model, self.kv_dim)
|
|
23
|
+
self.v_proj = nn.Linear(d_model, self.kv_dim)
|
|
24
|
+
|
|
25
|
+
# Output projection
|
|
26
|
+
self.out_proj = nn.Linear(d_model, d_model)
|
|
27
|
+
|
|
28
|
+
# Scaling factor
|
|
29
|
+
self.scale = math.sqrt(self.head_dim)
|
|
30
|
+
|
|
31
|
+
def forward(self, x, mask=None):
|
|
32
|
+
batch_size, seq_len, _ = x.shape
|
|
33
|
+
|
|
34
|
+
# Projections
|
|
35
|
+
Q = self.q_proj(x) # (batch, seq, d_model)
|
|
36
|
+
K = self.k_proj(x) # (batch, seq, kv_dim)
|
|
37
|
+
V = self.v_proj(x) # (batch, seq, kv_dim)
|
|
38
|
+
|
|
39
|
+
# Reshape for multi-head attention
|
|
40
|
+
# Q: (batch, num_query_heads, seq, head_dim)
|
|
41
|
+
Q = Q.view(batch_size, seq_len, self.num_query_heads, self.head_dim).transpose(1, 2)
|
|
42
|
+
|
|
43
|
+
# K/V: (batch, num_kv_heads, seq, head_dim)
|
|
44
|
+
K = K.view(batch_size, seq_len, self.num_kv_heads, self.head_dim).transpose(1, 2)
|
|
45
|
+
V = V.view(batch_size, seq_len, self.num_kv_heads, self.head_dim).transpose(1, 2)
|
|
46
|
+
|
|
47
|
+
# Expand K and V to match number of query heads
|
|
48
|
+
# Each KV head is shared by num_queries_per_kv query heads
|
|
49
|
+
K = K.repeat_interleave(self.num_queries_per_kv, dim=1)
|
|
50
|
+
V = V.repeat_interleave(self.num_queries_per_kv, dim=1)
|
|
51
|
+
|
|
52
|
+
# Attention scores
|
|
53
|
+
scores = torch.matmul(Q, K.transpose(-2, -1)) / self.scale
|
|
54
|
+
|
|
55
|
+
if mask is not None:
|
|
56
|
+
scores = scores.masked_fill(mask == 0, float('-inf'))
|
|
57
|
+
|
|
58
|
+
attn_weights = torch.softmax(scores, dim=-1)
|
|
59
|
+
attn_output = torch.matmul(attn_weights, V)
|
|
60
|
+
|
|
61
|
+
# Concatenate heads
|
|
62
|
+
attn_output = attn_output.transpose(1, 2).contiguous()
|
|
63
|
+
attn_output = attn_output.view(batch_size, seq_len, self.d_model)
|
|
64
|
+
|
|
65
|
+
return self.out_proj(attn_output)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from linear_attention_transformer import LinearAttentionTransformerLM
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def linear_attention_example(num_tokens=20000, dim=512, heads=8, max_seq_len=8192, device=None):
|
|
6
|
+
if device is None:
|
|
7
|
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
8
|
+
|
|
9
|
+
model = LinearAttentionTransformerLM(
|
|
10
|
+
num_tokens=num_tokens,
|
|
11
|
+
dim=dim,
|
|
12
|
+
heads=heads,
|
|
13
|
+
depth=1,
|
|
14
|
+
max_seq_len=max_seq_len,
|
|
15
|
+
causal=True,
|
|
16
|
+
).to(device)
|
|
17
|
+
|
|
18
|
+
x = torch.randint(0, num_tokens, (1, max_seq_len), device=device)
|
|
19
|
+
output = model(x)
|
|
20
|
+
return output
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if __name__ == "__main__":
|
|
24
|
+
linear_attention_example()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
"""$ pip install linear-attention-transformer
|
|
28
|
+
|
|
29
|
+
Linear attention is a faster version of self-attention that avoids comparing
|
|
30
|
+
all token pairs. By restructuring the computation with kernel functions
|
|
31
|
+
(kernel functions are a way to compute similarity between vectors as if they were
|
|
32
|
+
transformed into another space, without actually performing that transformation),
|
|
33
|
+
it reduces complexity from O(N2) to O(N), making it much more efficient for long sequences."""
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
import torch.nn.functional as F
|
|
4
|
+
|
|
5
|
+
class MultiHeadAttention(nn.Module):
|
|
6
|
+
def __init__(self, embed_dim, num_heads):
|
|
7
|
+
super(MultiHeadAttention, self).__init__()
|
|
8
|
+
assert embed_dim % num_heads == 0, "Embedding dimension must be divisible by number of heads"
|
|
9
|
+
self.embed_dim = embed_dim
|
|
10
|
+
self.num_heads = num_heads
|
|
11
|
+
self.head_dim = embed_dim // num_heads
|
|
12
|
+
|
|
13
|
+
# Linear Layers for query, key, and value
|
|
14
|
+
self.query_linear = nn.Linear(embed_dim, embed_dim)
|
|
15
|
+
self.key_linear = nn.Linear(embed_dim, embed_dim)
|
|
16
|
+
self.value_linear = nn.Linear(embed_dim, embed_dim)
|
|
17
|
+
|
|
18
|
+
# output projection Layer
|
|
19
|
+
self.out_linear = nn.Linear(embed_dim, embed_dim)
|
|
20
|
+
|
|
21
|
+
def forward(self, query, key, value, mask=None):
|
|
22
|
+
batch_size = query.size(0)
|
|
23
|
+
|
|
24
|
+
# project and reshape to (batch_size, num_heads, seq_length, head_dim)
|
|
25
|
+
query = self.query_linear(query).view(batch_size, -1, self.num_heads, self.head_dim).transpose(1, 2)
|
|
26
|
+
key = self.key_linear(key).view(batch_size, -1, self.num_heads, self.head_dim).transpose(1, 2)
|
|
27
|
+
value = self.value_linear(value).view(batch_size, -1, self.num_heads, self.head_dim).transpose(1, 2)
|
|
28
|
+
|
|
29
|
+
# Scaled Dot-Product Attention
|
|
30
|
+
scores = torch.matmul(query, key.transpose(-2, -1)) / (self.head_dim ** 0.5)
|
|
31
|
+
if mask is not None:
|
|
32
|
+
scores = scores.masked_fill(mask == 0, float('-inf'))
|
|
33
|
+
attn_weights = F.softmax(scores, dim=-1)
|
|
34
|
+
attn_output = torch.matmul(attn_weights, value)
|
|
35
|
+
|
|
36
|
+
# Concatenate heads and project
|
|
37
|
+
attn_output = attn_output.transpose(1, 2).contiguous().view(batch_size, -1, self.num_heads * self.head_dim)
|
|
38
|
+
return self.out_linear(attn_output)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
"""Multi-Head Attention (MHA)
|
|
44
|
+
Splits attention into multiple heads (one independent attention mechanism), so the model can learn different interaction patterns
|
|
45
|
+
in parallel. It became the defining feature of Transformers and LLMs, and it still remains the baseline for novel attention
|
|
46
|
+
variants"""
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
import torch.nn.functional as F
|
|
4
|
+
|
|
5
|
+
class MultiHeadLatentAttention(nn.Module):
|
|
6
|
+
def __init__(self, d_model, num_heads, q_latent_dim, kv_latent_dim):
|
|
7
|
+
super().__init__()
|
|
8
|
+
self.num_heads = num_heads
|
|
9
|
+
self.head_dim = d_model // num_heads
|
|
10
|
+
|
|
11
|
+
# Query compression and decomposition
|
|
12
|
+
self.w_q_d = nn.Linear(d_model, q_latent_dim, bias=False)
|
|
13
|
+
self.w_qk = nn.Linear(q_latent_dim, num_heads * kv_latent_dim, bias=False)
|
|
14
|
+
|
|
15
|
+
# Key/Value compression and decomposition
|
|
16
|
+
self.w_kv_d = nn.Linear(d_model, kv_latent_dim, bias=False)
|
|
17
|
+
self.w_v_u = nn.Linear(kv_latent_dim, num_heads * self.head_dim, bias=False)
|
|
18
|
+
|
|
19
|
+
# Output projection
|
|
20
|
+
self.w_o = nn.Linear(num_heads * self.head_dim, d_model)
|
|
21
|
+
|
|
22
|
+
def forward(self, x):
|
|
23
|
+
batch_size, seq_len, d_model = x.shape
|
|
24
|
+
|
|
25
|
+
# 1. Project to latent space
|
|
26
|
+
c_q = self.w_q_d(x) # [B, S, q_latent_dim]
|
|
27
|
+
c_kv = self.w_kv_d(x) # [B, S, kv_latent_dim]
|
|
28
|
+
|
|
29
|
+
# 2. Compute attention scores via latent multiplication
|
|
30
|
+
# Reshape for multi-head: [B, S, num_heads, kv_latent_dim]
|
|
31
|
+
c_q_w_qk = self.w_qk(c_q).view(batch_size, seq_len, self.num_heads, -1)
|
|
32
|
+
|
|
33
|
+
# Scores: [B, num_heads, S, S]
|
|
34
|
+
scores = torch.matmul(
|
|
35
|
+
c_q_w_qk.transpose(1, 2),
|
|
36
|
+
c_kv.transpose(-2, -1)[:, None, ...]
|
|
37
|
+
) / (self.head_dim ** 0.5)
|
|
38
|
+
|
|
39
|
+
attn_weights = torch.softmax(scores, dim=-1)
|
|
40
|
+
|
|
41
|
+
# 3. Restore Values and compute output
|
|
42
|
+
v = self.w_v_u(c_kv).view(batch_size, seq_len, self.num_heads, -1)
|
|
43
|
+
|
|
44
|
+
# [B, num_heads, S, head_dim]
|
|
45
|
+
output = torch.matmul(attn_weights, v.transpose(1, 2)).transpose(1, 2).contiguous()
|
|
46
|
+
|
|
47
|
+
# 4. Final projection
|
|
48
|
+
return self.w_o(output.view(batch_size, seq_len, -1))
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
import math
|
|
4
|
+
|
|
5
|
+
class MultiQueryAttention(nn.Module):
|
|
6
|
+
def __init__(self, embedding_dim, num_heads):
|
|
7
|
+
super().__init__()
|
|
8
|
+
assert embedding_dim % num_heads == 0, "embedding_dim must be divisible by num_heads"
|
|
9
|
+
|
|
10
|
+
self.embedding_dim = embedding_dim
|
|
11
|
+
self.num_heads = num_heads
|
|
12
|
+
self.head_dim = embedding_dim // num_heads
|
|
13
|
+
|
|
14
|
+
# Each head has its own Query projection
|
|
15
|
+
self.W_q = nn.Linear(embedding_dim, embedding_dim, bias=False)
|
|
16
|
+
|
|
17
|
+
# Single shared Key and Value projections for all heads
|
|
18
|
+
self.W_k = nn.Linear(embedding_dim, self.head_dim, bias=False)
|
|
19
|
+
self.W_v = nn.Linear(embedding_dim, self.head_dim, bias=False)
|
|
20
|
+
|
|
21
|
+
# Final output projection
|
|
22
|
+
self.W_o = nn.Linear(embedding_dim, embedding_dim, bias=False)
|
|
23
|
+
|
|
24
|
+
def forward(self, x):
|
|
25
|
+
batch_size, seq_len, _ = x.size()
|
|
26
|
+
|
|
27
|
+
# Compute Q, K, V
|
|
28
|
+
Q = self.W_q(x) # [B, S, E]
|
|
29
|
+
K = self.W_k(x) # [B, S, H]
|
|
30
|
+
V = self.W_v(x) # [B, S, H]
|
|
31
|
+
|
|
32
|
+
# Split Q into multiple heads: [B, S, E] -> [B, H, S, H]
|
|
33
|
+
Q = Q.view(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2)
|
|
34
|
+
|
|
35
|
+
# Broadcast single K and V across all heads: [B, S, H] -> [B, 1, S, H]
|
|
36
|
+
K = K.unsqueeze(1)
|
|
37
|
+
V = V.unsqueeze(1)
|
|
38
|
+
|
|
39
|
+
# Scaled Dot-Product Attention
|
|
40
|
+
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.head_dim)
|
|
41
|
+
attn_weights = torch.softmax(scores, dim=-1)
|
|
42
|
+
|
|
43
|
+
# Apply attention weights to V
|
|
44
|
+
context = torch.matmul(attn_weights, V) # [B, H, S, H]
|
|
45
|
+
|
|
46
|
+
# Merge heads: [B, H, S, H] -> [B, S, E]
|
|
47
|
+
context = context.transpose(1, 2).contiguous().view(batch_size, seq_len, self.embedding_dim)
|
|
48
|
+
|
|
49
|
+
return self.W_o(context)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
import torch.nn.functional as F
|
|
4
|
+
|
|
5
|
+
class SelfAttention(nn.Module):
|
|
6
|
+
def __init__(self, input_dim):
|
|
7
|
+
super().__init__()
|
|
8
|
+
self.input_dim = input_dim
|
|
9
|
+
self.query = nn.Linear(input_dim, input_dim)
|
|
10
|
+
self.key = nn.Linear(input_dim, input_dim)
|
|
11
|
+
self.value = nn.Linear(input_dim, input_dim)
|
|
12
|
+
self.softmax = nn.Softmax(dim=-1)
|
|
13
|
+
|
|
14
|
+
def forward(self, x, mask=None):
|
|
15
|
+
# x shape: (batch_size, seq_len, input_dim)
|
|
16
|
+
Q = self.query(x)
|
|
17
|
+
K = self.key(x)
|
|
18
|
+
V = self.value(x)
|
|
19
|
+
|
|
20
|
+
# Scaled dot-product attention
|
|
21
|
+
scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.input_dim ** 0.5)
|
|
22
|
+
|
|
23
|
+
if mask is not None:
|
|
24
|
+
scores = scores.masked_fill(mask == 0, float('-inf'))
|
|
25
|
+
|
|
26
|
+
attention_weights = self.softmax(scores)
|
|
27
|
+
output = torch.matmul(attention_weights, V)
|
|
28
|
+
return output, attention_weights
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
"""Self-attention:
|
|
33
|
+
Let's each token attend directly to other tokens in same sequence: the model compares every token with
|
|
34
|
+
the rest of the sequence and build a context-aware representation. This is what allows transformers to
|
|
35
|
+
capture relationships accross distanceand make it possible to modellong-range dependencies without recurrence."""
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: transformer-lib
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Attention mechanisms from Every_Thing_About_Transformer, as an importable library
|
|
5
|
+
Author: Aniket Verma
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Requires-Dist: torch>=2.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
11
|
+
Provides-Extra: linear
|
|
12
|
+
Requires-Dist: linear-attention-transformer; extra == 'linear'
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# transformer-lib
|
|
16
|
+
|
|
17
|
+
Attention mechanisms from Every_Thing_About_Transformer, as an importable Python library.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install -e .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from transformer_lib import MultiHeadAttention
|
|
29
|
+
import torch
|
|
30
|
+
|
|
31
|
+
mha = MultiHeadAttention(embed_dim=512, num_heads=8)
|
|
32
|
+
x = torch.randn(2, 10, 512)
|
|
33
|
+
out = mha(x, x, x)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Available Modules
|
|
37
|
+
|
|
38
|
+
| Module | Description |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `SelfAttention` | Basic scaled dot-product self-attention |
|
|
41
|
+
| `MultiHeadAttention` | Multi-head attention with separate Q/K/V projections |
|
|
42
|
+
| `CausalAttention` | Multi-head with lower-triangular causal mask |
|
|
43
|
+
| `CrossAttention` | Encoder-decoder cross-attention with self-attention pre-step |
|
|
44
|
+
| `GlobalAttention` | Additive (Bahdanau-style) alignment scoring |
|
|
45
|
+
| `MultiQueryAttention` | Shared K/V across all heads |
|
|
46
|
+
| `GroupedQueryAttention` | Groups of query heads sharing K/V (GQA) |
|
|
47
|
+
| `MultiHeadLatentAttention` | Latent-space compression for keys/values |
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
transformer_lib/__init__.py,sha256=Vmi_DhF_WaVhY5t_asoFnlnqorHuRBByyAFB0mf5joA,432
|
|
2
|
+
transformer_lib/attention/__init__.py,sha256=yy4a__dDEIIPujUYqThAy1rCtjqhFk9NE6yvkOvdsyI,621
|
|
3
|
+
transformer_lib/attention/causal_attention.py,sha256=gvmeQzXSIY4TR2OOin11YLn6citaZvs0AHwcZzLAgCU,2919
|
|
4
|
+
transformer_lib/attention/cross_attention.py,sha256=1GG9i3DOFe6T-fTqO4DmS6TIQlRIsHFIpZl-_laV6p0,1202
|
|
5
|
+
transformer_lib/attention/global_attention.py,sha256=9cP1ocn5cscJuf97N6VyczlNkJC_JPGUrXosQ-wLvhk,1990
|
|
6
|
+
transformer_lib/attention/grouped_q_attention.py,sha256=cVoAaMANWWkzA3uSprdiqObqKT0DfcIek1TZw0mKuxk,2711
|
|
7
|
+
transformer_lib/attention/linear_attention.py,sha256=qzR2LTAwZyH5q1I6NXGCQ8u-yyVCDnQMPi3DNFB-r1Q,1118
|
|
8
|
+
transformer_lib/attention/multi_head_attention.py,sha256=2zPMicAyx5quZwWjOBc1k1QassZJ-FrRRBLUqouZtDk,2110
|
|
9
|
+
transformer_lib/attention/multi_head_latent_attention.py,sha256=b1eprcBOZxRGSzzmKT7gopNg42cR2mMj8avikYD8vco,1939
|
|
10
|
+
transformer_lib/attention/multi_q_attention.py,sha256=JX0RRSs2k70mU58HuzOj4ZKo10jSKQPmUeB40Q-w0lI,1916
|
|
11
|
+
transformer_lib/attention/self_attention.py,sha256=GtYgVj2IeLH2i5iHHGtXpEZCLrOQE7NosOK0qnAft-U,1298
|
|
12
|
+
transformer_lib-0.1.0.dist-info/METADATA,sha256=tvxcag6WMqhPLk8eu62c-SLlzumuQXh-ZSZGk_S_3KI,1386
|
|
13
|
+
transformer_lib-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
14
|
+
transformer_lib-0.1.0.dist-info/RECORD,,
|