import math
import torch
import torch.nn as nnSelf Attention with a causal mask
When we are decoding autoregressively, we obviously don’t know what tokens are coming next. So when we are training a model, we need to mask out future tokens.
inputs = torch.tensor(
[
[0.43, 0.15, 0.89], # Your (x^1)
[0.55, 0.87, 0.66], # journey (x^2)
[0.57, 0.85, 0.64], # starts (x^3)
[0.22, 0.58, 0.33], # with (x^4)
[0.77, 0.25, 0.10], # one (x^5)
[0.05, 0.80, 0.55], # step (x^6)
]
)class SelfAttention(nn.Module):
def __init__(self, d_in, d_out, qkv_bias=False):
super().__init__()
self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_key = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_value = nn.Linear(d_in, d_out, qkv_bias)
def forward(self, x):
queries = x @ self.W_query
keys = x @ self.W_key
values = x @ self.W_value
attn_scores = queries @ keys.T
attn_weights = torch.softmax(attn_scores / keys.shape[-1] ** 0.5, dim=-1)
context_vec = attn_weights @ values
return context_vec
torch.manual_seed(0)
self_attention = SelfAttention(d_in=inputs.shape[1], d_out=2)queries = self_attention.W_query(inputs)
keys = self_attention.W_key(inputs)
attn_scores = queries @ keys.T
attn_weights = torch.softmax(attn_scores / keys.shape[-1] ** 0.5, dim=-1)
print(attn_weights)tensor([[0.1762, 0.1616, 0.1619, 0.1662, 0.1712, 0.1630],
[0.1665, 0.1678, 0.1675, 0.1669, 0.1614, 0.1699],
[0.1664, 0.1679, 0.1676, 0.1670, 0.1612, 0.1700],
[0.1654, 0.1679, 0.1677, 0.1669, 0.1631, 0.1689],
[0.1645, 0.1691, 0.1686, 0.1671, 0.1595, 0.1713],
[0.1666, 0.1671, 0.1670, 0.1668, 0.1648, 0.1678]],
grad_fn=<SoftmaxBackward0>)
context_length = attn_scores.shape[0]
mask_simple = torch.tril(torch.ones(context_length, context_length))mask_simpletensor([[1., 0., 0., 0., 0., 0.],
[1., 1., 0., 0., 0., 0.],
[1., 1., 1., 0., 0., 0.],
[1., 1., 1., 1., 0., 0.],
[1., 1., 1., 1., 1., 0.],
[1., 1., 1., 1., 1., 1.]])
masked_simple = attn_weights * mask_simplemasked_simpletensor([[0.1762, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
[0.1665, 0.1678, 0.0000, 0.0000, 0.0000, 0.0000],
[0.1664, 0.1679, 0.1676, 0.0000, 0.0000, 0.0000],
[0.1654, 0.1679, 0.1677, 0.1669, 0.0000, 0.0000],
[0.1645, 0.1691, 0.1686, 0.1671, 0.1595, 0.0000],
[0.1666, 0.1671, 0.1670, 0.1668, 0.1648, 0.1678]],
grad_fn=<MulBackward0>)
mask = torch.triu(torch.ones(context_length, context_length), diagonal=1)
masked = attn_scores.masked_fill(mask.bool(), -torch.inf)maskedtensor([[-0.0021, -inf, -inf, -inf, -inf, -inf],
[ 0.0191, 0.0297, -inf, -inf, -inf, -inf],
[ 0.0196, 0.0319, 0.0293, -inf, -inf, -inf],
[ 0.0109, 0.0323, 0.0306, 0.0234, -inf, -inf],
[ 0.0231, 0.0619, 0.0585, 0.0451, -0.0203, -inf],
[ 0.0068, 0.0113, 0.0104, 0.0086, -0.0085, 0.0175]],
grad_fn=<MaskedFillBackward0>)
attn_weights = torch.softmax(masked / keys.shape[-1] ** 0.5, dim=1)attn_weightstensor([[1.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
[0.4981, 0.5019, 0.0000, 0.0000, 0.0000, 0.0000],
[0.3316, 0.3345, 0.3339, 0.0000, 0.0000, 0.0000],
[0.2476, 0.2514, 0.2511, 0.2498, 0.0000, 0.0000],
[0.1985, 0.2040, 0.2035, 0.2016, 0.1925, 0.0000],
[0.1666, 0.1671, 0.1670, 0.1668, 0.1648, 0.1678]],
grad_fn=<SoftmaxBackward0>)
class CausalAttention(nn.Module):
def __init__(self, d_in, d_out, context_length, dropout, qkv_bias=False):
super().__init__()
self.d_out = d_out
self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_key = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias)
self.dropout = nn.Dropout(dropout)
self.register_buffer("mask", torch.triu(torch.ones(context_length, context_length), diagonal=1))
def forward(self, x):
b, num_tokens, d_in = x.shape
keys = self.W_key(x)
queries = self.W_query(x)
values = self.W_value(x)
attn_scores = queries @ keys.transpose(1, 2)
attn_scores.masked_fill_(self.mask.bool()[:num_tokens, :num_tokens], -torch.inf)
attn_weights = torch.softmax(attn_scores / keys.shape[-1] ** 0.5, dim=-1)
attn_weights = self.dropout(attn_weights)
context_vec = attn_weights @ values
return context_vecbatch = torch.stack((inputs, inputs), dim=0)batchtensor([[[0.4300, 0.1500, 0.8900],
[0.5500, 0.8700, 0.6600],
[0.5700, 0.8500, 0.6400],
[0.2200, 0.5800, 0.3300],
[0.7700, 0.2500, 0.1000],
[0.0500, 0.8000, 0.5500]],
[[0.4300, 0.1500, 0.8900],
[0.5500, 0.8700, 0.6600],
[0.5700, 0.8500, 0.6400],
[0.2200, 0.5800, 0.3300],
[0.7700, 0.2500, 0.1000],
[0.0500, 0.8000, 0.5500]]])
d_in = inputs.shape[1]
d_out = 2
ca = CausalAttention(d_in, d_out, context_length, 0.0)context_vecs = ca(batch)context_vecstensor([[[0.2154, 0.3506],
[0.1528, 0.4540],
[0.1266, 0.4926],
[0.1074, 0.4399],
[0.0555, 0.4571],
[0.0641, 0.4228]],
[[0.2154, 0.3506],
[0.1528, 0.4540],
[0.1266, 0.4926],
[0.1074, 0.4399],
[0.0555, 0.4571],
[0.0641, 0.4228]]], grad_fn=<UnsafeViewBackward0>)