Code Architecture: Vision Transformer (ViT) from Scratch
Overview
The notebook implements a miniature Vision Transformer from scratch using PyTorch, following the architecture described in “An Image is Worth 16x16 Words” (Dosovitskiy et al., 2020). The model is trained on a subset of CIFAR-10 to demonstrate the core mechanics of patch embedding, positional encoding, multi-head self-attention, and the [CLS] token classification paradigm.
Section-by-Section Breakdown
1. Setup & Imports
- Installs
torch,torchvision,matplotlib,numpy - Sets random seeds for reproducibility
- Detects CUDA device (GPU) or falls back to CPU
2. Data Loading & Preprocessing
- Uses CIFAR-10 (32×32 RGB images, 10 classes) from torchvision
- Applies standard normalization with horizontal flip augmentation
- Uses a 20K-image subset of the training set for faster training
- Creates train/test DataLoaders with batch_size=256
- Simplification: Uses a CIFAR-10 subset instead of ImageNet-21k or JFT-300M. The original paper pre-trains on massive datasets; we train from scratch on a small subset to demonstrate the architecture within Kaggle’s GPU/time constraints.
3. Patch Embedding Layer
- Class
PatchEmbedding: Takes input images of shape (B, C, H, W), splits into non-overlapping P×P patches, flattens, and linearly projects to D dimensions - Uses
nn.Conv2dwith kernel_size=stride=P as an efficient patch extraction + projection - Output shape: (B, N, D) where N = (H/P) × (W/P)
- Key shapes: Input (B, 3, 32, 32) → with P=4 → N=64 patches → Output (B, 64, 128)
4. Multi-Head Self-Attention (MSA)
- Class
MultiHeadSelfAttention: Standard scaled dot-product attention with multiple heads - Input: (B, N+1, D) — includes the [CLS] token
- Projects to Q, K, V via a single QKV linear layer, splits into
num_headsheads - Attention: softmax(QK^T / sqrt(d_k)) V
- Concatenates heads and projects back to D
- Stores attention weights for visualization
5. Transformer Encoder Block
- Class
TransformerEncoderBlock: LayerNorm → MSA → residual; LayerNorm → MLP(GELU) → residual - MLP: two linear layers with GELU activation, hidden dim = 2×D (reduced from 4×D for faster training)
- Pre-LayerNorm architecture (as in the paper)
6. Vision Transformer
- Class
VisionTransformer: Assembles all componentsPatchEmbedding: image → patch tokenscls_token: learnable (1, 1, D) parameter prepended to sequencepos_embed: learnable (1, N+1, D) positional embeddingnn.SequentialofTransformerEncoderBlock× L layersLayerNormafter encoderLinear(D, num_classes)classification head on [CLS] token output
get_attention_maps(): extracts CLS-to-patch attention from each layer for visualization
7. Training Loop
- Cross-entropy loss, AdamW optimizer, cosine annealing LR scheduler
- Trains for 10 epochs on 20K images
- Tracks and plots training/test loss and accuracy curves
8. Patch Attention Visualization
- Extracts attention weights from the [CLS] token to all patch tokens
- Reshapes attention weights to spatial grid (8, 8)
- Upsamples to original image size and overlays as heatmap
- Visualizes which image patches the model attends to across layers
9. Positional Embedding Similarity
- Computes cosine similarity between learned positional embeddings
- Shows that nearby patches develop similar embeddings (spatial structure emerges)
Key Functions/Classes
| Component | Purpose | Input → Output |
|---|---|---|
PatchEmbedding | Split image into patches, project to D dims | (B,C,H,W) → (B,N,D) |
MultiHeadSelfAttention | Scaled dot-product attention with H heads | (B,N+1,D) → (B,N+1,D) |
TransformerEncoderBlock | One encoder layer with MSA + MLP | (B,N+1,D) → (B,N+1,D) |
VisionTransformer | Full ViT model | (B,C,H,W) → (B,num_classes) |
Deliberate Simplifications vs Full Paper
- Dataset: CIFAR-10 20K subset vs ImageNet-21k/JFT-300M (14M–300M images). No pre-training; trained from scratch.
- Model size: ViT-Tiny (L=4, D=128, heads=4, ~0.5M params) vs ViT-Base (L=12, D=768, 86M params). Scaled down for GPU memory and time constraints.
- Patch size: P=4 (for 32×32 images) vs P=16 (for 224×224 images in the paper). Same principle, adjusted for smaller resolution.
- MLP ratio: 2×D vs 4×D in the paper, for faster training.
- No pre-training/fine-tuning: The paper’s key contribution is pre-training on massive datasets then fine-tuning. We train end-to-end on CIFAR-10 subset only.
- Epochs: 10 vs hundreds in the paper. Demonstrates architecture, not SOTA results.
- 1D positional embeddings: Same as the paper; no 2D-aware variants explored.
Data Flow
Image (B, 3, 32, 32)
→ PatchEmbedding: (B, 64, 128) [P=4, N=64, D=128]
→ prepend CLS token: (B, 65, 128)
→ add positional embedding: (B, 65, 128)
→ Transformer Encoder × 4: (B, 65, 128)
→ LayerNorm: (B, 65, 128)
→ extract CLS: (B, 128)
→ Linear classifier: (B, 10)
→ softmax → prediction