Skip to content
AILinkDeepTech
Go back
Deep Learning Medium

LLaMA Transformer Implementation in PyTorch

Abstract

A from-scratch PyTorch implementation of the LLaMA-style Transformer: RMSNorm, Rotary Position Embeddings (RoPE), GQA-style multi-head attention with causal mask, SwiGLU MLP, and a stack of pre-norm decoder layers.

LLaMA Transformer Implementation in PyTorch

This implementation builds the LLaMA-style Transformer from scratch. It includes RMSNorm, Rotary Position Embeddings (RoPE), GQA-style multi-head attention with masking, SwiGLU MLP, and a stack of pre-norm decoder layers.

import torch
import torch.nn as nn
import torch.nn.functional as F
import math

class RMSNorm(nn.Module):
    def __init__(self, dim: int, eps: float = 1e-6):
        super().__init__()
        self.eps = eps
        self.weight = nn.Parameter(torch.ones(dim))

    def forward(self, x):
        rms = torch.sqrt(torch.mean(x ** 2, dim=-1, keepdim=True) + self.eps)
        # Normalize and scale
        x_norm = x / rms * self.weight
        return x_norm

class RotaryEmbedding(nn.Module):
    def __init__(self, dim, max_position_embeddings=2048):
        super().__init__()
        inv_freq = 1.0 / (10000 ** (torch.arange(0, dim, 2).float() / dim))
        self.register_buffer("inv_freq", inv_freq)
        self.max_seq_len_cached = max_position_embeddings

        # Initialize cache
        t = torch.arange(self.max_seq_len_cached, device=inv_freq.device).type_as(self.inv_freq)
        freqs = torch.einsum("i,j->ij", t, self.inv_freq)
        emb = torch.cat((freqs, freqs), dim=-1)
        self.register_buffer("cos_cached", emb.cos()[None, None, :, :])
        self.register_buffer("sin_cached", emb.sin()[None, None, :, :])

    def forward(self, x, seq_len=None):
        if seq_len > self.max_seq_len_cached:
            # Extend cache if needed
            self.max_seq_len_cached = seq_len
            t = torch.arange(seq_len, device=x.device).type_as(self.inv_freq)
            freqs = torch.einsum("i,j->ij", t, self.inv_freq)
            emb = torch.cat((freqs, freqs), dim=-1)
            self.cos_cached = emb.cos()[None, None, :, :]
            self.sin_cached = emb.sin()[None, None, :, :]
        
        return (
            self.cos_cached[:, :, :seq_len, ...],
            self.sin_cached[:, :, :seq_len, ...]
        )

class LlamaAttention(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.hidden_size = config['hidden_size']
        self.num_heads = config['num_attention_heads']
        self.head_dim = self.hidden_size // self.num_heads
        
        self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
        self.k_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
        self.v_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
        self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
        
        self.rotary_emb = RotaryEmbedding(self.head_dim)

    def forward(self, hidden_states, attention_mask=None):
        batch_size, seq_length, _ = hidden_states.size()
        
        q = self.q_proj(hidden_states)
        k = self.k_proj(hidden_states)
        v = self.v_proj(hidden_states)
        
        # Reshape for multi-head attention
        q = q.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)
        k = k.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)
        v = v.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)
        
        # Apply rotary embeddings
        cos, sin = self.rotary_emb(q, seq_length)
        q, k = self.apply_rotary_pos_emb(q, k, cos, sin)
        
        scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim)
        
        if attention_mask is not None:
            attention_mask = attention_mask.unsqueeze(1).unsqueeze(2)
            attention_mask = (1.0 - attention_mask) * torch.finfo(scores.dtype).min
            scores = scores + attention_mask
        
        attn_weights = F.softmax(scores, dim=-1)

        # output
        attn_output = torch.matmul(attn_weights, v)
        attn_output = attn_output.transpose(1, 2).contiguous()
        attn_output = attn_output.view(batch_size, seq_length, self.hidden_size)
        
        return self.o_proj(attn_output)

    @staticmethod
    def apply_rotary_pos_emb(q, k, cos, sin):
        q_embed = (q * cos) + (q.roll(shifts=1, dims=-1) * sin)
        k_embed = (k * cos) + (k.roll(shifts=1, dims=-1) * sin)
        return q_embed, k_embed

class LlamaMLP(nn.Module):
    def __init__(self, config):
        super().__init__()
        hidden_size = config['hidden_size']
        intermediate_size = config['intermediate_size']
        
        self.gate_proj = nn.Linear(hidden_size, intermediate_size, bias=False)
        self.up_proj = nn.Linear(hidden_size, intermediate_size, bias=False)
        self.down_proj = nn.Linear(intermediate_size, hidden_size, bias=False)

    def forward(self, x):
        # SwiGLU activation
        gate = F.silu(self.gate_proj(x))
        up = self.up_proj(x)
        return self.down_proj(gate * up)

class LlamaDecoderLayer(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.self_attn = LlamaAttention(config)
        self.mlp = LlamaMLP(config)
        self.input_norm = RMSNorm(config['hidden_size'])
        self.post_attention_norm = RMSNorm(config['hidden_size'])

    def forward(self, hidden_states, attention_mask=None):
        # Pre-norm
        norm_states = self.input_norm(hidden_states)
        attn_output = self.self_attn(norm_states, attention_mask)
        hidden_states = hidden_states + attn_output
        
        # MLP with pre-norm
        norm_states = self.post_attention_norm(hidden_states)
        mlp_output = self.mlp(norm_states)
        hidden_states = hidden_states + mlp_output
        
        return hidden_states

class LlamaModel(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.config = config
        
        self.embed_tokens = nn.Embedding(config['vocab_size'], config['hidden_size'])
        self.layers = nn.ModuleList([
            LlamaDecoderLayer(config) for _ in range(config['num_hidden_layers'])
        ])
        self.norm = RMSNorm(config['hidden_size'])

        # Initialize weights
        self.apply(self._init_weights)

    def _init_weights(self, module):
        if isinstance(module, nn.Linear):
            torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
        elif isinstance(module, nn.Embedding):
            torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)

    def forward(self, input_ids, attention_mask=None):
        hidden_states = self.embed_tokens(input_ids)
        
        for layer in self.layers:
            hidden_states = layer(hidden_states, attention_mask)
        
        hidden_states = self.norm(hidden_states)
        return hidden_states

def test_llama_model():
    config = {
        'vocab_size': 32000,
        'hidden_size': 512,
        'num_attention_heads': 8,
        'num_hidden_layers': 4,
        'intermediate_size': 1024
    }

    model = LlamaModel(config)
    
    batch_size = 2
    seq_length = 16
    input_ids = torch.randint(0, config['vocab_size'], (batch_size, seq_length))
    
    attention_mask = torch.ones(batch_size, seq_length)
    
    output = model(input_ids, attention_mask)

    # Assertions for output shape
    assert output.shape == (batch_size, seq_length, config['hidden_size']), \
        f"Expected output shape {(batch_size, seq_length, config['hidden_size'])}, got {output.shape}"
    
    # Test attention mechanism
    attention_layer = LlamaAttention(config)
    hidden_states = torch.randn(batch_size, seq_length, config['hidden_size'])
    attn_output = attention_layer(hidden_states, attention_mask)
    
    assert attn_output.shape == hidden_states.shape, \
        f"Expected attention output shape {hidden_states.shape}, got {attn_output.shape}"
    
    print("All tests passed!")

if __name__ == "__main__":
    test_llama_model()


Cite this Explanation

@article{ailinkdeeptech2025llamaalgo,
  title={LLaMA Transformer Implementation in PyTorch},
  author={AILinkDeepTech},
  journal={AILinkDeepTech Algorithm Explanations},
  year={2025},
  url={https://ailinkdeeptech.com/research/llama_algo}
}

Related Explanations