Transformers in Machine Learning: Self-Attention and Beyond

Understand the Transformer architecture for ML — self-attention, multi-head attention, positional encoding, encoder-decoder, BERT, GPT, and Vision Transformers.

Transformers in Machine Learning

The Transformer architecture, introduced in “Attention Is All You Need” (2017), revolutionized machine learning by replacing recurrence with self-attention. Today, Transformers are the dominant architecture in NLP, computer vision, audio, and increasingly in tabular data.


The Core Problem Transformers Solve

RNNs/LSTMs process sequences step by step — they can’t parallelize training and struggle to connect information from very distant positions. Transformers replace the sequential process with self-attention: every position can directly attend to every other position in a single operation.


Self-Attention

For each token, self-attention computes a weighted sum of all other tokens’ values, where weights are determined by query-key similarity:

Input sequence: "The cat sat on the mat"
For each word:
Query (Q): "What am I looking for?"
Key (K): "What do I contain?"
Value (V): "What information do I pass on?"
Attention(Q, K, V) = softmax(QKᵀ / √dₖ) × V
The word "sat" attends strongly to "cat" (who sat?) and "mat" (sat where?)
import torch
import torch.nn as nn
import math
class SelfAttention(nn.Module):
def __init__(self, d_model, num_heads):
super().__init__()
self.num_heads = num_heads
self.d_head = 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 forward(self, x, mask=None):
B, T, C = x.shape
Q = self.W_q(x).view(B, T, self.num_heads, self.d_head).transpose(1, 2)
K = self.W_k(x).view(B, T, self.num_heads, self.d_head).transpose(1, 2)
V = self.W_v(x).view(B, T, self.num_heads, self.d_head).transpose(1, 2)
scores = (Q @ K.transpose(-2, -1)) / math.sqrt(self.d_head)
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
attn = torch.softmax(scores, dim=-1)
out = attn @ V # (B, heads, T, d_head)
out = out.transpose(1, 2).contiguous().view(B, T, C)
return self.W_o(out)

Transformer Block

A standard Transformer block consists of self-attention + feedforward network with residual connections and layer normalization:

class TransformerBlock(nn.Module):
def __init__(self, d_model, num_heads, ff_dim, dropout=0.1):
super().__init__()
self.attention = SelfAttention(d_model, num_heads)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.ff = nn.Sequential(
nn.Linear(d_model, ff_dim),
nn.GELU(),
nn.Dropout(dropout),
nn.Linear(ff_dim, d_model),
nn.Dropout(dropout)
)
def forward(self, x, mask=None):
x = self.norm1(x + self.attention(x, mask)) # Residual + attention
x = self.norm2(x + self.ff(x)) # Residual + FFN
return x

Positional Encoding

Unlike RNNs, Transformers process all tokens simultaneously — they have no built-in notion of order. Positional encodings are added to token embeddings to inject position information:

class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_seq_len=5000):
super().__init__()
pe = torch.zeros(max_seq_len, d_model)
position = torch.arange(max_seq_len).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2) * (-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)
def forward(self, x):
return x + self.pe[:x.size(1)].unsqueeze(0)

Transformer Families

Encoder-only (BERT-style): Bidirectional attention over the full sequence. Best for understanding tasks: classification, NER, question answering.

Decoder-only (GPT-style): Causal (masked) attention — each token attends only to past tokens. Best for generation: text completion, summarization, code generation.

Encoder-decoder (T5-style): Full encoder + causal decoder. Best for sequence-to-sequence: translation, summarization with fixed-length output.


Using Pretrained Transformers with HuggingFace

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
model = AutoModelForSequenceClassification.from_pretrained("bert-base-uncased", num_labels=3)
texts = ["I love this product!", "Terrible experience.", "It's okay."]
inputs = tokenizer(texts, padding=True, truncation=True, return_tensors="pt", max_length=128)
with torch.no_grad():
outputs = model(**inputs)
predictions = outputs.logits.argmax(dim=-1)

Vision Transformers (ViT)

Transformers now achieve state-of-the-art on image tasks by splitting images into 16×16 patches and treating them as “tokens”:

from torchvision.models import vit_b_16
vit = vit_b_16(pretrained=True)

Transformers are the unified architecture of modern AI — if you understand self-attention, you understand the core mechanism behind GPT, BERT, ViT, Whisper, DALL·E, and most state-of-the-art models.