Vision Transformer (ViT)
Kunna förklara patch-embeddings och träna en liten ViT.
Förkunskaper
Intuition
En transformer vill ha en sekvens av tokens. En bild är ett rutnät av pixlar. ViT:s lösning är nästan provocerande enkel:
- Klipp bilden i kvadratiska rutor — 16 × 16 pixlar är standard.
- Platta ut varje ruta till en vektor och kör den genom ett linjärt lager. Nu är varje ruta en «token».
- Lägg till en inlärd positionsembedding, eftersom transformern annars inte vet var rutorna satt.
- Lägg till en extra
[CLS]-token som ska samla ihop helheten. - Kör en helt vanlig transformer-encoder.
- Klassificera utifrån
[CLS]-tokenen.
En 224 × 224-bild med 16 × 16-patchar blir 14 × 14 = 196 tokens. Det är en kort sekvens med språkmodellmått mätt.
Det överraskande resultatet: det fungerar bättre än CNN — men bara med tillräckligt mycket data.
Formellt
Induktiv bias är den centrala skillnaden.
Ett CNN har två antaganden inbyggda i arkitekturen: lokalitet (närliggande pixlar hör ihop) och translationsekvivarians (samma filter överallt). De antagandena är sanna om bilder, och de är gratis — nätet behöver inte lära sig dem.
En ViT har nästan inga antaganden. Attention kopplar vilken patch som helst till vilken annan som helst redan i första lagret. Det betyder att den måste lära sig att närliggande patchar hör ihop — vilket kräver data.
| Träningsdata | Vinnare |
|---|---|
| ~1 000 bilder | CNN, med stor marginal |
| ImageNet-1k (1,3 M) | jämnt, CNN något före utan tricks |
| ImageNet-21k (14 M) | ViT |
| JFT-300M | ViT, tydligt |
Slutsatsen är inte «ViT är bättre» utan att induktiv bias kan ersättas av data — och att det är en dålig affär när data är få. Med litet dataset: använd en förtränad ViT och finjustera, eller ta ett CNN.
Tricks som gör ViT hanterbar på små dataset:
| Trick | Effekt |
|---|---|
| Kraftig augmentering (RandAugment, Mixup, CutMix) | störst enskild effekt |
| Distillation från ett CNN (DeiT) | för in CNN:ets bias via läraren |
| Mindre patchar på små bilder (4 × 4 på CIFAR) | fler tokens, mer lokal information |
| Uppvärmning av lärhastigheten + kraftig weight decay | ViT är känsligare än CNN |
Beräkningskostnaden är kvadratisk i antalet patchar. Fördubblad upplösning ger fyra gånger fler patchar och sexton gånger dyrare attention — därav hierarkiska varianter som Swin, som gör attention lokalt i fönster.
Kod
import torch, torch.nn as nn
class PatchEmbed(nn.Module):
"""Patchning och projektion är samma sak som en faltning med stride = patchstorlek."""
def __init__(self, bild=32, patch=4, kanaler=3, d=192):
super().__init__()
self.n = (bild // patch) ** 2
self.proj = nn.Conv2d(kanaler, d, kernel_size=patch, stride=patch)
def forward(self, x):
return self.proj(x).flatten(2).transpose(1, 2) # (B, n, d)
class ViT(nn.Module):
def __init__(self, n_klasser=10, bild=32, patch=4, d=192, djup=6, huvuden=3):
super().__init__()
self.patch = PatchEmbed(bild, patch, 3, d)
self.cls = nn.Parameter(torch.zeros(1, 1, d))
self.pos = nn.Parameter(torch.randn(1, self.patch.n + 1, d) * 0.02)
lager = nn.TransformerEncoderLayer(d, huvuden, d * 4, dropout=0.1,
batch_first=True, norm_first=True,
activation="gelu")
self.enc = nn.TransformerEncoder(lager, djup)
self.norm = nn.LayerNorm(d)
self.huvud = nn.Linear(d, n_klasser)
def forward(self, x):
z = self.patch(x)
z = torch.cat([self.cls.expand(z.size(0), -1, -1), z], dim=1) + self.pos
return self.huvud(self.norm(self.enc(z))[:, 0])
m = ViT()
print(m(torch.randn(2, 3, 32, 32)).shape) # torch.Size([2, 10])
print(sum(p.numel() for p in m.parameters()) / 1e6, "M parametrar") # ~2.7 M
print("tokens:", m.patch.n + 1) # 65
Två detaljer värda att lägga märke till:
nn.Conv2dmedstride=patchÄR patch-embeddingen. Att klippa ut rutor och projicera dem linjärt är exakt samma operation som en faltning utan överlapp. Det är effektivare och mindre kod.norm_first=True(pre-LN). ViT tränar betydligt stabilare med normalisering före attention än efter — samma erfarenhet som i språkmodeller.
Utan kraftig augmentering kommer den här modellen att överanpassa CIFAR-10 inom några epoker. Det är inte ett fel i koden, utan poängen med avsnittet om induktiv bias.
Behärskning innebär
- Förklarar hur en bild blir en tokensekvens
- Jämför ViT och CNN i induktiv bias och datakrav
- Tränar en liten ViT på ett litet dataset
Logga in för att göra övningarna och bygga upp din behärskning.
Källor
- arXiv — An Image is Worth 16x16 Words (ViT) — arXiv (öppen åtkomst; licens per artikel)
- arXiv — Training data-efficient image transformers (DeiT) — arXiv (öppen åtkomst; licens per artikel)
- Dive into Deep Learning (CC BY-SA 4.0) — CC BY-SA 4.0