import torch import torch.nn as nn from torch.nn import functional as F class LayerNorm(nn.Module): """LayerNorm but with an optional bias. PyTorch doesn't support simply bias=False""" def __init__(self, ndim): super().__init__() self.weight = nn.Parameter(torch.ones(ndim)) def forward(self, input): return F.layer_norm(input, self.weight.shape, self.weight, None, 1e-5) def create_sin_embedding( positions: torch.Tensor, dim: int, max_period: float = 10_000, dtype: torch.dtype = torch.float32 ) -> torch.Tensor: """Create sinusoidal positional embedding, with shape `[B, T, C]`""" assert dim % 2 == 0 half_dim = dim // 2 positions = positions.to(dtype) adim = torch.arange(half_dim, device=positions.device, dtype=dtype).view(1, 1, -1) max_period_tensor = torch.full([], max_period, device=positions.device, dtype=dtype) # avoid sync point phase = positions / (max_period_tensor ** (adim / (half_dim - 1))) return torch.cat([torch.cos(phase), torch.sin(phase)], dim=-1) class CausalSelfAttention(nn.Module): def __init__(self, n_embd, n_head): super().__init__() assert n_embd % n_head == 0 # main self.n_head = n_head self.n_embd = n_embd # key, query, value projections for all heads, but in a batch self.c_attn = nn.Linear(n_embd, 3 * n_embd, bias=False) # output projection self.c_proj = nn.Linear(n_embd, n_embd, bias=False) def forward(self, x): B, T, C = x.size() # b_size, sequ_len, emb_dim (n_embd) # calculate q, k, v for all heads in batch and move head forward to be the batch dim q, k, v = self.c_attn(x).split(self.n_embd, dim=2) k = k.view(B, T, self.n_head, C // self.n_head) v = v.view(B, T, self.n_head, C // self.n_head) q = q.view(B, T, self.n_head, C // self.n_head) # causal self-attention; Self-attend: (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T) k = k.transpose(1, 2) # (B, nh, T, hs) v = v.transpose(1, 2) # (B, nh, T, hs) q = q.transpose(1, 2) # (B, nh, T, hs) # with torch.backends.cuda.sdp_kernel( # enable_flash=True, enable_math=False, enable_mem_efficient=False # ): # TODO: for compile to work we have to remove this context manager y = torch.nn.functional.scaled_dot_product_attention( q, k, v, dropout_p=0, is_causal=False ) y = y.transpose(1, 2) # re-assemble head outputs side by side y = y.contiguous().view(B, T, C) # output projection y = self.c_proj(y) return y class MLP(nn.Module): def __init__(self, n_embd): super().__init__() embd_inner = 4 * n_embd self.embd_inner = embd_inner self.c_fc = nn.Linear(n_embd, self.embd_inner, bias=False) self.c_proj = nn.Linear(self.embd_inner, n_embd, bias=False) self.activation = nn.GELU() def forward(self, x): x = self.c_fc(x) x = self.activation(x) x = self.c_proj(x) return x class Block(nn.Module): def __init__(self, n_head, n_embd): super().__init__() self.ln_1 = LayerNorm(n_embd) self.attn = CausalSelfAttention(n_embd, n_head) self.ln_2 = LayerNorm(n_embd) self.mlp = MLP(n_embd) def forward(self, x): x = x.swapaxes(-1, -2) x = x + self.attn(self.ln_1(x)) x = x + self.mlp(self.ln_2(x)) x = x.swapaxes(-1, -2) return x