A Vision Transformer (ViT) applies a standard Transformers encoder to images by cutting the image into fixed size patches and treating each patch as a token. It shows that convolutions are not necessary for strong image recognition given enough data.
From Image to Tokens
The core trick is patchify: split an image of shape into a grid of non overlapping patches, flatten each patch, and linearly project it to the model dimension . This produces tokens.
- is the shared patch embedding projection.
- is a learnable class token prepended to the sequence; its final state is the image representation for classification.
- are learnable positional embeddings added because the transformer is otherwise permutation invariant.
Patch Embedding in PyTorch
einops.rearrange expresses patchify in one line, exactly as the ViT paper describes it.
import torch
import torch.nn as nn
from einops import rearrange, repeat
class PatchEmbed(nn.Module):
def __init__(self, img_size=224, patch=16, in_ch=3, dim=768):
super().__init__()
self.p = patch
n_patches = (img_size // patch) ** 2
self.proj = nn.Linear(patch * patch * in_ch, dim)
self.cls = nn.Parameter(torch.zeros(1, 1, dim))
self.pos = nn.Parameter(torch.zeros(1, n_patches + 1, dim))
def forward(self, x):
# x: (b, c, h, w) -> (b, num_patches, patch_dim)
patches = rearrange(
x, "b c (h p1) (w p2) -> b (h w) (p1 p2 c)",
p1=self.p, p2=self.p,
)
tokens = self.proj(patches) # (b, n, dim)
cls = repeat(self.cls, "1 1 d -> b 1 d", b=x.shape[0])
tokens = torch.cat([cls, tokens], dim=1) # prepend class token
return tokens + self.pos # add positionsThe rearrange pattern
b c (h p1) (w p2) -> b (h w) (p1 p2 c)reads the height axis ashpatch-rows each of sizep1, the width aswpatch-cols of sizep2, then flattens the spatial grid into a token axis(h w)and each patch into a feature vector(p1 p2 c). AConv2dwith stride= patchis an equivalent, common alternative.
Transformer Encoder
Each encoder block is pre-norm (LayerNorm) then Self-Attention then MLP, with residuals:
Attention is global from the first layer: every patch can attend to every other patch, unlike a CNN’s local kernel. The class token collects information from all patches; a final linear head on its output produces logits.
Practical Notes
Data hungry
ViTs lack the built in locality and translation equivariance of CNNs, so trained from scratch on small datasets they underperform. They need large scale pretraining (for example JFT-300M) or heavy augmentation and regularization (DeiT) to match or beat CNNs.
- Positional embeddings must be interpolated when changing input resolution, since the number of patches changes.
- Patch size trades resolution for compute: smaller patches mean more tokens and quadratic attention cost.
- Hybrid variants feed CNN feature maps as the patch source, and later models (Swin) reintroduce locality with windowed attention.