Autoencoders
Kunna träna en autoencoder och tolka det latenta rummet.
Förkunskaper
Intuition
En autoencoder tränas att återskapa sin egen indata — vilket låter meningslöst tills man ser flaskhalsen:
indata (784) → encoder → latent (32) → decoder → utdata (784)
↑
flaskhalsen
Eftersom all information måste passera genom 32 tal tvingas nätet att komprimera: behålla det väsentliga, kasta resten. Det som återstår i flaskhalsen är en inlärd representation.
Förlusten är bara avståndet mellan indata och utdata — ingen märkning behövs. Det är självövervakad inlärning i sin enklaste form.
Kopplingen till PCA: en autoencoder med linjära lager och kvadratisk förlust hittar samma underrum som PCA. Det är med olinjäriteterna den blir intressant — då kan den följa krökta grenrör som PCA inte kan.
Formellt
Fyra användningar, i fallande ordning efter hur ofta de faktiskt är rätt val:
| Användning | Hur | Kommentar |
|---|---|---|
| Avbrusning | träna på (brusig, ren)-par | fungerar mycket bra |
| Anomalidetektion | högt rekonstruktionsfel = avvikare | vanligaste produktionsanvändningen |
| Dimensionsreduktion | använd flaskhalsen som features | ofta slagen av enklare metoder |
| Generering | sampla i det latenta rummet | kräver VAE — se nedan |
Varför en vanlig autoencoder inte kan generera. Det latenta rummet har inga garantier: mellan två tränade punkter kan det finnas hål där decodern producerar nonsens. Samplar du en slumpmässig punkt får du oftast skräp.
En VAE löser det genom att koda varje indata till en fördelning i stället för en punkt, och straffa avvikelsen från en normalfördelning (KL-termen). Det gör rummet sammanhängande och samplingsbart — till priset av suddigare rekonstruktioner.
Varianter:
| Variant | Idé |
|---|---|
| Denoising | lägg till brus i indata, kräv ren utdata |
| Sparse | straffa antalet aktiva latenta enheter |
| Contractive | straffa känsligheten för små indataändringar |
| VAE | probabilistisk latent, KL-straff |
| Masked (MAE) | dölj 75 % av bilden, återskapa den |
Den sista är den moderna varianten: masked autoencoders är en av de mest framgångsrika förträningsmetoderna för bildmodeller.
Glesa autoencoders i tolkningsforskning är en nutida tillämpning värd att känna till: man tränar en bredare men gles autoencoder på en språkmodells aktiveringar, för att bryta upp polysemantiska neuroner i monosemantiska riktningar. Flaskhalsen är där inte kompression utan gleshet.
Anomalidetektion i praktiken: träna bara på normala exempel, mät rekonstruktionsfelet, sätt tröskeln på till exempel 99:e percentilen av träningsfelet. Avvikare rekonstrueras dåligt eftersom nätet aldrig lärt sig dem. Fallgropen är att en tillräckligt stor autoencoder lär sig rekonstruera allt, inklusive avvikarna — flaskhalsen måste vara verkligt trång.
Kod
import torch, torch.nn as nn
class Autoencoder(nn.Module):
def __init__(self, in_dim=784, latent=32):
super().__init__()
self.encoder = nn.Sequential(
nn.Linear(in_dim, 256), nn.ReLU(),
nn.Linear(256, 64), nn.ReLU(),
nn.Linear(64, latent),
)
self.decoder = nn.Sequential(
nn.Linear(latent, 64), nn.ReLU(),
nn.Linear(64, 256), nn.ReLU(),
nn.Linear(256, in_dim), nn.Sigmoid(),
)
def forward(self, x):
z = self.encoder(x)
return self.decoder(z), z
# Denoising: brus in, rent ut
def trana_denoising(modell, dataloader, opt, brus=0.3, epoker=10):
for _ in range(epoker):
for x, _ in dataloader:
x = x.flatten(1)
xb = (x + brus * torch.randn_like(x)).clamp(0, 1)
ut, _ = modell(xb)
loss = nn.functional.mse_loss(ut, x) # jämför mot den RENA bilden
opt.zero_grad(); loss.backward(); opt.step()
# Anomalidetektion: träna bara på normala exempel
def anomalitroskel(modell, normal_loader, percentil=99):
fel = []
modell.eval()
with torch.no_grad():
for x, _ in normal_loader:
x = x.flatten(1)
ut, _ = modell(x)
fel += ((ut - x) ** 2).mean(dim=1).tolist()
return float(torch.tensor(fel).quantile(percentil / 100))
def ar_anomali(modell, x, troskel):
with torch.no_grad():
ut, _ = modell(x.flatten(1))
return ((ut - x.flatten(1)) ** 2).mean(dim=1) > troskel
# Kontrollera flaskhalsen: för stor latent → nätet lär sig kopiera allt
for latent in (2, 8, 32, 128, 784):
m = Autoencoder(latent=latent)
komprimering = latent / 784
print(f"latent {latent:>3}: {komprimering:.1%} av indata"
+ (" ← ingen flaskhals alls" if latent >= 784 else ""))
# Linjär autoencoder ≈ PCA — kontrollera själv
import numpy as np
X = np.random.default_rng(0).normal(size=(500, 20)) @ np.random.default_rng(1).normal(size=(20, 20))
Xc = X - X.mean(0)
U, s, Vt = np.linalg.svd(Xc, full_matrices=False)
print("PCA behåller", np.round((s[:5]**2 / (s**2).sum()).sum(), 3), "av variansen med 5 komponenter")
Raden med latent=784 är poängen med flaskhalsen: är den latenta dimensionen lika stor som indata kan nätet lära sig identitetsavbildningen och har inte lärt sig något alls. För anomalidetektion är det särskilt förrädiskt, eftersom en tillräckligt stor autoencoder rekonstruerar även avvikarna perfekt.
Behärskning innebär
- Tränar en autoencoder
- Tolkar det latenta rummet
- Vet när en autoencoder är rätt verktyg
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
- arXiv — Masked Autoencoders Are Scalable Vision Learners — arXiv (öppen åtkomst; licens per artikel)