Implementing Transformers from Scratch in PyTorch
A deep dive into the inner workings of the Transformer architecture, complete with heavily annotated PyTorch code for every layer.
Hammad Qaiser
Understanding how Transformers work under the hood is a rite of passage for any modern AI Engineer. In this deep dive, we'll implement a complete Transformer from scratch using PyTorch.
The Core Concept: Self-Attention
At the heart of the Transformer is the self-attention mechanism. It allows the model to weigh the importance of different words in a sequence relative to each other.
Here is the mathematical formulation:
Attention(Q, K, V) = softmax(QK^T / sqrt(d_k))V
Code Implementation
Let's write the code for the MultiHeadAttention block.
import torch
import torch.nn as nn
import math
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
assert d_model % num_heads == 0, "d_model must be divisible by num_heads"
self.d_model = d_model
self.num_heads = num_heads
self.d_k = d_model // num_heads
self.W_q = nn.Linear(d_model, d_model)
self.W_k = nn.Linear(d_model, d_model)
self.W_v = nn.Linear(d_model, d_model)
self.W_o = nn.Linear(d_model, d_model)
def scaled_dot_product_attention(self, Q, K, V, mask=None):
attn_scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
if mask is not None:
attn_scores = attn_scores.masked_fill(mask == 0, -1e9)
attn_probs = torch.softmax(attn_scores, dim=-1)
output = torch.matmul(attn_probs, V)
return output
def split_heads(self, x):
batch_size, seq_length, d_model = x.size()
return x.view(batch_size, seq_length, self.num_heads, self.d_k).transpose(1, 2)
def combine_heads(self, x):
batch_size, _, seq_length, d_k = x.size()
return x.transpose(1, 2).contiguous().view(batch_size, seq_length, self.d_model)
def forward(self, Q, K, V, mask=None):
Q = self.split_heads(self.W_q(Q))
K = self.split_heads(self.W_k(K))
V = self.split_heads(self.W_v(V))
attn_output = self.scaled_dot_product_attention(Q, K, V, mask)
output = self.W_o(self.combine_heads(attn_output))
return outputPosition-wise Feed-Forward Networks
In addition to attention sub-layers, each of the layers in our encoder and decoder contains a fully connected feed-forward network.
class PositionWiseFeedForward(nn.Module):
def __init__(self, d_model, d_ff):
super(PositionWiseFeedForward, self).__init__()
self.fc1 = nn.Linear(d_model, d_ff)
self.fc2 = nn.Linear(d_ff, d_model)
self.relu = nn.ReLU()
def forward(self, x):
return self.fc2(self.relu(self.fc1(x)))Positional Encoding
Since the model contains no recurrence and no convolution, we inject some information about the relative or absolute position of the tokens in the sequence.
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_seq_length):
super(PositionalEncoding, self).__init__()
pe = torch.zeros(max_seq_length, d_model)
position = torch.arange(0, max_seq_length, dtype=torch.float).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
self.register_buffer('pe', pe.unsqueeze(0))
def forward(self, x):
return x + self.pe[:, :x.size(1)]Wrapping Up
By combining these three elements—Attention, Feed-Forward layers, and Positional Encoding—you can construct the full Transformer Encoder and Decoder. Experiment with the parameters d_model, num_heads, and d_ff to see how they impact your memory footprint and model capacity!
Continue Reading
Deploying LLMs with vLLM and Ray
A comprehensive tutorial on setting up a high-throughput, low-latency LLM serving cluster using vLLM and Ray.
Understanding Diffusion Models: Math and Intuition
Break down the complex mathematics behind Denoising Diffusion Probabilistic Models (DDPMs) into intuitive concepts.
The Complete Guide to GPU Optimization for Deep Learning
Maximize your CUDA core utilization and avoid common memory bottlenecks with this exhaustive guide to GPU profiling and optimization.
How to Build a RAG System From Scratch
A comprehensive guide on building a Retrieval-Augmented Generation system using modern tools and techniques.