Vad ser nätet? Bilder genom lagren
Kunna följa en bild genom ett nät och tolka enkla aktiveringar.
Förkunskaper
Intuition
En bild går genom nätet och förvandlas steg för steg. Vad varje steg producerar går att titta på.
| Lager | Vad som händer | Storlek (MNIST) |
|---|---|---|
| Indata | råa pixlar | 1 × 28 × 28 |
| Conv 1 + ReLU | kanter och ljusövergångar | 16 × 28 × 28 |
| Pooling | halverar upplösningen | 16 × 14 × 14 |
| Conv 2 + ReLU | hörn, bågar, mönsterbitar | 32 × 14 × 14 |
| Pooling | 32 × 7 × 7 | |
| Platta ut | gör om till en vektor | 1 568 |
| Fullt kopplat | kombinerar till klassbevis | 10 |
Hierarkin är det centrala: de första lagren ser små lokala mönster, de senare kombinerar dem till större. Kanter → hörn → former → delar → objekt. Ingen har programmerat den ordningen; den uppstår av att varje lager bara ser utdata från det förra.
Två saker blir mindre, en blir större. Höjd och bredd krymper, medan antalet kanaler växer. Nätet byter alltså var något är mot vad det är.
Kod
import torch, torch.nn as nn
nat = nn.Sequential(
nn.Conv2d(1, 16, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2), # 28 → 14
nn.Conv2d(16, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2), # 14 → 7
nn.Flatten(), nn.Linear(32 * 7 * 7, 10),
)
bild = torch.randn(1, 1, 28, 28)
x = bild
for i, lager in enumerate(nat):
x = lager(x)
andel_aktiva = float((x > 0).float().mean()) if x.dim() == 4 else float("nan")
print(f"{i} {lager.__class__.__name__:<10} {tuple(x.shape)} aktiva {andel_aktiva:.2f}")
# 0 Conv2d (1, 16, 28, 28) aktiva 0.50
# 1 ReLU (1, 16, 28, 28) aktiva 0.50 ← hälften nollställs
# 2 MaxPool2d (1, 16, 14, 14) aktiva 0.76
# 3 Conv2d (1, 32, 14, 14) aktiva 0.50
# ...
# 7 Linear (1, 10)
# Titta på enskilda kanaler efter första lagret
med_hook = {}
nat[0].register_forward_hook(lambda m, i, o: med_hook.update(conv1=o.detach()))
nat(bild)
a = med_hook["conv1"][0] # (16, 28, 28)
for k in range(3):
print(f"kanal {k}: medel {a[k].mean():+.3f} max {a[k].max():.3f}")
# Vilken kanal reagerar starkast på just den här bilden?
print("starkast kanal:", int(a.flatten(1).max(dim=1).values.argmax()))
register_forward_hook är verktyget för att titta inuti ett nät utan att ändra det. Den anropas varje gång lagret kör och får både in- och utdata — precis det du behöver för att visualisera aktiveringar.
Andelen aktiva enheter efter ReLU är värd att hålla ögonen på. Är den nära 0 i ett lager har de neuronerna «dött» och bidrar inget — ett tecken på för hög lärhastighet. Ligger den runt 0,3–0,6 är allt normalt.
Interaktivt
Titta på ett riktigt nät utan att skriva kod. Öppna CNN Explainer och gör följande i ordning:
- Klicka på en bild. Följ den genom lagren och titta på hur många kanaler som finns i varje.
- Klicka på en ruta i första conv-lagret. Du ser vilken 3×3-kärna som användes och hur den skjuts över bilden.
- Jämför två kanaler i första lagret. De reagerar på olika saker — ofta kanter i olika riktningar.
- Gå till sista conv-lagret. Bilderna är nu små och abstrakta; du kan inte längre se vad de föreställer.
- Byt indatabild och se vilka kanaler som ändras mest.
Tre frågor att svara på medan du tittar:
- Varför blir bilderna mindre men fler för varje lager?
- Varför är de sista aktiveringarna omöjliga att tolka som bilder?
- Om nätet klassar fel — i vilket lager skulle du börja leta?
Svaret på den sista är oftast «sist»: felet syns lättast i det fullt kopplade lagret, där du kan se vilka två klasser som konkurrerade. Men orsaken ligger nästan alltid tidigare.
Behärskning innebär
- Följer en bilds väg genom lagren
- Tolkar vad aktiveringar i olika lager representerar
- Förklarar varför djupare lager blir mer abstrakta
Logga in för att göra övningarna och bygga upp din behärskning.
Källor
- Dive into Deep Learning (CC BY-SA 4.0) — CC BY-SA 4.0
- PyTorch — tutorials (BSD-3) — BSD-3-Clause
- CNN Explainer (Georgia Tech, MIT-licens) — MIT