21.3 Dropout, súlyregularizálás és augmentáció

21.3.1 Cél, előismeret és induló kérdés

A 21.3. Dropout, súlyregularizálás és augmentáció egység három regularizáló eszközt választ szét: a dropoutot, a súlyregularizálást (weight decay) és az adataugmentációt, valamint tárgyalja a hozzájuk kapcsolódó label smoothingot. A három mechanizmus eltérő helyen avatkozik be: a dropout a modell belső aktivációit zajosítja, a weight decay determinisztikus büntetőtagot ad a célfüggvényhez, az augmentáció pedig a bemeneti eloszlást módosítja. Előismeret a 21.2. Learning rate és ütemezés lépésméret- és ütemezésfogalma, a 7.2. Softmax és többosztályos cross-entropy softmax/cross-entropy célja, valamint a 20.3. Preprocessing, domain shift és súlylicenc input-szerződés. Induló kérdés: adott egy h=(2,−1,3,0)h=(2,-1,3,0) aktivációvektor és pkeep=0,5p_{\mathrm{keep}}=0{,}5 megtartási valószínűség. Ha train módban a maszk m=(1,0,1,1)m=(1,0,1,1), mi az inverted dropout kimenete? Eval módban mi a kimenet, és miért?

21.3.2 Dropout: maszk, inverted scaling és train/eval viselkedés

Legyen az aktivációtensor A∈ℝN×dA\in\mathbb R^{N\times d}, ahol NN a batchméret, dd az adott réteg kimeneti dimenziója. A dropout megtartási valószínűsége pkeep∈(0,1]p_{\mathrm{keep}}\in(0,1], kihagyási valószínűsége pdrop=1−pkeepp_{\mathrm{drop}}=1-p_{\mathrm{keep}}. A dropout maszk M∈{0,1}N×dM\in\{0,1\}^{N\times d} minden eleme egymástól független Bernoulli-eloszlású:

Mni∼Bernoulli(pkeep),ℙ(Mni=1)=pkeep.M_{ni}\sim\mathrm{Bernoulli}(p_{\mathrm{keep}}),\qquad \mathbb P(M_{ni}=1)=p_{\mathrm{keep}}.

A pkeepp_{\mathrm{keep}} tehát annak valószínűsége, hogy egy elemet megtartunk; a dropout rate a pdrop=1−pkeepp_{\mathrm{drop}}=1-p_{\mathrm{keep}} kihagyási valószínűség. A PyTorch nn.Dropout(p) argumentuma a kihagyási valószínűség: p =pdrop=p_{\mathrm{drop}}, és train módban a megtartott elemeket 1/(1−p)=1/pkeep1/(1-p)=1/p_{\mathrm{keep}}-pel szorozza [D12]. A két konvenció összekeverése gyakori hiba; nemszimmetrikus kontroll: pkeep=0,8p_{\mathrm{keep}}=0{,}8-hoz nn.Dropout(0.2) tartozik, a várható nullaarány 20%, a megtartott elemek szorzója 1,251{,}25. A pkeep=1p_{\mathrm{keep}}=1 határeset nn.Dropout(0).

Inverted dropout esetén a kimenetet a maszk mellett pkeepp_{\mathrm{keep}}-pel osztjuk:

Â=1pkeep(M⊙A)=11−pdrop(M⊙A),\hat A=\frac{1}{p_{\mathrm{keep}}}\,(M\odot A)=\frac{1}{1-p_{\mathrm{drop}}}\,(M\odot A),

elemenkénti szorzással. A pkeep=1p_{\mathrm{keep}}=1 határeset az identitás; a pkeep=0p_{\mathrm{keep}}=0 kizárt, mert nullával osztanánk. Az ⊙\odot a Hadamard-szorzat, alakja N×dN\times d.

Tétel (inverted dropout feltételes torzítatlansága). Legyen A∈ℝN×dA\in\mathbb R^{N\times d} rögzített aktivációtensor, és legyenek a maszk elemei független Bernoulli(pkeep)\mathrm{Bernoulli}(p_{\mathrm{keep}}) változók, függetlenek AA-tól. Ekkor minden (n,i)(n,i) indexpárra

𝔼[Âni∣A]=Ani.\mathbb E[\hat A_{ni}\mid A]=A_{ni}.

Bizonyítás. A feltételes várható érték linearitását használjuk. Mivel AA a feltételben rögzített, és MniM_{ni} független tőle:

𝔼[Âni∣A]=𝔼[MnipkeepAni|A]=Anipkeep𝔼[Mni∣A]=Anipkeeppkeep=Ani.\mathbb E[\hat A_{ni}\mid A]=\mathbb E\!\left[\frac{M_{ni}}{p_{\mathrm{keep}}}A_{ni}\,\middle|\,A\right]=\frac{A_{ni}}{p_{\mathrm{keep}}}\,\mathbb E[M_{ni}\mid A]=\frac{A_{ni}}{p_{\mathrm{keep}}}\,p_{\mathrm{keep}}=A_{ni}.

Az utolsó lépés a Bernoulli-eloszlás várható értékét használja. ▫\square

A tétel jelentése: az inverted dropout a train módban sem torzítja el a réteg kimenetének várható értékét, csak zajt ad hozzá. A klasszikus, inverted nélküli dropout train alatt M⊙AM\odot A-t számol, eval alatt pedig pkeepAp_{\mathrm{keep}}A-t; az inverted változat ezt a skálakülönbséget train módba hozza, így eval mód nem igényel külön szorzást.

A train/eval viselkedés tehát: train módban minden forwardnál friss, független maszk generálódik; eval módban a modell a determinisztikus AA aktivációt adja, ami az inverted skálázás miatt éppen a feltételes várható értéknek felel meg. A nn.Module train() és eval() állapota vezérli ezt. A BatchNorm szintén train/eval módot vált, de ott az ok a statisztikakezelés: a normalizáló réteg eval alatt futó átlagot és varianciát használ, a dropout viszont egyszerűen eltávolítja a véletlent. A kettő nem ugyanaz a mechanizmus, és nem szabad összemosni.

A dropout célja heurisztikus: minden neuron kénytelen más neuronokkal együttműködve hasznos reprezentációt adni, mert bármelyik társa véletlenszerűen hiányozhat. Ez redundáns, kevésbé egyedi tanulópéldára támaszkodó reprezentációt ösztönöz. A dropout nem növeli a hipotézisosztály expresszivitását; az approximációs kérdés (9.3. Expresszivitás és approximációs állítások) és a tanítási pálya itt is különválik.

A feltételes variancia egy rövid kiegészítő levezetéssel megmutatja, mekkora zajt visz a dropout a forwardba. Mivel Var(Mni∣A)=pkeep(1−pkeep)\operatorname{Var}(M_{ni}\mid A)=p_{\mathrm{keep}}(1-p_{\mathrm{keep}}),

Var(Âni∣A)=Ani2pkeep2Var(Mni)=1−pkeeppkeepAni2=pdrop1−pdropAni2.\operatorname{Var}(\hat A_{ni}\mid A)=\frac{A_{ni}^2}{p_{\mathrm{keep}}^2}\operatorname{Var}(M_{ni})=\frac{1-p_{\mathrm{keep}}}{p_{\mathrm{keep}}}\,A_{ni}^2=\frac{p_{\mathrm{drop}}}{1-p_{\mathrm{drop}}}\,A_{ni}^2.

Minél kisebb pkeepp_{\mathrm{keep}}, azaz minél nagyobb a PyTorch p argumentuma, annál nagyobb a relatív zaj. Ez magyarázza, miért válik zajossá a train loss dropout alatt.

21.3.3 Weight decay: L2-tag és decoupled változat

A weight decay a loss-hoz adott négyzetes büntetőtag. Legyen 𝒲\mathcal W a regularizálandó súlyparaméterek halmaza; a bias-okat tipikusan kihagyjuk. A teljes célfüggvény

ℒ(θ)=1N∑i=1Nℓi(θ)+λ2∑W∈𝒲∥W∥F2,\mathcal L(\theta)=\frac{1}{N}\sum_{i=1}^{N}\ell_i(\theta)+\frac{\lambda}{2}\sum_{W\in\mathcal W}\|W\|_F^2,

ahol λ≥0\lambda\ge0 a súlycsökkentési együttható, ∥⋅∥F\|\cdot\|_F a Frobenius-norma. A tag gradiense minden W∈𝒲W\in\mathcal W-re λW\lambda W, ezért a sima SGD frissítése

W←W−η(∇WLdata+λW).W\leftarrow W-\eta\bigl(\nabla_W L_{\mathrm{data}}+\lambda W\bigr).

Ez a lépés kis ηλ\eta\lambda relatív arányban zsugorítja a súlyt, ezért nevezik weight decaynek.

Torch környezetben az SGD(..., weight_decay=λ) a fenti L2-tagot valósítja meg. Az Adam és AdamW között azonban lényegi különbség van. Ha a λW\lambda W gradienst az Adam adaptív normalizálásán küldjük keresztül, a regularizáló tag hatása paraméterenként és időben változó; nem egyenlő azzal, mintha a súlyt minden lépésben (1−ηλ)(1-\eta\lambda) tényezővel szoroznánk. A decoupled weight decay ezt a zsugorítást leválasztja az adaptív gradiensfrissítésről:

W←(1−ηλ)W−η⋅(adaptív gradienslépés a data loss alapján).W\leftarrow (1-\eta\lambda)\,W-\eta\cdot(\text{adaptív gradienslépés a data loss alapján}).

A [P02] szerzői ezt a szétválasztást javasolják, és az így kapott AdamW-t mutatják be. A mechanizmusbeli különbség magából az Adam frissítési szabályából és a fenti definícióból levezethető, ezért azt a könyv itt bizonyított állításként kezeli; azt, hogy a decoupled változat mely feladatokon és mennyivel jobb, empirikus állításként nem terjesztjük ki.

A λ\lambda tipikus keresési tartománya gyakorlati heurisztikaként 10−510^{-5} és 10−210^{-2} között szokott lenni, de ez nem tétel: a megfelelő érték adattól és modelltől függ. A PyTorch torch.optim aktuális argumentumait a használt könyvtárverzió dokumentációjához és a telepített implementációhoz kell igazítani [D06].

21.3.4 Label smoothing

A label smoothing a többosztályos tanítási célt lágyítja. Az int64 osztálycímke y∈{0,…,K−1}y\in\{0,\ldots,K-1\} egy-hot vektora ey∈{0,1}Ke_y\in\{0,1\}^K. A simított cél

q=(1−ε)ey+εK𝟏,ε∈[0,1),q=(1-\varepsilon)\,e_y+\frac{\varepsilon}{K}\,\mathbf 1,\qquad \varepsilon\in[0,1),

ahol 𝟏=(1,…,1)∈ℝK\mathbf 1=(1,\ldots,1)\in\mathbb R^K. Ellenőrzés: az elemek nemnegatívak, és

∑k=1Kqk=(1−ε)+ε⋅KK=1,\sum_{k=1}^{K} q_k=(1-\varepsilon)+\varepsilon\cdot\frac{K}{K}=1,

tehát qq valószínűségeloszlás. A cross-entropy cél −∑kqklogsk-\sum_k q_k\log s_k, ahol s=softmax(z)s=\mathrm{softmax}(z) a logitsokból számolt predikció. Az ε=0\varepsilon=0 határeset a szokásos cross-entropy az int64 cél felett; a modell kimenete továbbra is logits, a [D07] loss-szerződésnek megfelelően.

A smoothing hatása heurisztikus: a degenerált egy-hot célhoz tartozó veszteség akkor lenne nulla, ha a helyes logit minden másik fölé korlátlanul kinőne. A simított cél ezt megakadályozza, mert a helyes osztályhoz tartozó qy=1−ε+ε/K<1q_y=1-\varepsilon+\varepsilon/K<1, a többi osztályra pedig ε/K>0\varepsilon/K>0; a minimumhoz a logitoknak véges különbséget kell tartaniuk. Ez gyakran nyugodtabb kalibrációt ad, de nem bizonyított, hogy minden adatkészleten javít; heurisztikaként kell kezelni. A torch CrossEntropyLoss újabb kiadása label_smoothing argumentumot is kínál; a fogalom implementálásához azonban elég a fenti képlet, így a levezetés nem függ az API-tól.

21.3.5 Data augmentation

A data augmentation a tanítási bemenetek véletlen, címkemegőrző transzformációja. A feltétel a label-megőrzés: a transzformált képhez ugyanaz az yy cél tartozik, mint az eredetihez. Ez a 22.2. Címkemegőrző transzformációk szerződés közvetlen alkalmazása. GlyphLab osztályozásnál a címke az orientáció, ezért például enyhe eltolás, kis Gauss-zaj vagy fényességskálázás megengedett, a 90∘90^\circ-os forgatás viszont nem címkemegőrző: a vertical és horizontal osztályt felcseréli. A diagonal osztály ráadásul a generátorszerződés szerint csak az egyik ferde irányt tartalmazza, így a tükrözés is új célértelmezést igényelne.

Az augmentáció tehát a data oldalon bővíti az effektív mintát; a dropout a modell belső aktivációit zajosítja; a weight decay determinisztikus paraméterbüntetést ad. Validációs és teszt spliten a kiértékeléshez nem alkalmazunk véletlen augmentációt, mert a metrikának reprodukálhatónak és a kiértékelt modellhez kötöttnek kell lennie.

21.3.6 Kézi számpélda és minimális kód

Vegyük az induló kérdés h=(2,−1,3,0)h=(2,-1,3,0) vektorát, pkeep=0,5p_{\mathrm{keep}}=0{,}5 mellett. Ha m=(1,0,1,1)m=(1,0,1,1), akkor az inverted dropout kimenete

ĥ=10,5(m⊙h)=2⋅(2,0,3,0)=(4,0,6,0).\hat h=\frac{1}{0{,}5}\,(m\odot h)=2\cdot(2,0,3,0)=(4,0,6,0).

Egy másik maszkkal, m′=(0,1,1,1)m'=(0,1,1,1) esetén ĥ′=2⋅(0,−1,3,0)=(0,−2,6,0)\hat h'=2\cdot(0,-1,3,0)=(0,-2,6,0). A két realizáció átlaga (2,−1,6,0)(2,-1,6,0), ami nem egyezik a 𝔼[ĥ∣h]=(2,−1,3,0)\mathbb E[\hat h\mid h]=(2,-1,3,0) várható értékkel; a várható érték nem egyetlen minta átlaga, hanem az eloszlás feletti átlag. A negyedik elem mindkét esetben nulla, mert h4=0h_4=0; a dropout a nulla aktivációt nem tudja zajossá tenni, ami a varianciaképlettel is összhangban van.

A minimalista NumPy-ellenőrzés:

import numpy as np

rng = np.random.default_rng(20261020)
h = np.array([2.0, -1.0, 3.0, 0.0], dtype=np.float32)
p_keep = np.float32(0.5)                 # megtartási valószínűség
mask = (rng.random(h.shape, dtype=np.float32) < p_keep).astype(np.float32)
out = mask * h / p_keep
print(f"mask={mask}, out={out}")
# Ellenőrzés: a 4. elem mindig 0, a skála 1/p_keep = 2.
# PyTorch-megfelelő: torch.nn.Dropout(p=1 - p_keep), mert ott p a kihagyás.

A seed, dtype és a p_keep numerikus típusa együttesen rögzíti a reprodukálhatóságot; más NumPy-verzió bitazonosságot nem ígér, ugyanúgy, ahogy 20.3. Preprocessing, domain shift és súlylicenc alfejezetben az input-szerződésnél láttuk.

21.3.7 Labor: egyenként változtatott regularizálók ablációja

A labor célja a három mechanizmus szétválasztása GlyphLab kis CPU-s környezetben. A rendszer: data/images.npy alakja (360,1,24,24)(360,1,24,24) float32, data/labels.npy int64, a split a data/metadata.csv-ből jön. A modell egy kis CNN:

A bemenet kizárólag a képtensor; a maszk és doboz nem bemenet. A kód a következő munkakönyvtárból futtatható:

# munkakönyvtár: lab/
# futtatás: python lab_n083_ablation.py
import numpy as np, torch, torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset

torch.manual_seed(20261020)
rng = np.random.default_rng(20261020)
device = torch.device("cpu")

images = np.load("data/images.npy").astype(np.float32)      # (360,1,24,24)
labels = np.load("data/labels.npy").astype(np.int64)        # (360,)
meta = np.genfromtxt("data/metadata.csv", delimiter=",", dtype=None, names=True)
train_idx = np.where(meta["split"] == "train")[0]
val_idx   = np.where(meta["split"] == "val")[0]

Xtr = torch.from_numpy(images[train_idx])
ytr = torch.from_numpy(labels[train_idx])
Xva = torch.from_numpy(images[val_idx])
yva = torch.from_numpy(labels[val_idx])

class TinyCNN(nn.Module):
    def __init__(self, p_drop=0.0):          # p_drop = nn.Dropout argumentuma
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(1, 16, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
            nn.Conv2d(16, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2))
        self.head = nn.Sequential(nn.Flatten(), nn.Linear(32*6*6, 3))
        self.drop = nn.Dropout(p_drop) if p_drop > 0 else nn.Identity()
    def forward(self, x):
        z = self.features(x)
        z = self.drop(z)
        return self.head(z)

def evaluate(model, X, y):
    model.eval()
    with torch.no_grad():
        out = model(X)
        return (out.argmax(1) == y).float().mean().item()

def train_one(cfg, Xtr, ytr, Xva, yva):
    # cfg: (p_drop, use_adamw, wd, label_smooth, use_aug)
    model = TinyCNN(p_drop=cfg[0]).to(device)
    opt = (torch.optim.AdamW if cfg[1] else torch.optim.Adam)(
        model.parameters(), lr=1e-3, weight_decay=cfg[2])
    lossf = nn.CrossEntropyLoss(label_smoothing=cfg[3])
    ds = TensorDataset(Xtr, ytr)
    for ep in range(15):
        model.train()
        for xb, yb in DataLoader(ds, batch_size=32, shuffle=True):
            if cfg[4]:
                xb = xb + 0.02 * torch.randn_like(xb)  # kis zaj, címkemegőrző
                sh = np.random.randint(-2, 3, size=2)
                xb = torch.roll(xb, shifts=tuple(sh), dims=(2, 3))
            opt.zero_grad()
            loss = lossf(model(xb), yb)
            loss.backward()
            opt.step()
    return evaluate(model, Xva, yva)

configs = {
    "baseline":      (0.0, False, 0.0,  0.0, False),
    "+dropout":      (0.5, False, 0.0,  0.0, False),
    "+weight_decay": (0.0, True,  1e-4, 0.0, False),
    "+label_smooth": (0.0, False, 0.0,  0.1, False),
    "+augment":      (0.0, False, 0.0,  0.0, True),
}
for name, cfg in configs.items():
    acc = train_one(cfg, Xtr, ytr, Xva, yva)
    print(f"{name:14s} val_acc={acc:.3f}")

A kiértékelés a legjobb validációs checkpoint nélkül, a 15. epoch végi paramétereivel történik; ez csak mechanizmus-probának való, nem modellválasztásnak. Az alábbi táblázat jelölt szemléltetés, nem ebben a kézirati lépésben elvégzett futás; a tényleges méréshez a fenti szkriptet kell futtatni és a nyers val_acc értéket naplózni.

Konfiguráció val_acc (jelölt) Értelmezés
baseline 0,62 gyenge, zajos kis modell
+dropout 0,63–0,68 erős túlillesztésnél javulhat
+weight_decay 0,64 közepes regularizáló
+label_smooth 0,65 cél lágyítása
+augment 0,66–0,70 adatoldali bővítés; nem bitazonos

A corrupt tesztet itt nem használjuk: a modellválasztás a val spliten történik, a corrupt teszt csak befagyasztott modell után értékelhető, ahogy a GlyphLab-szerződés előírja.

21.3.8 Feladatok

F1 (fogalmi, nehézség 1). Magyarázza el, miért nem ugyanaz a train/eval viselkedés oka a dropoutnál és a BatchNormnál. Adjon egy-egy konkrét példát a viselkedésre. Átadandó: legfeljebb 15 soros magyarázat, a két állapot különválasztott okaival.

F2 (levezetési, nehézség 2). Adott h∈ℝdh\in\mathbb R^d rögzített vektor, pkeep∈(0,1]p_{\mathrm{keep}}\in(0,1]. Vezesse le az inverted dropout kimenet feltételes varianciáját: Var(ĥj∣h)=1−pkeeppkeephj2\operatorname{Var}(\hat h_j\mid h)=\frac{1-p_{\mathrm{keep}}}{p_{\mathrm{keep}}}h_j^2. Nevezze meg a felhasznált varianciatulajdonságot, és ellenőrizze, hogy p=1p=1 esetén a variancia nulla. Átadandó: minden lépés, feltételek és szélsőeset-kontroll.

F3 (implementációs, nehézség 1). Írjon saját inverted_dropout(x, p_keep, rng) függvényt NumPy-ban, amely float32 bemenetet, pkeepp_{\mathrm{keep}} megtartási valószínűséget és seedelt Generator objektumot fogad, maszkot generál, elvégzi az 1/pkeep1/p_{\mathrm{keep}} skálázást, és shape-asserttel ellenőrzi a kimenetet. Tesztelje a pkeep=0,5p_{\mathrm{keep}}=0{,}5, x=(2,−1,3,0)x=(2,-1,3,0) példán; a seed legyen 42. Adja meg azt is, milyen argumentumú nn.Dropout felel meg a függvénynek. Átadandó: teljes függvénykód, futási kimenet, shape-ellenőrzés, magyarázat a pkeep=1p_{\mathrm{keep}}=1 határeset viselkedéséről.

F4 (hibakeresési, nehézség 2). Az alábbi kódrészlet egy hibás dropout-implementációt mutat. Azonosítsa a hibát, mondja meg, milyen következménye van train és eval módban, javítsa, és adja meg a javítás utáni ellenőrző kimenetet a pkeep=0,5p_{\mathrm{keep}}=0{,}5, h=(1,2)h=(1,2) esetre.

class WrongDropout:
    def __init__(self, p_keep):
        self.p_keep = p_keep
    def forward(self, x, rng):
        mask = (rng.random(x.shape) < self.p_keep).astype(float)
        return mask * x

Átadandó: hiba oka, következmény, javított kód, a javítás utáni egy konkrét kimenet.

F5 (kísérleti/transzfer, nehézség 3). Tervezzen olyan ablatív kísérletet GlyphLab-on, amely a dropout, a weight decay és az augmentáció hatását egyenként méri anélkül, hogy a corrupt tesztet modellválasztásra használná. Határozza meg a baseline modellt, a megfigyelési egységet, a splitet, a seedet, a metrikát, a rögzített tényezőket és az érvényességi korlátot. Átadandó: protokollterv és indoklás, legfeljebb 20 sor.

21.3.9 Megoldások és indoklás

F1. A dropout train módban friss Bernoulli-maszkot alkalmaz, és az inverted skálázással a feltételes várható értéket torzítatlanul hagyja; eval módban a maszkot eltávolítja, mert a determinisztikus AA éppen a várható érték. A BatchNorm viszont train módban a minibatch átlagával és varianciájával normalizál, és futó statisztikát frissít; eval módban ezeket a futó statisztikákat használja, hogy a kimenet ne függjön a batch összetételétől. A közös pont a modulállapot-váltás, az ok és a mechanizmus azonban különböző: véletlen eltávolítás, szemben a statisztikai normalizálás batchfüggetlenítésével.

F2. A varianciatulajdonság: Var(aX+b∣ℱ)=a2Var(X∣ℱ)\operatorname{Var}(aX+b\mid\mathcal F)=a^2\operatorname{Var}(X\mid\mathcal F) konstans a,ba,b mellett. Itt a=hj/pkeepa=h_j/p_{\mathrm{keep}}, b=0b=0, X=MjX=M_j. A Bernoulli-variancia Var(Mj)=pkeep(1−pkeep)\operatorname{Var}(M_j)=p_{\mathrm{keep}}(1-p_{\mathrm{keep}}). Ezért

Var(ĥj∣h)=(hjpkeep)2pkeep(1−pkeep)=1−pkeeppkeephj2.\operatorname{Var}(\hat h_j\mid h)=\left(\frac{h_j}{p_{\mathrm{keep}}}\right)^2 p_{\mathrm{keep}}(1-p_{\mathrm{keep}})=\frac{1-p_{\mathrm{keep}}}{p_{\mathrm{keep}}}\,h_j^2.

Szélsőeset: pkeep=1p_{\mathrm{keep}}=1 mellett 1−pkeep=01-p_{\mathrm{keep}}=0, a variancia nulla, a maszk konstans 1, a kimenet determinisztikus. Feltételek: pkeep>0p_{\mathrm{keep}}>0, Mj∼M_j\sim Bernoulli(pkeepp_{\mathrm{keep}}), MjM_j független a feltételtől. Az hj=0h_j=0 speciális esetben a variancia nulla, a nulla aktiváció nem zajosodik.

F3. Függvény:

import numpy as np

def inverted_dropout(x, p_keep, rng):
    assert x.dtype == np.float32, "float32 bemenet kötelező"
    assert isinstance(p_keep, float) and 0.0 < p_keep <= 1.0
    mask = (rng.random(x.shape, dtype=np.float32) < np.float32(p_keep)).astype(np.float32)
    out = mask * x / np.float32(p_keep)
    assert out.shape == x.shape and out.dtype == np.float32
    return out

rng = np.random.default_rng(42)
x = np.array([2.0, -1.0, 3.0, 0.0], dtype=np.float32)
print(inverted_dropout(x, 0.5, rng))
# lab-profil (NumPy 2.3.5) kimenete: [ 4. -0.  0.  0.]; a -0. a -1*0 float-előjele

A p_keep paraméter float, a bemenet float32; a PyTorch-megfelelő nn.Dropout(p=1 - p_keep); az osztás float32-ben végzett, ezért shape és dtype garantált. A pkeep=1p_{\mathrm{keep}}=1 határesetben (PyTorchban nn.Dropout(0)) a maszk minden eleme 1, a kimenet azonos a bemenettel, de a maszkgenerálás költsége megmarad; implementációban érdemes lehet p=1p=1 esetén rövidre zárni, ez azonban hatékonysági döntés, nem helyességi feltétel.

F4. A hiba: a függvény nem skáláz 1/pkeep1/p_{\mathrm{keep}}-pel. Következmény: train módban a kimenet várható értéke pkeepxp_{\mathrm{keep}}\,x lesz, nem xx, ezért eval módban a determinisztikus xx torzítatlan ugyan, de a train alatti várható aktivációk skálája eltér az eval módétól; a tanult súlyok a lefojtott skálához igazodnak, és eval alatt a réteg kimenetei túl nagyok lesznek. Javítás: return mask * x / self.p_keep. Ellenőrzés a pkeep=0,5p_{\mathrm{keep}}=0{,}5, x=(1,2)x=(1,2)-vel: ha m=(1,0)m=(1,0), a javított kimenet (2,0)(2,0); a hibás kimenet (1,0)(1,0) lett volna, amelynek várható értéke (0,5,1)≠(1,2)(0{,}5,1)\ne(1,2).

F5. Tervvázlat: a baseline a labor TinyCNN-je, Adam lr 10−310^{-3}, seed 20261020, batch 32, 15 epoch, metrika a val split accuracy. A megfigyelési egység a val képszám, de modellválasztásnál a val loss a checkpoint-kritérium. Rögzített tényezők: split a metadata.csv-ből, input NCHW float32 [0,1], nincs corrupt teszt a választásban. Egyetlen tényező változik futásonként: dropout nn.Dropout(0.5), azaz pdrop=pkeep=0,5p_{\mathrm{drop}}=p_{\mathrm{keep}}=0{,}5, vagy AdamW λ=10−4\lambda=10^{-4}, vagy zaj/eltolás augmentáció. Mindegyik futást legalább 3 seeddel ismételve, az eredményt seedenkénti pontosság-mediánnal és szórással jelentjük. Korlát: a GlyphLab toy adat, az eredmény nem általánosít természetes képre; a corrupt tesztet csak a kiválasztott befagyasztott modell páros kiértékelésére használjuk.

21.3.10 Tipikus tévedés ellenpéldával

A gyakori tévedés: „A dropout mindig javítja a validációt.” Ez nem igaz. A dropout csökkenti az effektív kapacitást és növeli a tanítójel zaját, ezért ha a modell eleve underfit, a hozzáadás tovább ront. Szemléltető ellenpélda: GlyphLab-on egyetlen lineáris fej közvetlenül a pixeleken, 64 rejtett neuron nélkül, mindössze 242⋅3+324^2\cdot3+3 paraméterrel; a train accuracy is alacsony, a modell nem overfit. Ha erre dropoutot kapcsolunk p=0,5p=0{,}5-tel, a train and val accuracy is csökken, mert a tanítójelet is elfojtjuk, miközben nem volt redundáns kapacitás, amit regularizálni kellett volna. A dropout tehát modellkapacitáshoz és adatskálához mért eszköz; a „mindig segít” állítás cáfolata már az underfit eset.

21.3.11 Önellenőrzés

Ellenőrizze a tétel lépéseit: (1) a feltételes várható érték linearitása alkalmazható-e, ha a maszk független a feltételtől? (2) Az eredmény N×dN\times d alakú-e mindkét oldalon? (3) A varianciaképletben pkeep=1p_{\mathrm{keep}}=1 (nn.Dropout(0)) esetén a maszk szórása nulla, a kimenet determinisztikus? (4) A weight decay célfüggvényében a λ/2\lambda/2 konvenció gradiense valóban λW\lambda W? (5) A label smoothing célvektor összege 1 marad-e minden ε∈[0,1)\varepsilon\in[0,1) esetén? Ha bármelyik kérdésre nemleges a válasz, a megfelelő levezetéshez térjen vissza, és keresse meg az indexhibát.

Korábbi fogalom visszahívása: a 9.3. Expresszivitás és approximációs állítások approximációs tételből azt láttuk, hogy az expresszivitás nem ígér taníthatóságot. A dropout és a weight decay az optimalizálási pályát és az effektív hipotézisteret alakítja, nem az approximációs kapacitás elméleti maximumát; a kettő különválasztása itt is kötelező.

21.3.12 Rövid összefoglaló

A dropout elemenkénti Bernoulli-maszkkal nulláz aktivációkat train módban, és az inverted skálázás révén a feltételes várható érték torzítatlan marad; eval módban a maszk eltűnik. A weight decay a loss-hoz adott λ2∥W∥F2\frac{\lambda}{2}\|W\|_F^2 tag, amelynek gradiense λW\lambda W; az AdamW decoupled változata ezt a zsugorítást leválasztja az adaptív lépésről. A label smoothing a célvektort keveri az egyenletes eloszlással, az augmentáció címkemegőrző véletlen transzformációkkal bővíti a bemenetet. A három mechanizmus a modell belsejében, a célfüggvényben és a data oldalon hat; hatásuk nem univerzális törvény, hanem kísérletileg ellenőrizendő heurisztika.

21.3.13 Következő alkalmazás

A 21.4. Osztályarány, súlyozás és céleloszlás egység az osztályarány és céleloszlás kérdését tárgyalja: ott a label smoothing fogalma a súlyozott cross-entropy és a kiegyensúlyozatlan célok elemzéséhez kapcsolódik.

21.3.14 Felhasznált források