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

Hyperparametrar för djupa nät

Kunna prioritera vilka hyperparametrar som spelar roll och sätta upp ett svep.

Förkunskaper

Intuition

Det finns tiotals hyperparametrar. De flesta spelar nästan ingen roll, och att söka över alla är slöseri.

Prioritetsordning, med ungefärlig effektstorlek:

PrioritetParameterEffekt
1Lärhastighetenorm — kan avgöra om modellen lär sig alls
2Batchstorlek (och lr tillsammans med den)stor
3Weight decaymåttlig
4Lärhastighetsschemamåttlig
5Modellstorlek och djupstor, men dyr att söka
6Dropout och augmenteringmåttlig, störst vid lite data
7Optimerarens β-värdenliten — låt vara
8Initialiseringliten med moderna standardval

Regel: sök över 1–4 och lämna resten på standardvärden. Att söka brett över allt ger sämre resultat på samma budget än att söka noggrant över det som betyder något.

Och kom ihåg: bättre data slår nästan alltid bättre hyperparametrar. En timme på datakvalitet ger oftare mer än en dag på svep.

Formellt

Slumpsökning slår rutnätssökning. Bergstra & Bengio (2012) visade varför: med ett rutnät över två parametrar där bara den ena betyder något provar du samma få värden av den viktiga parametern om och om igen. Slumpsökning provar ett nytt värde varje gång.

Rutnät 3×3 (9 körningar)        Slump (9 körningar)
viktig param: 3 unika värden    viktig param: 9 unika värden

Sök i rätt skala. Lärhastighet och weight decay ska samplas log-uniformt, inte uniformt: skillnaden mellan 1e-5 och 1e-4 är lika betydelsefull som mellan 1e-3 och 1e-2.

ParameterSkalaTypiskt intervall
Lärhastighetlog1e-5 – 1e-2
Weight decaylog1e-6 – 1e-1
Dropoutlinjär0 – 0,5
Batchstorleklog₂16 – 512
Antal lagerheltal2 – 12

Batchstorlek och lärhastighet hänger ihop. Ökar du batchen behöver du oftast höja lr. Två tumregler cirkulerar: linjär skalning (η∝B\eta \propto B, bra för SGD på bildnät) och kvadratrotsskalning (η∝B\eta \propto \sqrt{B}, ofta bättre för Adam). Behandla dem som utgångspunkter, inte lagar — men sök inte de två oberoende av varandra, det slösar budget.

Bättre än ren slumpsökning, när budgeten är knapp:

MetodIdé
Successive halving / Hyperbandstarta många, döda de sämsta tidigt, ge resten mer budget
Bayesiansk optimeringmodellera resultatet som funktion av parametrarna och sampla där det ser lovande ut
Population based traininglåt körningar kopiera och mutera varandras parametrar under tiden

Hyperband är oftast den bästa avvägningen mellan enkelhet och effekt.

Fällan: att överanpassa valideringsmängden. Provar du 500 konfigurationer och väljer den bästa på valideringen har du i praktiken tränat på den. Skillnaden mellan bästa och femte bästa är då ofta ren slump. Motmedel: håll en separat testmängd som bara används en gång, och rapportera spridningen över några frön — inte bara den bästa siffran.

Kod

import numpy as np, itertools, json
from pathlib import Path

RUM = {
    "lr":           ("log", 1e-5, 1e-2),
    "weight_decay": ("log", 1e-6, 1e-1),
    "dropout":      ("lin", 0.0, 0.5),
    "batch":        ("log2", 4, 9),          # 16 … 512
}

def sampla(rng):
    ut = {}
    for namn, (skala, lo, hi) in RUM.items():
        if skala == "log":
            ut[namn] = float(10 ** rng.uniform(np.log10(lo), np.log10(hi)))
        elif skala == "log2":
            ut[namn] = int(2 ** rng.integers(lo, hi + 1))
        else:
            ut[namn] = float(rng.uniform(lo, hi))
    return ut

def hyperband(trana, budget_min=1, budget_max=27, eta=3, frö=0):
    """Starta många korta körningar, behåll de bästa, ge dem mer budget."""
    rng = np.random.default_rng(frö)
    n = int(eta ** np.floor(np.log(budget_max / budget_min) / np.log(eta)))
    kandidater = [sampla(rng) for _ in range(n)]
    budget = budget_min
    while len(kandidater) > 1:
        resultat = [(trana(k, epoker=budget), k) for k in kandidater]
        resultat.sort(key=lambda r: r[0])                 # lägre förlust är bättre
        behall = max(1, len(kandidater) // eta)
        print(f"  budget {budget:>3} epoker: {len(kandidater)} → {behall} kandidater, "
              f"bäst {resultat[0][0]:.4f}")
        kandidater = [k for _, k in resultat[:behall]]
        budget *= eta
    return kandidater[0]

# Rapportera spridning, inte bara bästa siffran
def med_spridning(trana, params, fron=(0, 1, 2)):
    v = [trana(params, frö=f) for f in fron]
    return {"medel": round(float(np.mean(v)), 4), "std": round(float(np.std(v)), 4),
            "alla": [round(float(x), 4) for x in v]}

# Logga allt — annars går sökningen inte att återanvända
def logga(kor, params, resultat, fil="svep.jsonl"):
    with Path(fil).open("a", encoding="utf-8") as f:
        f.write(json.dumps({"kor": kor, **params, **resultat}, ensure_ascii=False) + "\n")

med_spridning är det som skiljer ett användbart svep från ett vilseledande. Är standardavvikelsen mellan frön 0,01 och skillnaden mellan bästa och femte bästa konfiguration 0,005, har du inte hittat en bättre konfiguration — du har hittat ett lyckosamt frö.

Behärskning innebär

  • Prioriterar hyperparametrar efter förväntad effekt
  • Sätter upp ett svep med rätt söktyp och skala
  • Undviker att överanpassa valideringsmängden

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

Källor

Alla källor och licenser