April 14, 2026
Last lecture we built GPT — the decoder-only side of the transformer.
GPT answers the question: “given what you’ve seen so far, what comes next?”
Today: the other side of the Schism. Encoder-only models.
These answer a different question: “given this input, what does it mean?”
GPT only sees tokens from the left. When predicting the next token, it must work with incomplete context.
But for understanding a piece of text, you often need to see the full context:
“I went to the bank to deposit some money.”
“I went to the bank of the river to watch the sunset.”
Only by seeing the words after “bank” can you disambiguate its meaning.
GPT can generate coherent text, but its representations are fundamentally unidirectional. For tasks that require deep understanding — classification, retrieval, similarity — bidirectional context is a massive advantage.
GPT produces tokens — text.
BERT and ViT produce representations — dense vectors that encode meaning.
For every input position, encoder-only models output a contextualized embedding:
\[\mathbf{x} = [x_1, x_2, \ldots, x_T] \;\to\; \text{Encoder} \;\to\; \mathbf{h} = [\mathbf{h}_1, \mathbf{h}_2, \ldots, \mathbf{h}_T]\]
Each \(\mathbf{h}_i\) summarizes what position \(i\) means in the context of the entire sequence.
These representations are the foundation for everything downstream:
Bidirectional Encoder Representations from Transformers (Devlin et al., 2018)
Architecture: the encoder half of the original transformer. Nothing fancy.
The innovation isn’t the architecture. It’s the training objective.
For GPT, the training objective was obvious: predict the next token. A left-to-right signal that every sequence provides for free.
For bidirectional models, this doesn’t work. If position \(t\) sees positions \(t+1, t+2, \ldots\) during training, “predict the next token” is trivial — just read it directly.
We need a training objective that:
Randomly mask 15% of input tokens. Ask the model to predict what was masked, using the bidirectional context.
Example:
To predict the masked word, the model must build a rich representation of the surrounding context. It must learn:
MLM is the encoder equivalent of GPT’s next-token prediction — a self-supervised objective that forces the model to learn useful representations.
The mask is more subtle than “replace 15% with [MASK]”:
Why the complexity?
BERT also trains on a second objective: next sentence prediction (NSP).
Given two sentences \(A\) and \(B\), predict whether \(B\) actually follows \(A\) in the original corpus, or was randomly sampled from elsewhere.
Input format:
\[\text{[CLS]} \;\; A_1 \; A_2 \ldots A_m \;\; \text{[SEP]} \;\; B_1 \; B_2 \ldots B_n \;\; \text{[SEP]}\]
The [CLS] token’s final embedding is used for the binary classification.
Why NSP? It teaches cross-sentence coherence — useful for downstream tasks like question answering and natural language inference where understanding relationships between sentences matters.
Later work found NSP wasn’t very important. RoBERTa dropped it and did better. But it was part of the original BERT recipe.
Every BERT input starts with a special token: [CLS]. A learned beginning-of-sequence token that serves as a summary representation for the entire input.
This is the token you use for sentence-level classification tasks. Pass the [CLS] embedding through a small linear head to produce class logits:
cls_embedding = bert(input_ids).last_hidden_state[:, 0, :] # (B, 768)
logits = classifier_head(cls_embedding) # (B, num_classes)| Model | Layers | Hidden Dim | Heads | Parameters |
|---|---|---|---|---|
| BERT-base | 12 | 768 | 12 | 110M |
| BERT-large | 24 | 1024 | 16 | 340M |
But BERT’s release in 2018 was the watershed moment.
Before BERT: every NLP task required its own model, trained from scratch.
After BERT: one pretrained model, fine-tuned for everything.
BERT was released in October 2018. Within 18 months, dozens of variants appeared, each addressing some limitation of the original.
The core architecture barely changed. What changed: training strategy, training data, parameter efficiency, and downstream adaptation.
Let’s look at the major descendants. Each one teaches something about what matters in pretraining.
RoBERTa (Liu et al., 2019) is BERT with no architectural changes. But it significantly outperforms BERT.
The changes:
The lesson: BERT was undertrained. The architecture was fine — the recipe needed improvement.
DistilBERT (Sanh et al., 2019): a BERT that’s 40% smaller, 60% faster, retains 97% of BERT’s performance on downstream tasks.
How? Knowledge distillation.
Train a small student model (6 layers instead of 12) to match the output distribution of the full teacher BERT. The student learns to mimic the teacher’s predictions across many inputs.
Loss function combines:
Hard loss: predict the true labels (like normal training)
Soft loss: match the teacher’s output distribution (KL divergence)
Cosine loss: match the teacher’s hidden states
Why this works: the teacher’s output distribution contains more information than hard labels. “90% dog, 9% wolf, 1% cat” teaches the student something that “1.0 dog, 0 wolf, 0 cat” doesn’t — namely, that dogs and wolves are similar.
Distillation became a standard technique for deploying large models on edge devices or in latency-sensitive applications.
ALBERT (Lan et al., 2019): “A Lite BERT” — dramatically reduces parameters with two tricks.
Factorized embeddings: instead of a \(V \times H\) embedding matrix (vocabulary × hidden), use \(V \times E\) and \(E \times H\) where \(E \ll H\). Drops embedding parameters by ~85%.
Cross-layer parameter sharing: use the same attention weights across all 12 layers. The model still has 12 computational layers but only 1 layer’s worth of parameters.
Result: ALBERT-base has 12M parameters (vs BERT-base’s 110M) and matches or slightly beats BERT on most tasks.
ELECTRA (Clark et al., 2020): “Efficiently Learning an Encoder that Classifies Token Replacements Accurately”
MLM has an inefficiency: the model only learns from the 15% of tokens that were masked. The other 85% provide no training signal.
ELECTRA’s insight: instead of predicting masked tokens, detect replaced tokens.
Result: ELECTRA trains ~4× faster than BERT for equivalent performance. Small ELECTRA matches BERT-base with 1/30 the compute.
Before BERT, the NLP workflow for each task looked like this:
Problems:
The paradigm shift:
The pretrained model already knows:
Fine-tuning teaches it to apply this knowledge to a specific task, not to learn language from scratch.
This works because most of what a sentiment classifier needs to know (grammar, vocabulary, semantics) is the same as what a Q&A model needs to know. Pretraining captures the shared foundation.
The mechanics are beautifully simple:
from transformers import BertModel, BertTokenizer
# Load pretrained BERT (downloaded once, used for everything)
bert = BertModel.from_pretrained("bert-base-uncased")
tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
# Add a task-specific head
classifier = nn.Linear(768, num_classes)
# Train on your task
for batch in dataloader:
outputs = bert(**batch)
cls_embedding = outputs.last_hidden_state[:, 0, :]
logits = classifier(cls_embedding)
loss = F.cross_entropy(logits, labels)
loss.backward()
optimizer.step()During fine-tuning, both BERT’s weights and the classifier head are updated. The pretrained knowledge isn’t frozen — it’s a starting point that adapts to the specific task.
Three forces combine:
1. Massive pretraining data. BERT was trained on Wikipedia + BookCorpus (~3.3 billion words). No downstream task has this much labeled data. Pretraining extracts knowledge from unlabeled text that fine-tuning couldn’t possibly learn.
2. Shared linguistic foundation. Every NLP task requires understanding grammar, vocabulary, and semantics. Pretraining captures this once, fine-tuning adapts it.
3. Small labeled datasets become sufficient. Because most of the model’s knowledge comes from pretraining, fine-tuning can succeed with just a few thousand labeled examples — sometimes even a few hundred.
Different tasks use different “heads” on top of BERT:
The backbone is always the same BERT. Only the head and the input format change.
BERT treats text as a sequence of word tokens. The transformer encoder processes the sequence bidirectionally.
Can we do the same for images?
The key insight: treat an image as a sequence of patches instead of pixels.
For a 224×224 RGB image:
The result: a sequence of 197 tokens (196 patches + [CLS]) that a transformer can process using the exact same machinery as BERT.
After the patch embedding step, ViT is architecturally identical to BERT:
The only differences are the input preprocessing (patches vs word embeddings) and the training objective (supervised classification vs MLM).
ViT-Base: 12 layers, 86M parameters. ViT-Large: 24 layers, 307M parameters. ViT-Huge: 32 layers, 632M parameters.
Same size categories as BERT. Same transformer recipe, different input.
Here’s where ViT gets interesting.
Trained on ImageNet (1.3M images): ViT underperforms comparable CNNs. ResNet-50 beats ViT-Base. Why?
CNNs have built-in priors that help with images:
ViT has none of these. It treats patches as an unordered set (until positional embeddings are added). It must learn the visual equivalents of these inductive biases from data.
With only 1.3M images, ViT doesn’t have enough data to learn these priors. CNNs win because their architecture provides the priors for free.
Trained on JFT-300M (300 million images): ViT matches or beats every CNN. Why?
At massive scale, the inductive biases baked into CNNs become a ceiling, not a floor.
CNNs are forced to extract features in a specific way (hierarchical, translation-equivariant). When you have enough data, this rigidity limits what the model can learn.
ViT has no such rigidity. With enough data, it learns more general, more flexible representations that outperform the hand-designed CNN inductive biases.
This has real consequences.
CNNs work for everyone. A research group with a few GPUs and a few million images can train a competitive CNN.
ViTs only work at massive scale. You need hundreds of millions of training images and enormous compute to get ViTs to beat CNNs.
Who has hundreds of millions of proprietary images? Google, Meta, OpenAI, Anthropic. A handful of companies.
Fortunately, you don’t need JFT-300M to use a ViT. Pretrained models are freely available:
from torchvision.models import vit_b_16, ViT_B_16_Weights
model = vit_b_16(weights=ViT_B_16_Weights.IMAGENET1K_V1)Or from HuggingFace:
You’ve seen LoRA before — low-rank updates to frozen pretrained weights, trained efficiently, merged at deployment.
For transformers in the HuggingFace ecosystem, the standard tool is the PEFT library (Parameter-Efficient Fine-Tuning).
PEFT wraps any model from transformers with LoRA adapters in a few lines:
from transformers import AutoModelForSequenceClassification
from peft import LoraConfig, get_peft_model
model = AutoModelForSequenceClassification.from_pretrained(
"bert-base-uncased", num_labels=2
)
lora_config = LoraConfig(
r=8, # rank of the low-rank decomposition
lora_alpha=16, # scaling factor for the update
target_modules=["query", "value"], # which weights to adapt
lora_dropout=0.1,
bias="none",
task_type="SEQ_CLS",
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# trainable params: 295,682 || all params: 109,778,690 || trainable%: 0.27%Fine-tuning a 110M-parameter BERT with LoRA updates ~300K parameters. For a 70B-parameter Llama with LoRA, you update ~35M parameters — trainable on a single consumer GPU.
The target_modules parameter controls which weights get LoRA adapters. Different choices for different architectures:
For BERT and most encoder models:
Empirically, adapting Q and V is sufficient for most tasks. Adding K projections helps marginally.
For LLMs (Llama, GPT-style):
Newer practice adapts attention projections and MLP layers. More parameters, better performance on complex reasoning tasks.
For ViT:
We’ve spent the whole semester on representation learning. From PCA to CNNs to VAEs to BERT to ViT, the core question has always been:
How do we extract meaningful structure from unstructured data?
Encoder-only models are the current gold standard for this. The paradigm we’ve covered — massive self-supervised pretraining, lightweight task-specific adaptation — dominates every corner of applied ML that isn’t generation.
Before we talk about transformers taking over vision, let’s correct a common misconception.
CNNs are still extremely important. They haven’t been replaced — they’ve been supplemented.
Modern CNN architectures continue to improve and compete with ViTs on many tasks:
The lesson: architecture choice matters less than training protocol and scale. A well-trained CNN with modern techniques matches a well-trained ViT on most benchmarks.
CNNs have real practical advantages over pure ViTs:
Efficiency at small scale. CNNs’ inductive biases (locality, translation equivariance) mean they learn well from smaller datasets. If you have 10K images, use a CNN.
Mobile and edge deployment. CNN operations are highly optimized for mobile GPUs and NPUs. Modern phones can run EfficientNet in real-time; ViTs struggle without significant optimization.
Dense prediction tasks. Semantic segmentation, depth estimation, optical flow — tasks that need pixel-accurate outputs. CNNs’ spatial structure aligns naturally with these outputs. U-Net (CNN-based) remains the default for medical imaging.
Real-time video. Object detection and tracking in videos need to run at 30+ FPS on modest hardware. CNN detectors (YOLO series, particularly YOLOv8/v9/v10) dominate here.
Despite ViT’s dominance on web-scale datasets, LoRA fine-tuning of pretrained CNNs (especially ResNet variants) continues to win in specific domains:
Scientific imaging: ImageNet-pretrained ResNet-50 or ResNet-152, fine-tuned with LoRA, consistently outperforms ViT on:
Despite ViT’s dominance on web-scale datasets, LoRA fine-tuning of pretrained CNNs (especially ResNet variants) continues to win in specific domains:
Sports analytics: ResNet backbones dominate in:
The general rule: for specialized domains with modest data, LoRA-fine-tuned CNNs often beat ViTs. Don’t assume the latest architecture wins every task — check the domain literature before defaulting to a transformer.
The real state-of-the-art isn’t pure CNN or pure ViT. It’s hybrid architectures that combine both.
The intuition: convolutions are efficient at capturing local spatial patterns. Self-attention is efficient at capturing long-range dependencies. Use each where it works best.
Key hybrid architectures:
Swin Transformer (Liu et al., 2021): windowed self-attention that processes local regions (like convolutions) with periodic shifts to enable cross-window communication. Dominant backbone for detection and segmentation in 2021-2023.
CoAtNet (Dai et al., 2021): “Coupling Attention with Convolution” — uses convolutions in early layers (to leverage locality) and attention in later layers (for global context). Wins ImageNet at every model size.
ConvNeXt V2 (Woo et al., 2023): CNNs updated with masked autoencoder pretraining. Matches self-supervised ViT performance.
EfficientFormer (Li et al., 2022): designed specifically for mobile deployment. Hybrid conv + attention that runs as fast as MobileNet while matching ViT accuracy.
The pure architectures have complementary weaknesses:
| Strength | Weakness | |
|---|---|---|
| CNN | Strong locality prior, efficient, great at small scale | Weak long-range dependencies, limited receptive field |
| ViT | Global attention, scales to massive data | Quadratic attention cost, no locality prior, data-hungry |
Hybrid architectures get the best of both:
This mirrors a more general principle: priors are most valuable when information is most local. Pixels → convolutions. Abstract features → attention.
Masked Autoencoders (He et al., 2022): BERT-style self-supervised pretraining applied to vision.
The recipe:
Why this beats supervised pretraining:
MAEs now serve as the foundation for many vision models that used to be trained supervised on ImageNet. Same pattern as BERT: self-supervised pretraining replaces labeled pretraining.
A broader trend: move entirely away from labeled datasets during pretraining.
Key techniques:
The backbone architecture (CNN, ViT, hybrid) matters less than the self-supervised objective. Pick the backbone that fits your deployment constraints; pick the objective that fits your data.
Two competing approaches to vision foundation models:
DINOv2 (Oquab et al., 2023): Meta’s self-supervised ViT trained on 142M curated images. Produces general-purpose visual features useful for classification, segmentation, depth estimation, retrieval. One encoder, many tasks, no task-specific training.
SAM — Segment Anything (Kirillov et al., 2023): trained on 1 billion segmentation masks across 11M images. Accepts prompts (points, boxes, text) and produces pixel-accurate masks. Foundation model for dense prediction.
Both represent the dream: the vision analog of BERT/GPT. Solve the representation problem once, then apply it everywhere.
Text representation learning has also moved past vanilla BERT.
Sentence-BERT (SBERT): fine-tune BERT so that the [CLS] embedding directly produces useful sentence vectors. Specialized for retrieval and similarity.
E5, BGE, Cohere Embed: dedicated embedding models trained with contrastive objectives on massive retrieval datasets. Outperform general BERT on vector search, RAG, and semantic similarity.
Multilingual models (XLM-R, mBERT): pretrain on 100+ languages simultaneously, producing shared representations across languages.
The cutting edge: learn representations that work across multiple modalities simultaneously.
CLIP (next lecture): text and images in a shared embedding space.
ImageBind (Meta, 2023): six modalities in a shared space — images, text, audio, depth, thermal, IMU (motion) data. Any modality can be used to retrieve any other.
DINO + CLIP: combining self-supervised visual features with language alignment produces vision models that “understand” content in ways earlier models couldn’t.
The pattern: representations are becoming universal. One embedding space that captures semantic content regardless of input modality.
The unifying claim of modern ML:
Almost every problem becomes tractable if you have a good representation.
If a text encoder produces meaningful sentence embeddings, classification becomes a nearest-neighbor problem. Retrieval becomes vector search. Clustering becomes k-means. Similarity becomes cosine distance.
If a visual encoder (CNN, ViT, or hybrid) produces meaningful image embeddings, all the same tools apply to images.
If a multimodal encoder aligns text and images in one space, cross-modal retrieval and zero-shot classification become trivial.
The hard part isn’t solving downstream tasks. It’s learning good representations.
That’s what this semester has been about. From PCA to ConvNeXt to ViT, every lecture has added tools to the representation-learning toolkit. You now have the modern state-of-the-art arsenal.
A conceptual aside: GPT is also a pretrained model.
Pretraining: next-token prediction on web text. Fine-tuning for chatbots: supervised fine-tuning on instruction-following data + RLHF.
The difference is that GPT-style pretraining also happens to produce models that can generate text, so they can serve as chatbots after fine-tuning. BERT’s pretraining produces models that can understand text but not easily generate it.
We now have:
What if we trained these together so that images and their captions produce similar representations?
A shared embedding space where “a photo of a dog” and an image of a dog land in nearly the same place.
This is CLIP. Next lecture.
And once we have CLIP, we can finish the DALL-E 1 and introduce the true bleeding edge - Stable Diffusion.