CNN-arkitekturer: LeNet till ResNet
Kunna förklara hur arkitekturerna utvecklats och varför ResNet blev standard.
Förkunskaper
- EFaltningsnät (CNN)krävs
- EResidualkopplingarkrävs
Intuition
CNN-arkitekturernas historia är en serie svar på konkreta problem.
| År | Arkitektur | Nyhet | Problem den löste |
|---|---|---|---|
| 1998 | LeNet-5 | faltning + pooling, 7 lager | handskrivna siffror |
| 2012 | AlexNet | ReLU, dropout, GPU, augmentering | ImageNet — halverade felet |
| 2014 | VGG | bara 3×3-kärnor, 16–19 lager | enkelhet och djup |
| 2014 | Inception | parallella kärnstorlekar, 1×1 | beräkningseffektivitet |
| 2015 | ResNet | residualkopplingar, 152 lager | djupet självt |
| 2017 | MobileNet | djupseparerbar faltning | mobil hårdvara |
| 2019 | EfficientNet | balanserad skalning av djup/bredd/upplösning | bästa kvalitet per FLOP |
| 2022 | ConvNeXt | CNN med transformer-recept | konkurrera med ViT |
Två insikter återkommer:
- Små kärnor staplade slår stora. Två 3×3-lager har samma receptiva fält som ett 5×5 men färre parametrar (18 mot 25 per kanalpar) och två olinjäriteter i stället för en.
- 1×1-faltning är gratis dimensionsreduktion. Den blandar kanaler utan att röra rumsliga dimensioner, och används för att krympa kanalantalet före dyra operationer.
Formellt
Varför ResNet blev vändpunkten. Före 2015 var djupet ett problem: ett 56-lagers nät presterade sämre än ett 20-lagers, och inte på grund av överanpassning utan optimering. Residualkopplingen gjorde identitetsavbildningen trivial att lära och gav gradienten en oskalad väg genom nätet.
Effekten var omedelbar: 152 lager, bättre än allt tidigare, och principen spred sig till i princip varje djup arkitektur som byggts sedan dess — inklusive transformern.
Beräkningskostnad per lager:
Det förklarar tre designval:
| Val | Effekt på formeln |
|---|---|
| Nedsampla tidigt | faller fyrfaldigt per steg |
| 1×1 före 3×3 (flaskhals) | i den dyra termen minskar |
| Djupseparerbar faltning | blir |
Den sista är MobileNets idé och ger 8–9× färre beräkningar för 3×3-faltning med många kanaler.
Läget i dag:
| Behov | Val |
|---|---|
| Standardval för bild | förtränad ResNet eller ConvNeXt, finjusterad |
| Litet dataset | förtränad modell — träna aldrig från grunden |
| Mobil eller inbäddad | MobileNet, EfficientNet-Lite |
| Mycket stor data | ViT eller hybrid |
| Segmentering, detektion | ryggrad + uppgiftsspecifikt huvud |
Den viktigaste praktiska slutsatsen är att arkitekturvalet sällan är det som avgör. Förträning, dataaugmentering och datakvalitet ger nästan alltid mer än att byta från en modern arkitektur till en annan — skillnaden mellan ResNet-50 och ConvNeXt-T på ett typiskt finjusteringsproblem är ofta mindre än skillnaden mellan bra och dålig augmentering.
Kod
import torch, torch.nn as nn
# 1. Två 3×3 slår ett 5×5: samma receptiva fält, färre parametrar, fler olinjäriteter
C = 64
en_5x5 = nn.Conv2d(C, C, 5, padding=2, bias=False)
tva_3x3 = nn.Sequential(nn.Conv2d(C, C, 3, padding=1, bias=False), nn.ReLU(),
nn.Conv2d(C, C, 3, padding=1, bias=False))
for namn, m in (("ett 5×5", en_5x5), ("två 3×3", tva_3x3)):
print(f"{namn:<9} {sum(p.numel() for p in m.parameters()):>8,} parametrar")
# ett 5×5 102,400 parametrar
# två 3×3 73,728 parametrar
# 2. Flaskhalsblocket: 1×1 ner, 3×3, 1×1 upp
class Flaskhals(nn.Module):
def __init__(self, kanaler, krymp=4):
super().__init__()
m = kanaler // krymp
self.f = nn.Sequential(
nn.Conv2d(kanaler, m, 1, bias=False), nn.BatchNorm2d(m), nn.ReLU(),
nn.Conv2d(m, m, 3, padding=1, bias=False), nn.BatchNorm2d(m), nn.ReLU(),
nn.Conv2d(m, kanaler, 1, bias=False), nn.BatchNorm2d(kanaler),
)
nn.init.zeros_(self.f[-1].weight) # startar som identitet
def forward(self, x):
return torch.relu(x + self.f(x))
class Enkel(nn.Module):
def __init__(self, kanaler):
super().__init__()
self.f = nn.Sequential(
nn.Conv2d(kanaler, kanaler, 3, padding=1, bias=False), nn.BatchNorm2d(kanaler),
nn.ReLU(),
nn.Conv2d(kanaler, kanaler, 3, padding=1, bias=False), nn.BatchNorm2d(kanaler),
)
def forward(self, x):
return torch.relu(x + self.f(x))
for namn, m in (("enkelt block", Enkel(256)), ("flaskhals", Flaskhals(256))):
print(f"{namn:<14} {sum(p.numel() for p in m.parameters()):>9,} parametrar")
# enkelt block 1,180,672 parametrar
# flaskhals 70,400 parametrar ← 17× färre, samma djup
# 3. Djupseparerbar faltning (MobileNet)
def separerbar(cin, cut, k=3):
return nn.Sequential(
nn.Conv2d(cin, cin, k, padding=k // 2, groups=cin, bias=False), # per kanal
nn.BatchNorm2d(cin), nn.ReLU(),
nn.Conv2d(cin, cut, 1, bias=False), # blanda kanaler
nn.BatchNorm2d(cut), nn.ReLU(),
)
vanlig = nn.Conv2d(128, 256, 3, padding=1, bias=False)
sep = separerbar(128, 256)
print(f"vanlig 3×3: {sum(p.numel() for p in vanlig.parameters()):>8,}")
print(f"separerbar: {sum(p.numel() for p in sep.parameters()):>8,}")
# vanlig 3×3: 294,912
# separerbar: 34,688 ← ~8× färre
# 4. I praktiken: använd en förtränad modell
from torchvision.models import resnet50, ResNet50_Weights
m = resnet50(weights=ResNet50_Weights.IMAGENET1K_V2)
m.fc = nn.Linear(m.fc.in_features, 10) # byt huvudet mot dina klasser
Behärskning innebär
- Beskriver utvecklingen från LeNet till ResNet
- Förklarar varför varje steg var en förbättring
- Vet vad som gäller i dag
Logga in för att göra övningarna och bygga upp din behärskning.
Källor
- arXiv — Deep Residual Learning for Image Recognition — arXiv (öppen åtkomst; licens per artikel)
- arXiv — A ConvNet for the 2020s (ConvNeXt) — arXiv (öppen åtkomst; licens per artikel)
- PyTorch — tutorials (BSD-3) — BSD-3-Clause