Two students take the same exam.
One can flip to any page — reading forward, backward, skipping around.
The other can only read left to right, never peeking ahead.
The first student is better at understanding. The second is better at writing.
BERT is the first student. GPT is the second.
Same exam. Same knowledge. Completely different strategies.
01 — One Architecture, Two Families
In Day 24 we built the full transformer architecture. In Day 25 we added positional encodings. The transformer is now complete — but here's something remarkable: BERT and GPT are both transformers. They use the same multi-head attention, the same feed-forward blocks, the same layer norm and residual connections. The difference between them is not the architecture — it's what part of the sequence each token is allowed to see during training.
Imagine you're trying to understand a mystery novel. A bidirectional reader (BERT) reads the whole book at once — they can see the ending when analysing chapter 1, and they can see chapter 1 when analysing the ending. They build a complete, context-rich understanding of every sentence from all directions.
A left-to-right reader (GPT) reads strictly page by page — they can never look ahead. When they reach chapter 5, they only know what happened in chapters 1–4. But this constraint turns out to be incredibly useful: because they've learned to predict what comes next, they can write the novel themselves.
Understanding vs writing. Reading vs generating. That's the BERT/GPT split.
This distinction flows from a single change in the attention mask. When computing self-attention, the mask determines which positions each token can attend to:
Every token sees every other token
Each token only sees itself and what came before
That grid is not just a visual — it's literally the attention mask applied before the softmax in Day 24. BERT sets all mask values to 0 (attend everywhere). GPT sets the upper triangle to −∞ (block future tokens). Everything else — the Q/K/V projections, multi-head attention, FFN layers, residuals — is identical.
02 — BERT: The Encoder
What BERT Is
BERT (Bidirectional Encoder Representations from Transformers — Google, 2018) is an encoder-only transformer. It takes a sequence of tokens and produces a rich contextual embedding for each token — a representation that captures the meaning of that token in the context of the entire sentence, both what comes before and what comes after.
A proofreader reads the entire document before correcting a single word. When they get to the word "bank" in sentence 3, they've already read sentence 10 — which mentions "a river" — so they know it means riverbank, not a financial institution. They understand every word in full context.
BERT is the proofreader. It reads the whole sentence before assigning meaning to any word. The embedding BERT produces for "bank" in "The fish swam past the bank" is completely different from the embedding for "bank" in "I deposited money at the bank." Same word, totally different vectors — because BERT saw both the fish and the money before deciding.
BERT's Pre-Training: Masked Language Modelling (MLM)
BERT was pre-trained on two tasks simultaneously. The first and most important is Masked Language Modelling (MLM): randomly hide 15% of tokens in the input by replacing them with a special [MASK] token, then train the model to predict what the hidden tokens were.
Target: predict "sat" and "mat"
Loss = CrossEntropy( BERT_output[masked_pos], true_token_id )
15% of tokens are masked each training step
Of those 15%: 80% replaced with [MASK], 10% random token, 10% unchanged
(The 10% variations prevent the model from only learning [MASK] positions)
To predict "sat," the model must understand both the subject ("The cat") and the destination ("on the [MASK]"). It cannot rely on just looking left — it must use both sides. This forces BERT to build genuinely bidirectional representations. After pre-training on 3.3 billion words, every layer of BERT learned something deep about language: syntax, coreference, semantic relationships, named entities — all encoded in the token embeddings.
The second task is Next Sentence Prediction (NSP): given two sentences A and B, predict whether B actually follows A in the original document or was randomly sampled. This teaches BERT cross-sentence relationships — important for tasks like question answering where the question and passage are both inputs.
The [CLS] Token — BERT's Summary Vector
BERT prepends a special [CLS] (classification) token to every input. After passing through all transformer layers, the final embedding of this token aggregates information from the entire sequence — it becomes a sentence-level representation. For classification tasks, you attach a small linear head on top of [CLS] and fine-tune. The rest of BERT's weights don't need to change much — they already understand language remarkably well.
For sentence pairs: [CLS] sentence_A [SEP] sentence_B [SEP]
Classification: Linear( BERT_output[CLS] ) → logits
BERT_output[CLS] ∈ ℝ^768 (base) or ℝ^1024 (large)
BERT Fine-Tuning: One Pre-trained Model, Any Task
The pre-train/fine-tune paradigm BERT introduced was revolutionary: train once on massive unlabelled text, then fine-tune on small labelled datasets for any specific task. A single BERT-base model reached state-of-the-art on 11 NLP benchmarks simultaneously in 2018 — sentiment analysis, named entity recognition, question answering, textual entailment, and more. The key insight: language understanding is universal. A model that deeply understands language in general can quickly learn any language task with a small additional training signal.
| Task | What you attach to BERT | What you fine-tune on |
|---|---|---|
| Text classification sentiment, spam, topic | Linear layer on [CLS] | Labelled sentences (hundreds–thousands) |
| Named entity recognition find names, dates, orgs | Linear layer on each token output | Tagged text (BIO labels per token) |
| Question answering SQuAD-style extractive | Two linear heads (start, end position) | (question, passage, answer_span) triples |
| Sentence similarity duplicate detection, NLI | Linear layer on [CLS] with two sentences | Sentence pairs with similarity labels |
| Semantic search / embeddings RAG retrieval, clustering | Mean-pool all token outputs (Sentence-BERT) | Contrastive learning on similar pairs |
03 — GPT: The Decoder
What GPT Is
GPT (Generative Pre-trained Transformer — OpenAI, 2018) is a decoder-only transformer. It takes a sequence of tokens and, one token at a time, predicts the next one. The causal mask ensures that when predicting token at position n, it can only see tokens 1 through n−1 — never anything after. This constraint is what makes generation possible.
An author writing a novel never knows what they'll write in chapter 10 when they're writing chapter 1. They write word by word, sentence by sentence, each new word informed only by everything that came before. The constraint "you can't look ahead" is not a weakness — it's exactly what writing requires.
GPT is the author. It reads everything it's written so far and asks: "Given all of this, what word comes next?" Train it on enough text — billions of words of books, articles, code, conversations — and it learns the statistical patterns of human language so thoroughly that its "next word guesses" become coherent paragraphs, then essays, then code, then multi-step reasoning.
GPT's Pre-Training: Causal Language Modelling (CLM)
GPT's training objective is beautifully simple: predict the next token. Given a sequence of tokens, maximise the probability of each token given all preceding tokens. No masking tricks, no special tokens — just next-token prediction across the entire training corpus.
Step 1: given ["The"] → predict "cat"
Step 2: given ["The", "cat"] → predict "sat"
Step 3: given ["The","cat","sat"]→ predict "on"
... and so on for every position
Loss = − (1/T) Σ log P(token_t | token_1, ..., token_{t-1})
T = sequence length. This is the language modelling objective.
This objective is strikingly powerful because it provides a training signal at every position in every sequence. A 1,024-token sequence generates 1,024 training examples in one forward pass. And the task is hard enough to force the model to learn everything about language: grammar (wrong grammar is less probable), facts (wrong facts are less probable), logic (incoherent continuations are less probable), and style — all emerge as side effects of trying to predict what word comes next.
Generation: How GPT Produces Text
At inference time, GPT generates text autoregressively — one token at a time, feeding each newly generated token back as input for the next step:
The sampling strategy matters enormously here. Greedy decoding (always pick the highest probability token) produces repetitive, boring text. Temperature scaling (divide logits by T before softmax) controls randomness — low T makes text more deterministic, high T more creative/random. Top-k sampling (only sample from the top-k tokens) and nucleus sampling / top-p (sample from the smallest set of tokens whose cumulative probability exceeds p) are used by GPT-3, GPT-4, and most modern LLMs.
04 — The Attention Mask: The One Real Difference
Everything above comes down to a single change in the attention computation. Here is the exact mask difference:
scores = Q · Kᵀ / √d_k
weights = softmax(scores) ← every token can attend to every other token
GPT (causal) — upper triangle masked:
mask[i, j] = 0 if j ≤ i (token j is at or before token i → allowed)
mask[i, j] = −∞ if j > i (token j is after token i → blocked)
weights = softmax(scores + mask) ← future tokens get weight 0
This one mask is the entire architectural difference between BERT and GPT. Both use:
- The same transformer blocks (attention + FFN + residual + LayerNorm)
- The same token embeddings and positional encodings
- The same training procedure (AdamW optimiser, learning rate warmup)
- The same tokenizer format (WordPiece for BERT, BPE for GPT)
The mask determines the pre-training objective — bidirectional attention enables MLM, causal attention enables CLM — and the pre-training objective determines what the model is good at.
05 — BERT vs GPT: Head-to-Head
| Dimension | BERT Encoder | GPT Decoder |
|---|---|---|
| Attention direction | Bidirectional — every token sees every other | Causal (left-to-right) — each token sees only past tokens |
| Training objective | Masked Language Modelling (MLM): predict hidden tokens | Causal Language Modelling (CLM): predict next token |
| Can generate text? | ✗ No — no autoregressive mechanism | ✓ Yes — designed for generation |
| Context quality | ✓ Full bidirectional — richest representations | ~ One-directional — weaker for understanding tasks |
| Best use cases | Classification, NER, QA (extractive), semantic search, embeddings | Text generation, chat, code, summarisation, instruction following |
| Fine-tuning paradigm | Add small head to [CLS] or token outputs; fine-tune on small labelled data | Instruction fine-tuning (IFT) + RLHF; few-shot via prompting |
| Scales to very large? | ~ Limited — largest BERT variants ~350M params | ✓ Yes — GPT-3 175B, GPT-4 ~1T, LLaMA-3 405B |
| Key models (2018–2026) | BERT, RoBERTa, ALBERT, DeBERTa, Sentence-BERT, BGE, E5 | GPT-2/3/4, Claude, LLaMA (all), Mistral, Gemma, Falcon, Qwen |
| Typical parameter count | 110M (base) – 350M (large) | 1.5B – 405B+ (modern LLMs) |
| Training data requirement | Moderate — BERT trained on ~3.3B words (Wikipedia + BookCorpus) | Massive — GPT-3 trained on ~300B tokens; LLaMA-3 on 15T tokens |
06 — The Third Family: Encoder-Decoder (T5, BART)
There's a third architecture that combines both: the encoder-decoder (or seq2seq) transformer. It uses a BERT-style encoder to read and understand the input, and a GPT-style decoder to generate the output. The decoder can attend to all encoder outputs via cross-attention — a third type of attention head that reads from the encoder's representations while generating.
A translator reads the entire French sentence first (encoder — bidirectional, full understanding), then writes the English translation word by word (decoder — left-to-right generation). While writing, they can refer back to any part of the French original at any time (cross-attention). The encoding and generation are separate but connected phases.
1. Encoder reads full input sequence → rich contextual embeddings (bidirectional)
2. Decoder generates output token by token (causal)
3. At each decode step, cross-attention lets the decoder query the encoder outputs
Cross-attention: Q from decoder, K and V from encoder
Decoder "asks questions" of the encoder's full-context representations
| Architecture | Examples | Best for | Weakness |
|---|---|---|---|
| Encoder-only | BERT, RoBERTa, DeBERTa, E5, BGE | Understanding, classification, retrieval, embeddings | Cannot generate text at all |
| Decoder-only | GPT-2/3/4, Claude, LLaMA, Mistral, Gemma | Generation, chat, code, reasoning, instruction-following | Weaker on pure understanding tasks without prompting tricks |
| Encoder-Decoder | T5, BART, mT5, FLAN-T5, mBART | Translation, summarisation, conditional generation | More complex, higher memory at inference, smaller ecosystem |
07 — Why the Industry Converged on Decoder-Only
By 2023 the industry had converged overwhelmingly on decoder-only models for general-purpose LLMs — GPT-4, Claude, LLaMA, Mistral, Gemma, Falcon, Qwen. BERT-family models are still dominant for embedding and retrieval. But for the "chat with an AI" use case, decoder-only won. Here's why:
Encoder-only models dominate wherever you need embeddings — fixed-size vector representations of text for search, clustering, or retrieval. BERT's bidirectional representations are denser and more semantically rich than decoder-only representations for the same parameter count. Models like BGE, E5, GTE, and Sentence-BERT are still the backbone of every production RAG pipeline (Day 29) and semantic search system. The key insight: for retrieval, you embed millions of documents offline and query them later — generation is irrelevant. Encoder-only models are 10–100× cheaper to run at that scale than 7B+ decoder models.
08 — Notable Models in Each Family
Encoder-Only — The BERT Family
| Model | Year | Key improvement over BERT |
|---|---|---|
| BERT-base | 2018 | The original. 110M params. Trained on Wikipedia + BookCorpus. |
| RoBERTa | 2019 | Removed NSP task, used dynamic masking, trained on 10× more data. Consistently beats BERT on benchmarks. |
| ALBERT | 2020 | Parameter sharing across layers — 89% fewer params than BERT-large with comparable performance. |
| DeBERTa | 2020 | Disentangled attention (content and position separate). Best BERT-style model on GLUE for years. |
| Sentence-BERT | 2019 | Fine-tuned with siamese networks for sentence embeddings. Still widely used for semantic search. |
| BGE / E5 / GTE | 2023–24 | State-of-the-art text embedding models. Used in RAG pipelines, vector databases, and search. |
Decoder-Only — The GPT Family
| Model | Year | Key milestone |
|---|---|---|
| GPT-1 | 2018 | Proved pre-training + fine-tuning works for language. 117M params. |
| GPT-2 | 2019 | 1.5B params. Coherent paragraph generation. "Too dangerous to release" (then released). |
| GPT-3 | 2020 | 175B params. Few-shot prompting. Demonstrated emergent reasoning. Changed the field. |
| LLaMA-1/2/3 | 2023–24 | Open-weight. LLaMA-3 70B matches GPT-4-level quality on many tasks. 15T training tokens. |
| Mistral 7B | 2023 | Outperforms LLaMA-2 13B at 7B params. Sliding window attention. Apache 2.0 license. |
| Claude (Anthropic) | 2023–26 | Constitutional AI training. Strong at reasoning, coding, and long context (200k+). |
| Gemma / Qwen / Falcon | 2023–24 | Open-weight decoder-only models from Google, Alibaba, and TII. Multilingual focus. |
Encoder-Decoder — The T5 Family
| Model | Year | Key milestone |
|---|---|---|
| T5 | 2019 | Framed every NLP task as text-to-text. Trained on C4 dataset. Unified framework for NLP. |
| BART | 2019 | Pre-trained by corrupting text and learning to reconstruct it. Excellent for summarisation. |
| FLAN-T5 | 2022 | T5 fine-tuned on 1,800+ instruction tasks. Outperforms GPT-3 on many tasks at 11B params. |
| mT5 / mBART | 2021 | Multilingual T5/BART variants. Still widely used for machine translation. |
09 — Practical Decision Guide: Which to Use?
Do you need to generate text? → Decoder-only (GPT-style) or Encoder-Decoder (T5-style)
Do you need fixed-size embeddings for search/retrieval? → Encoder-only (BERT-style)
Do you need to classify, label, or extract from existing text? → Either — BERT for small data, decoder-only LLM with prompting for zero-shot
| Your task | Best architecture | Why |
|---|---|---|
| Sentiment classification (with labelled data) | BERT | Fine-tune on [CLS] output. Needs only hundreds of examples. |
| Named entity recognition | BERT | Token-level output head. Bidirectional context critical for tagging. |
| Semantic search / document retrieval | BERT (Sentence-BERT / BGE) | Dense embeddings. Run offline on millions of docs cheaply. |
| Chatbot / conversational AI | GPT decoder-only | Natural next-token generation, instruction fine-tuning. |
| Code generation / completion | GPT decoder-only | Causal generation mirrors how code is written left-to-right. |
| Summarisation (abstractive) | T5/BART or GPT | Seq2seq natural fit; large decoder-only also works via prompting. |
| Machine translation | mT5 / mBART | Encoder-decoder designed for this. Cross-attention from source to target. |
| Zero-shot classification (no labels) | GPT large decoder-only | Prompt engineering: "Is this review positive or negative? Answer:" |
| RAG retrieval component | BERT (BGE / E5) | Embed documents for vector DB. 10–100× cheaper than LLM at scale. |
| RAG generation component | GPT decoder-only | Generate grounded response from retrieved context. |
10 — Code: Both Architectures Side by Side
A minimal BERT-style encoder and GPT-style decoder in PyTorch, showing the exact difference in the attention mask.
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
# ─────────────────────────────────────────────────────────────────────────
# Shared transformer block (identical for both BERT and GPT)
# ─────────────────────────────────────────────────────────────────────────
class TransformerBlock(nn.Module):
def __init__(self, d_model=128, n_heads=4, d_ff=512, dropout=0.1):
super().__init__()
self.attn = nn.MultiheadAttention(d_model, n_heads, dropout=dropout, batch_first=True)
self.ff = nn.Sequential(nn.Linear(d_model, d_ff), nn.GELU(), nn.Linear(d_ff, d_model))
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.drop = nn.Dropout(dropout)
def forward(self, x, attn_mask=None):
# Pre-LN + residual (modern stable training variant)
x_norm = self.norm1(x)
attn_out, _ = self.attn(x_norm, x_norm, x_norm, attn_mask=attn_mask)
x = x + self.drop(attn_out)
x = x + self.drop(self.ff(self.norm2(x)))
return x
# ─────────────────────────────────────────────────────────────────────────
# BERT-style encoder — bidirectional, no mask
# ─────────────────────────────────────────────────────────────────────────
class BERTEncoder(nn.Module):
def __init__(self, vocab_size=30522, d_model=128, n_layers=4,
n_heads=4, max_len=512):
super().__init__()
self.tok_emb = nn.Embedding(vocab_size, d_model)
self.pos_emb = nn.Embedding(max_len, d_model) # learned absolute PE (BERT-style)
self.layers = nn.ModuleList([TransformerBlock(d_model, n_heads) for _ in range(n_layers)])
self.norm = nn.LayerNorm(d_model)
def forward(self, token_ids):
B, T = token_ids.shape
positions = torch.arange(T, device=token_ids.device)
x = self.tok_emb(token_ids) + self.pos_emb(positions)
for layer in self.layers:
x = layer(x, attn_mask=None) # ← NO MASK: full bidirectional attention
return self.norm(x) # (B, T, d_model) — all token representations
# ─────────────────────────────────────────────────────────────────────────
# GPT-style decoder — causal (left-to-right) mask
# ─────────────────────────────────────────────────────────────────────────
class GPTDecoder(nn.Module):
def __init__(self, vocab_size=50257, d_model=128, n_layers=4,
n_heads=4, max_len=1024):
super().__init__()
self.tok_emb = nn.Embedding(vocab_size, d_model)
self.pos_emb = nn.Embedding(max_len, d_model)
self.layers = nn.ModuleList([TransformerBlock(d_model, n_heads) for _ in range(n_layers)])
self.norm = nn.LayerNorm(d_model)
self.lm_head = nn.Linear(d_model, vocab_size, bias=False) # predicts next token
def get_causal_mask(self, T, device):
# Upper triangle = -inf: token at position i cannot see positions j > i
return torch.triu(torch.full((T, T), float('-inf'), device=device), diagonal=1)
def forward(self, token_ids):
B, T = token_ids.shape
positions = torch.arange(T, device=token_ids.device)
x = self.tok_emb(token_ids) + self.pos_emb(positions)
mask = self.get_causal_mask(T, token_ids.device)
for layer in self.layers:
x = layer(x, attn_mask=mask) # ← CAUSAL MASK: only attend left
x = self.norm(x)
return self.lm_head(x) # (B, T, vocab_size) — next-token logits
@torch.no_grad()
def generate(self, prompt_ids, max_new_tokens=20, temperature=1.0):
"""Simple autoregressive generation with temperature sampling."""
ids = prompt_ids.clone()
for _ in range(max_new_tokens):
logits = self(ids) # (1, T, vocab)
next_logits = logits[:, -1, :] / temperature # last position
probs = F.softmax(next_logits, dim=-1)
next_id = torch.multinomial(probs, num_samples=1)
ids = torch.cat([ids, next_id], dim=1) # append and loop
return ids
# ─────────────────────────────────────────────────────────────────────────
# BERT classification head (add on top for fine-tuning)
# ─────────────────────────────────────────────────────────────────────────
class BERTClassifier(nn.Module):
def __init__(self, encoder, num_classes=2, d_model=128):
super().__init__()
self.encoder = encoder
self.head = nn.Linear(d_model, num_classes)
def forward(self, token_ids):
out = self.encoder(token_ids) # (B, T, d_model)
cls_repr = out[:, 0, :] # [CLS] token is at position 0
return self.head(cls_repr) # (B, num_classes) — classification logits
# ─────────────────────────────────────────────────────────────────────────
# Sanity checks
# ─────────────────────────────────────────────────────────────────────────
B, T = 2, 16 # batch=2, sequence_length=16
fake_input = torch.randint(0, 1000, (B, T))
bert = BERTEncoder(vocab_size=1000, d_model=128)
gpt = GPTDecoder(vocab_size=1000, d_model=128)
clf = BERTClassifier(bert, num_classes=3)
bert_out = bert(fake_input)
gpt_logits = gpt(fake_input)
clf_out = clf(fake_input)
# Check causal mask: position 0 should only attend to position 0
mask = gpt.get_causal_mask(4, 'cpu')
print(f"BERT output: {bert_out.shape} ← (batch, seq, d_model)")
print(f"GPT logits: {gpt_logits.shape} ← (batch, seq, vocab_size)")
print(f"Classifier output: {clf_out.shape} ← (batch, num_classes)")
print(f"\nCausal mask (4×4):\n{mask}")
print(f"\nBERT params: {sum(p.numel() for p in bert.parameters()):,}")
print(f"GPT params: {sum(p.numel() for p in gpt.parameters()):,}")
BERT output: torch.Size([2, 16, 128]) ← (batch, seq, d_model)
GPT logits: torch.Size([2, 16, 1000]) ← (batch, seq, vocab_size)
Classifier output: torch.Size([2, 3]) ← (batch, num_classes)
Causal mask (4×4):
tensor([[ 0., -inf, -inf, -inf],
[ 0., 0., -inf, -inf],
[ 0., 0., 0., -inf],
[ 0., 0., 0., 0.]])
BERT params: 4,072,832 ← encoder only, no LM head
GPT params: 4,200,960 ← includes LM head (vocab_size × d_model)
11 — A Thought to Sit With
BERT reads everything at once and is excellent at understanding. GPT reads left to right and is excellent at generating. Humans do both simultaneously — we understand and generate at the same time, in real-time conversation, without switching modes.
Is the encoder/decoder split a fundamental architectural truth about the nature of language tasks — or just a practical consequence of training objectives that happened to work well? Could a single architecture truly do both as well as specialised ones? And if the industry has converged on decoder-only for almost everything, what does that say about the relationship between generation and understanding?
12 — Coming Up Next
Tomorrow — Day 27: Tokenization Deep Dive. We've been using tokenizers throughout the series without fully opening the black box. Day 27 goes inside: Byte-Pair Encoding (BPE) — how GPT builds its vocabulary; WordPiece — how BERT does it differently; SentencePiece — the language-agnostic approach used by LLaMA and T5; and why the same word tokenizes into completely different numbers of tokens across models — with direct consequences for cost, context length, and multilingual performance.
13 — Go Deeper
📄 BERT: Pre-training of Deep Bidirectional Transformers — Devlin et al. (2018)
The original BERT paper. Section 3 (model architecture) and Section 4 (fine-tuning) are the essential parts. Table 1 showing 11 GLUE benchmark improvements simultaneously is one of the most impressive result tables in NLP history.
📄 Improving Language Understanding by Generative Pre-Training — Radford et al. (2018)
The original GPT-1 paper. Short and readable. Section 3 explains the pre-training/fine-tuning paradigm that the entire field adopted.
📄 RoBERTa: A Robustly Optimized BERT Pretraining Approach — Liu et al. (2019)
Shows that BERT was undertrained. Removing NSP, increasing batch size, and training longer produces consistent improvements. Section 4 is a clean ablation study worth reading.
📄 Exploring the Limits of Transfer Learning with T5 — Raffel et al. (2019)
Unifies every NLP task as text-to-text. The most rigorous comparison of architectures, objectives, and scales ever published. Section 3 is a masterclass in ablation study design.
▶ BERT explained — Andrej Karpathy / various
Karpathy's "Let's build GPT from scratch" on YouTube is the clearest derivation of the decoder architecture available. Highly recommended alongside this post.
- Same architecture, one difference: BERT and GPT are both transformer stacks with identical attention, FFN, residual, and norm components. The only architectural difference is the attention mask — BERT uses no mask (bidirectional), GPT uses a causal upper-triangle mask (left-to-right only).
- BERT (encoder-only): every token attends to every other token. Trained with Masked Language Modelling (MLM) — predict 15% hidden tokens using both left and right context. Produces rich contextual representations. Cannot generate text.
- GPT (decoder-only): each token only attends to itself and previous tokens. Trained with Causal Language Modelling (CLM) — predict the next token from all preceding tokens. Generates text autoregressively. Cannot use future context.
- BERT fine-tuning: add a small task-specific head (linear layer on [CLS] or token outputs) and fine-tune on small labelled datasets. The pre-trained weights transfer extremely well — hundreds of labelled examples often suffice.
- GPT generation: append the sampled token to the input and repeat. Temperature controls randomness. Top-k / nucleus (top-p) sampling prevents degenerate outputs. KV-cache (Day 24) makes this efficient at scale.
- Encoder-Decoder (T5, BART): combines both — a BERT-style encoder reads the input, a GPT-style decoder generates the output with cross-attention to the encoder. Best for translation and conditional summarisation.
- Why decoder-only won for LLMs: CLM uses 100% of tokens as training signal (vs 15% for MLM). Next-token prediction scales cleanly. Universal objective handles all tasks via prompting. Compatible with instruction fine-tuning and RLHF.
- BERT still wins at retrieval: for embedding-based semantic search and RAG retrieval, encoder-only models (BGE, E5, Sentence-BERT) produce better dense vectors and are 10–100× cheaper to run at scale than decoder-only models.
- Key model families: BERT → RoBERTa → DeBERTa → BGE/E5 (encoder); GPT-1 → GPT-3 → GPT-4 / LLaMA → Claude / Mistral / Gemma (decoder); T5 → FLAN-T5 / BART → mT5 (encoder-decoder).
- Decision rule: embedding/retrieval/classification with labelled data → encoder. Generation/chat/code/zero-shot → decoder. Translation/summarisation with seq2seq format → encoder-decoder.
- What is the difference between an encoder and a decoder in the transformer architecture?
An encoder uses bidirectional (full) attention — every token can attend to every other token. It maps an input sequence to a sequence of contextual representations. A decoder uses causal (unidirectional) attention — each token can only attend to itself and previous tokens. It maps a sequence to next-token probability distributions and generates text autoregressively. Same transformer blocks, different attention mask. BERT is encoder-only; GPT is decoder-only; T5 combines both. - What is the difference between Masked Language Modelling (MLM) and Causal Language Modelling (CLM)?
MLM (BERT): randomly mask 15% of input tokens and predict them using the surrounding context from both directions. Training signal covers 15% of tokens per pass. Forces the model to build bidirectional representations but cannot be used for generation. CLM (GPT): predict the next token given all preceding tokens, for every position in the sequence. Training signal covers 100% of tokens per pass — significantly more efficient. Scales cleanly. Enables autoregressive generation but can only use left context. The choice of objective determines the attention pattern and ultimately what the model can and cannot do. - Why are encoder-only models (BERT) preferred for semantic search and retrieval, even in a world dominated by decoder-only LLMs?
Three reasons: (1) Representation quality — BERT's bidirectional attention produces richer, more semantically complete fixed-size embeddings than decoder-only models' one-directional representations at the same parameter count. For retrieval, you need a single vector that captures the full meaning of a document. (2) Cost at scale — embedding 10M documents with a 110M-parameter BERT costs a fraction of running a 7B decoder-only model for the same. Embeddings are computed offline once and reused. (3) Task alignment — semantic search doesn't require generation. Running a full generative LLM to produce embeddings is wasteful — the generation head is never used. BGE, E5, and Sentence-BERT are purpose-built for this and significantly outperform decoder-only models at equivalent cost on retrieval benchmarks. - What is the [CLS] token in BERT and how is it used for classification?
BERT prepends a special [CLS] (classification) token to every input. During pre-training with NSP, [CLS]'s final representation is trained to capture sentence-level information — it aggregates context from the entire sequence through attention across all layers. For fine-tuning on classification tasks, a linear layer is attached to the final [CLS] embedding: logits = W · BERT_output[CLS] + b, where W ∈ ℝ^(num_classes × d_model). The full model (BERT + linear head) is then fine-tuned end-to-end on the labelled task data. [CLS] is not inherently special architecturally — it's just a token that BERT learns during pre-training to aggregate global information because the NSP objective requires it. In practice, mean-pooling all token outputs often performs equally well or better for sentence embeddings, which is why Sentence-BERT uses mean-pooling instead of [CLS]. - A company wants to build a customer support system that: (1) classifies incoming tickets into 12 categories, (2) generates personalised responses. What model architecture would you recommend for each and why?
Task (1) — classification: use a fine-tuned BERT-style encoder (BERT, RoBERTa, or DeBERTa). Reasons: with labelled ticket data available, fine-tuning an encoder on [CLS] output is highly sample-efficient (hundreds of examples per category typically sufficient), computationally cheap at inference (110M–350M params, runs fast on CPU or small GPU), and produces excellent accuracy for classification. A 12-class linear head on top of RoBERTa would be the first choice. Alternative: if labelled data is very limited (<50 examples/class), prompt a decoder-only LLM for zero-shot/few-shot classification instead. Task (2) — generation: use a fine-tuned decoder-only LLM (LLaMA-3 7B/8B or Mistral 7B). Reasons: generating personalised natural-language responses requires autoregressive generation, which only decoder-only models support. Fine-tune with instruction-following data (customer support Q&A pairs) to ground the model in company policies and tone. The two components run independently — the classifier routes the ticket and the LLM generates the response. This hybrid encoder (fast, cheap classification) + decoder (rich generation) architecture is the industry standard for production customer support AI systems.
📄 BERT: Pre-training of Deep Bidirectional Transformers — Devlin et al. (2018)
The paper that established the pre-train/fine-tune paradigm. Table 1 (11 GLUE tasks improved simultaneously) is still remarkable. Sections 3–4 cover architecture and fine-tuning clearly.
📄 RoBERTa — Liu et al. (2019)
BERT was undertrained. RoBERTa shows that removing NSP, using dynamic masking, and training longer consistently improves all tasks. Essential for understanding what actually matters in BERT pre-training.
📄 Exploring the Limits of Transfer Learning with T5 — Raffel et al. (2019)
The most thorough architecture + objective comparison in NLP. Unifies everything as text-to-text. Section 3's ablation table is the best guide to understanding what choices matter and why.
▶ Let's build GPT from scratch — Andrej Karpathy (2023)
YouTube · 2hr · The clearest hands-on derivation of the decoder architecture available. Builds a miniGPT from scratch in ~250 lines of PyTorch. Essential viewing for anyone who wants to truly understand how GPT works.
🔗 The Illustrated BERT — Jay Alammar
Visual step-by-step walkthrough of BERT's pre-training and fine-tuning. The diagrams for MLM, NSP, and the fine-tuning paradigm are the best available.