Hoppa till innehållet
AI-grafen
E· Universitetdeep-learning· ca 60 min· utvecklande· verifierad 2026-09-20

Träningsdiagnostik

Kunna läsa loss-kurvor, gradientnormer och lärhastighetssvep och avgöra vad som är fel.

Förkunskaper

Intuition

När träningen inte fungerar finns det en handfull orsaker, och de ser olika ut i kurvorna. Att kunna avläsa dem sparar dagar.

MönsterDiagnosÅtgärd
Båda kurvorna faller och planar ut ihopsuntklart
Träning faller, validering vänder uppåtöveranpassningtidigt stopp, mer data, regularisering
Båda planar ut högtunderanpassningstörre modell, längre träning, bättre features
Kurvan hoppar viltför hög lärhastighet eller för liten batchsänk lr, öka batch
Kurvan är helt platt från startinget lär sig allsse nedan
Kurvan går till NaNexplosion eller division med nollgradientklippning, lägre lr, kontrollera data

Den platta kurvan är den mest förvirrande. Den beror nästan alltid på något trivialt: glömd optimizer.step(), requires_grad=False någonstans, lärhastighet på 0, eller att förlusten beräknas på fel tensor.

Regel nummer ett i all felsökning: få modellen att överanpassa 10 exempel först. Klarar den inte att memorera tio datapunkter är felet i koden, inte i hyperparametrarna.

Formellt

Storheter att logga från början, inte när det redan gått fel:

StorhetSund nivåVad avvikelsen betyder
Träningsförlustfallerplatt = buggen sitter i koden
Valideringsförlustfaller, sedan planarvänder = överanpassning
Gradientnorm0,1–10→ 0: försvinnande; > 100: explosion
Andel aktiva ReLU0,2–0,7nära 0: döda neuroner
Vikternas normväxer långsamtrusar: för svag weight decay
Förhållandet uppdatering/vikt~1e-3> 1e-2: lr för hög; < 1e-4: för låg

Den sista raden är det mest underskattade måttet. Logga ∥η⋅Δw∥/∥w∥\|\eta \cdot \Delta w\| / \|w\| per lager: den säger direkt om lärhastigheten är rimlig, oberoende av modellens skala.

Lärhastighetssvep (LR range test) hittar rätt storleksordning på några minuter:

  1. Börja på en mycket liten lr (1e-7).
  2. Öka exponentiellt varje batch upp till ~1.
  3. Rita förlusten mot lr i log-skala.
  4. Välj en lr strax före minimum — typiskt en tiondel av den lr där förlusten är som lägst, eftersom kurvan redan är på väg att bli instabil där.

Felsökningsordningen, i tur och ordning:

#Kontroll
1Kan modellen överanpassa 10 exempel till nära noll förlust?
2Är förlusten vid start rimlig? (ln(antal klasser) för korsentropi)
3Är etiketterna rätt kopplade till indata?
4Är normaliseringen densamma i träning och utvärdering?
5Kör LR-svepet
6Först nu: övriga hyperparametrar

Kontroll 2 är gratis och avslöjar mycket. Med 10 klasser ska den första förlusten vara ungefär ln⁡10=2,30\ln 10 = 2{,}30. Är den 7 är något fel med initialiseringen eller etiketterna; är den 0,1 läcker facit in i modellen.

Kod

import math, torch, torch.nn as nn

# 1. Kan modellen överanpassa 10 exempel? Gör detta ALLTID först.
def kan_overanpassa(modell, x, y, steg=300, lr=1e-3):
    x, y = x[:10], y[:10]
    opt = torch.optim.Adam(modell.parameters(), lr=lr)
    for _ in range(steg):
        loss = nn.functional.cross_entropy(modell(x), y)
        opt.zero_grad(); loss.backward(); opt.step()
    return float(loss)     # ska bli < 0.01; annars sitter felet i koden

# 2. Är startförlusten rimlig?
def kontrollera_start(modell, x, y, n_klasser):
    with torch.no_grad():
        f = float(nn.functional.cross_entropy(modell(x), y))
    vantat = math.log(n_klasser)
    print(f"start {f:.3f}, väntat ~{vantat:.3f}")
    if abs(f - vantat) > 0.5:
        print("  ⚠ kontrollera initialisering och etiketter")

# 3. Logga det som avslöjar problem
def diagnostik(modell, lr):
    normer = {n: float(p.grad.norm()) for n, p in modell.named_parameters()
              if p.grad is not None}
    total = sum(v**2 for v in normer.values()) ** 0.5
    forhallande = {n: lr * float(p.grad.norm()) / (float(p.norm()) + 1e-12)
                   for n, p in modell.named_parameters() if p.grad is not None}
    return {"grad_norm": round(total, 4),
            "minsta_lager": min(normer, key=normer.get),
            "uppdatering_per_vikt": round(sum(forhallande.values()) / len(forhallande), 6)}

# 4. Lärhastighetssvep
def lr_svep(modell, dataloader, lr_min=1e-7, lr_max=1.0, steg=100):
    opt = torch.optim.Adam(modell.parameters(), lr=lr_min)
    faktor = (lr_max / lr_min) ** (1 / steg)
    lrs, losses = [], []
    it = iter(dataloader)
    for i in range(steg):
        try:
            x, y = next(it)
        except StopIteration:
            it = iter(dataloader); x, y = next(it)
        loss = nn.functional.cross_entropy(modell(x), y)
        opt.zero_grad(); loss.backward(); opt.step()
        lrs.append(opt.param_groups[0]["lr"]); losses.append(float(loss))
        if float(loss) > 4 * losses[0]:          # spårat ur, avbryt
            break
        for g in opt.param_groups:
            g["lr"] *= faktor
    bast = lrs[int(min(range(len(losses)), key=lambda i: losses[i]))]
    return {"lr_vid_minimum": bast, "rekommenderad": bast / 10}

rekommenderad = bast / 10 är tumregeln från Smiths LR range test: vid den lr där förlusten är som lägst är träningen redan på väg att bli instabil, så man tar en storleksordning under.

Behärskning innebär

  • Diagnosticerar ur loss-kurvan
  • Loggar och tolkar gradientnormer
  • Kör ett lärhastighetssvep

Logga in för att göra övningarna och bygga upp din behärskning.

Källor

Alla källor och licenser