Vision Transformers (ViT)
Architecture, attention mechanism, variants, and the vanishing-gradient story — a complete undergraduate lecture adapted from the CEAMLS slide deck.
What is a Vision Transformer? A regular CNN looks at an image through small sliding windows and slowly builds up what it sees. A Vision Transformer (ViT) instead chops the image into a grid of small square patches (think of cutting a photo into 16×16-pixel postage stamps), turns each patch into a vector, and lets every patch talk to every other patch from the very first layer. That "talking" is called self-attention.
Why is that useful? A CNN needs many layers before two pixels on opposite sides of the image can influence each other. A ViT can connect them in a single step. For satellite imagery — where a damaged roof on one corner of the tile relates to debris on the other corner — this long-range view is a big advantage.
![Vision Transformer (ViT) architecture: input image split into 16×16 patches, linear projection, patch + positional embeddings with [CLS] token, transformer encoder block (LayerNorm, Multi-Head Self-Attention, MLP, residuals) repeated L×, then MLP head for class label.](/assets/vit-architecture-Dgz0TIkx.jpg)
- ① Input Image (H×W×3) — one RGB photo, e.g. a 224×224 satellite tile of a building. The 3 is the red/green/blue channels.
- ② Split into Patches (16×16) — chop the image into a grid of small square "tiles". A 224×224 image gives 14×14 = 196 patches. Think of each patch as a "word" in a sentence.
- ③ Linear Projection (Flatten + Linear) — each patch (16·16·3 = 768 numbers) is multiplied by a learnable matrix E. This turns a raw patch into a vector the Transformer can understand. (It is mathematically identical to a single Conv 16×16, stride 16.)
- ④ Patch Embeddings — the stack of vectors that comes out of step 3, one per patch. The extra purple [CLS] token at the top is a learnable "summary" vector — its final value is what we use to classify the image (BERT-style).
- ⑤ Positional Embeddings (+) — self-attention is order-blind, so we add a small learnable vector that says "I am patch #3 in row 1". Without this, the model could not tell top-left from bottom-right.
- ⑥ Transformer Encoder (L×) — the heart of ViT, repeated L times (e.g. 12 in ViT-Base). Inside each block: Layer Norm stabilises values → Multi-Head Self-Attention lets every patch look at every other patch → residual add (⊕) preserves the input → Layer Norm → MLP (a small feed-forward network refining each token) → another residual add.
- ⊕ Residual connections — the curved "skip" arrows that add the input back to the output. They are what stops the gradient from vanishing in deep stacks (see §7 below).
- ⑦ MLP Head — a tiny classifier (one or two dense layers) that reads only the final [CLS] vector.
- ⑧ Class Label — the prediction. In our damage-assessment context: "building / no-damage / minor / major / destroyed".
- Legend symbols — D = embedding dimension (768 in ViT-Base), L = number of encoder blocks, N = number of patches.
Why Transformers for Vision? CNNs vs ViT
Fundamental limitations of convolution and what Transformers offer instead.
A 3×3 kernel sees only 9 pixels at a time — a local receptive field. To let a top-left pixel influence a bottom-right pixel, you must stack many conv layers, and the global receptive field is still approximate and path-dependent.
- Global self-attention — every patch attends to every other patch in a single layer.
- No baked-in inductive bias — the model learns translation invariance (or not) from data.
- Unified architecture with NLP Transformers → multi-modal models, transfer learning.
- Scales with data and parameters; ViT-G (6B params) hits 90%+ on ImageNet.
| Feature | CNN | ViT |
|---|---|---|
| Context range | Local (kernel size) | Global (all patches) |
| Inductive bias | Strong (translation inv.) | Weak — learned from data |
| Data needed | Less (biases help) | More, or pre-training |
| Computation | O(N·k²) | O(N²) — quadratic in tokens |
| Long-range dependencies | Many stacked layers | One single layer ✓ |
Patch Embedding & Tokenisation
How an image becomes a sequence of tokens — the very first step of ViT.
- Original image — a 224×224 pixel RGB photo. "RGB" = three colour channels (Red, Green, Blue), so the tensor shape is
[3, 224, 224]. - Extract patches — divide the image into non-overlapping P×P squares with P=16. That gives 14×14 = N=196 patches, and each patch holds 16·16·3 = 768 raw numbers. Think of each patch as one "word" in a 196-word sentence.
- Linear projection (the "embedding" step) — flatten each patch into a length-768 row and multiply by a learnable matrix . Out comes a D=768-dimensional vector per patch. (Trivia: this is mathematically identical to applying a single Conv 16×16 with stride 16 — convolution and patch embedding are the same op here.)
- Prepend the [CLS] token — we glue an extra learnable vector to the front of the sequence. "CLS" = "classification". After L layers of attention, its final value summarises the whole image and is what we hand to the classifier. (Borrowed from BERT in NLP, where the same trick summarises a sentence.)
- Add Positional Encoding (PE) — attention is "set-like": it doesn't know which patch came first. So we add a learnable vector for every position (1…197) that says "I'm patch #3 of row 1". Without PE, scrambling the patches would give the same answer.
Self-Attention — Query, Key, Value
The mathematical core of the Transformer — how patches 'talk to' each other.
Intuition before maths. Imagine a classroom of 197 students (= the 197 tokens). Each student writes three sticky-notes: a Question (Q — "I'm looking for information about…"), a Tag (K — "I know about…"), and a Note (V — the actual content they will share). Self-attention is the rule: every student compares their Q to everyone else's K, and copies a weighted average of everyone's V — listening hardest to whoever matches them best.
In maths, each token's Q, K, V is built by multiplying the token's vector by three learned matrices: , , .
- — queries — what each token is looking for
- — keys — what each token offers
- — values — the content each token will share
- — dimension of each key vector (64 in ViT-Base)
- — [N×N] similarity matrix — entry (i,j) = how much token i wants token j
- — row-wise: turns each row of scores into probabilities that sum to 1
Step by step: (1) compare every token to every other token via (one big dot-product matrix of similarities); (2) divide by so the numbers don't blow up; (3) softmax each row so the weights for one token sum to 1; (4) multiply by to take a weighted average of values. Each token's new representation is a mixture of every other token, weighted by relevance.
Multi-Head Self-Attention (MSA)
Running attention in parallel across multiple representation subspaces.
- — number of attention heads (12 in ViT-Base)
- — per-head projection matrices — each head sees a different 64-D subspace
- — output projection — mixes the h head outputs back into D = 768
One attention layer can only learn one kind of relationship. Running heads in parallel lets the model attend to different things simultaneously — e.g. head 1 on nearby texture, head 2 on long-range objects, head 3 on object boundaries. Concatenating their outputs and projecting with gives a single richer representation.
Complete ViT Architecture
From image → patches → 12 encoder blocks → CLS → classification head.
Image 224×224×3
↓ Patch Embed (16×16, stride 16)
↓ + [CLS] + Positional Encoding
[197 × 768]
↓ × 12 Encoder Blocks
[197 × 768]
↓ extract CLS token
[1 × 768]
↓ MLP head
[K classes]| Variant | Layers | Hidden D | Heads | Params |
|---|---|---|---|---|
| ViT-Small | 12 | 384 | 6 | 22M |
| ViT-Base | 12 | 768 | 12 | 86M |
| ViT-Large | 24 | 1024 | 16 | 307M |
| ViT-Huge | 32 | 1280 | 16 | 632M |
Transformer Encoder Block — Inside One Block
Two residual sub-blocks: Pre-LN → MSA → ⊕, Pre-LN → FFN → ⊕.
The equations below mention six things. Here is what each one is and why it's there — read this first, then the maths will look obvious.
- x — the token tensor. A matrix of shape [197 × 768]: one row per token. Everything inside the block reads and writes this matrix.
- LN — Layer Normalisation. A tiny "auto-volume-knob" that, for each token's 768-number vector, subtracts the mean and divides by the standard deviation, then re-scales with learnable parameters γ and β. Why? Without it, the numbers drift larger and larger as we stack blocks, eventually exploding or saturating activations. LN keeps every token in a sane range. "Pre-LN" just means we apply LN before MSA/FFN (more stable than the original "Post-LN" order from the 2017 paper).
- MSA — Multi-Head Self-Attention. The "patches talk to each other" step from §3 and §4, run with h=12 parallel heads. Output shape is the same as input ([197 × 768]) so we can add it back. Mixes information across tokens.
- FFN — Feed-Forward Network. A small two-layer MLP applied independently to each token: expand 768→3072, apply GELU, project back 3072→768. Think of it as a per-token "refine my representation now that I know what my neighbours said". Does NOT mix tokens.
- GELU — Gaussian Error Linear Unit. A smooth activation function. Where ReLU is a sharp corner at 0 (), GELU bends smoothly: it's roughly where is the Gaussian cumulative. Smoother → better gradient flow → trains slightly better than ReLU in Transformers.
- + x (residual / skip connection). The output of each sub-block is added back to its input. This is the "gradient highway" — see §7 for the proof. Visually, in the architecture figure, these are the curved arrows that bypass the sub-block.
— normalise, let tokens attend to each other, then add the original back.
— normalise again, refine each token independently, add the previous state back.
One-line summary: a block = "normalise, mix, add" then "normalise, refine, add". Repeat 12 times.
- — layer normalisation (see below)
- — multi-head self-attention from §4
- — per-token feed-forward network (below)
- — residual / skip connection — the gradient highway
Each block has two residual sub-blocks: one for mixing tokens (MSA), one for transforming each token independently (FFN). The on the end of each line is what lets gradients flow back through 12, 24, even 32 stacked blocks without dying.
- — mean and variance of the D = 768 features of that token
- — learnable per-feature scale and shift
- — small constant (e.g. 10^{-5}) for numerical stability
Centres and scales each token's features so the activations stay in a healthy range. Without this, deep stacks drift towards exploding or vanishing values.
- — expansion from 768 → 3072
- — projection back 3072 → 768
- — Gaussian Error Linear Unit — a smooth ReLU
The FFN acts on each token independently (no cross-token mixing). Think of it as a small per-token MLP that decides "given what this token now represents, transform it like so". MSA mixes tokens; FFN refines them.
- Pre-LN (normalise before MSA/FFN) is more stable than Post-LN for deep stacks.
- MSA mixes tokens; FFN does not — FFN acts independently per token, a learned "memory" lookup.
- GELU (Gaussian Error Linear Unit) smoothly gates the input; outperforms ReLU in Transformers.
- Two residuals per block × 12 blocks = 24 gradient highways from output back to input.
Vanishing Gradient — Why Residuals Save ViT
A short proof that the +x in x' = F(x)+x guarantees gradient flow.
- — activations entering block \ell
- — the block's non-linear transform (MSA or FFN here)
- — identity matrix from the skip connection
- — total number of blocks (12 for ViT-Base)
Even if (the classic vanishing-gradient problem), the term ensures every factor in the product is at least the identity. So the gradient can never collapse to zero before reaching the early layers — the skip connection is literally a gradient highway.
Combined with LayerNorm (keeps activations at μ=0, σ=1) and the scaling inside attention, ViT trains stably to 12, 24, even 32 layers deep — something CNNs only achieved after the ResNet (2015) skip-connection breakthrough.
- Jacobian (the term) = "how much the block's output wiggles when its input wiggles". A small Jacobian = the block barely passes information back. Adding the identity I forces it to pass something through, always.
- ℒ (script L) is the loss function — the number we're minimising. Its gradient w.r.t. the early activations is what training needs.
ViT Variants
Five families you should know — when to reach for each.
| Variant | Year | Key idea | Best for |
|---|---|---|---|
| DeiT | 2021 | Data-efficient ViT; distillation token from a CNN teacher. | Small/medium datasets — no JFT-300M pre-training needed. |
| Swin | 2021 | Shifted-window attention; hierarchical, linear complexity. | Dense prediction (detection, segmentation), high-res imagery. |
| BEiT | 2021 | BERT-style masked image modelling self-supervised pre-training. | Label-scarce domains (medical, satellite). |
| CaiT | 2021 | Class-Attention layers; LayerScale enables very deep ViTs. | Maximum ImageNet accuracy at fixed compute. |
| MaxViT | 2022 | Block-attention + grid-attention; hybrid with conv stem. | Long-range + local features in one network — strong all-rounder. |
ViT vs CNN — When to Use Which
| Situation | Prefer |
|---|---|
| < 50k labelled images, no pre-training | CNN (ResNet/EfficientNet) |
| Large pre-training corpus available (ImageNet-21k, JFT) | ViT |
| High-resolution dense prediction (segmentation) | Swin / MaxViT |
| Multi-modal (image + text) | ViT (shares Transformer with NLP) |
| Edge device, tight FLOPs budget | CNN or MobileViT |
| Satellite / medical, self-supervised pre-training | BEiT / MAE-pretrained ViT |
Equations cheat sheet
text1Patch embedding: z_0 = [x_cls; x_p^1 E; x_p^2 E; … ; x_p^N E] + E_pos2Attention head: A_i = softmax(Q_i K_iᵀ / √d_k) V_i3MSA: MSA(x) = Concat(A_1,…,A_h) W_O4Encoder block: x' = MSA(LN(x)) + x ; z = FFN(LN(x')) + x'5FFN: FFN(x) = GELU(x W_1 + b_1) W_2 + b_26Classification: ŷ = softmax( LN(z_L^{cls}) · W_head )
Skim this once, then refer back whenever a symbol looks unfamiliar. Each term is also re-defined the first time it appears in the text.