A 18.2. Batch normalization train és eval módban egységben tisztáztuk, hogy a BatchNorm2d train módban batchstatisztikát, eval módban futó buffert használ. Itt azt vizsgáljuk, hogy a normalizáló réteg mely tengelyeken aggregál, és ez hogyan hat a batchfüggésre. Az előismeret a következő: a normalizálás képlete, a train/eval különbség, valamint a 14.3. Paraméterszám, MAC és aktivációmemória alfejezetből ismert tenzoralakok és paraméterszámok, a 17.3. VGG és kis kernelek kompozíciója alfejezetből a mély, kis kernelű blokkok szerkezete, a 6.3. Sztochasztikus és minibatch gradiens alfejezetből pedig a minibatchstatisztika zajossága.
Induló kérdés: egyetlen, méretű batch esetén milyen statisztikát számol a BatchNorm, a LayerNorm és a GroupNorm? A becslésed rögzítése után ellenőrizzük a definíciókból.
Legyen a bemenet , ahol a batch, a csatorna, és a térbeli kiterjedés. Egy normalizáló réteg minden megfigyeléshez kiválaszt egy indexhalmazt, és azon számol átlagot és torzított varianciát:
majd a normalizált értéket és az affin kimenetet módon számolja. A és alakja a rétegtípustól függ; az a nullához közeli variancia elleni numerikus védelem. A PyTorch megvalósítás mindhárom alábbi rétegnél torzított, nevezőjű varianciát használ, ahogy azt a [D12] dokumentáció is rögzíti a BatchNorm2d esetén.
BatchNorm2d. A normalizálás minden csatornára külön-külön az indexek felett történik, azaz . A tanulható paraméterek alakja . Train módban a batchstatisztika egyben frissíti a futó buffert; eval módban ezt használja, így az inference nem függ a batch összetételétől.
LayerNorm normalizált alakkal
.
Itt minden egyes
mintára a
indexek felett számolunk, azaz
.
A normalizált alak egy mintára terjed ki, ezért az
batchdimenzió nem része a statisztikának. A PyTorch
LayerNorm(normalized_shape=[C,H,W]) esetén a
paraméterek alakja szintén
;
nincs futó állapot. Az inference ezért mindig bemenetfüggő, de
mintánként független: az
-edik
sor kimenetét nem befolyásolja, mi van még a batchben.
GroupNorm. A csatornákat darab azonos méretű csoportra osztjuk; ehhez egész kell. A -edik csoport csatornahalmaza legyen , mérete . Egy rögzített párra a
átlagot és a hozzá tartozó torzított varianciát számoljuk, majd az elemeket ezzel normalizáljuk. A paraméterek alakja ; nincs futó állapot. A választás egy mintára LayerNorm jellegű normalizálást ad, a választás pedig csatornánkénti, térbeli normalizálást.
A különböző rétegek nem ugyanazt a statisztikát normalizálják, és ezt a tengelyválasztás rögzíti.
| Réteg | Aggregációs indexek | Affin paraméter alakja | Futó állapot |
|---|---|---|---|
| BatchNorm2d | , csatornánként | van: | |
| LayerNorm | , mintánként | nincs | |
| GroupNorm | , mintánként és csoportonként | nincs |
Ez a táblázat a levezetési kötelezettség magja: a réteg viselkedése abból következik, hogy a fenti indexhalmazok mely dimenziókat fogják össze. A BatchNorm az dimenziót használja, ezért train módban a batchfüggés beépül; a LayerNorm és GroupNorm az dimenziót nem aggregálja, ezért egy minta kimenete más mintáktól független marad.
A [P06] forrás olvasott 3.1–3.2 szakasza szerint a reziduális blokk rövidített definíciója ; az eredeti elrendezésben a főág kimenetéhez adjuk a shortcutot, és az összeg után még egy ReLU áll. Ezt poszt-aktivációs formának nevezzük, mert az utolsó nemlinearitás a shortcut hozzáadása után következik.
A pre-aktivációs elrendezés ezzel szemben a normalizálást és a ReLU-t a konvolúció elé helyezi, és az összegzés után már nincs további ReLU. Egy két konvolúciós, csatornaszám-tartó blokk sematikus definíciója:
$$ y=x+W_2\,\mathrm{ReLU}\Bigl(\mathrm{BN}\bigl(W_1\,\mathrm{ReLU}(\mathrm{BN}(x))\bigr)\Bigr), $$
ahol a konvolúciós rétegek, és a bias a BatchNorm jelenléte miatt tipikusan elhagyható. A fontos különbség nem pusztán esztétikai: a poszt-aktivációs formában a BatchNorm a konvolúció után, az összegzés előtt normalizál, a pre-aktivációs formában viszont a normalizálás és a nemlinearitás a következő konvolúció bemenetét készíti elő, és az identitás shortcut közvetlenül, normalizálatlanul csatlakozik az összeghez.
Ebből a szerkezetből azonban nem következik automatikus konvergencia- vagy általánosítási garancia. Mind P06, mind a pre-aktivációs változatoknál az elrendezés előnye empirikus architektúraállítás; a matematikailag bizonyítható rész csak az, amit az operátorok definíciójából levezetünk. A [P07] absztraktja az internal covariate shift csökkentését nevezi meg motivációként, a [P08] absztraktja pedig az optimalizálási táj kedvezőbbé válását hangsúlyozza; e két állítás különböző szakirodalmi igény, és egyik sem olvasott teljes bizonyítás ebben az egységben. A jelen alfejezet csak a normalizálási statisztika és a batchfüggés pontos leírására vállalkozik.
Legyen , azaz két minta, két csatorna, egy sor és két oszlop. A négy csatornamátrix:
A kézzel levezetett idealizált esetben vegyünk , , értékeket, így a normalizált érték a tiszta standardizálás. A futtatott PyTorch-rétegekhez később pozitív értéket adunk: a kézi számolás ezért a numerikus kimenet jó közelítése, de nem bitpontos kódpredikció.
BatchNorm2d. A csatornán a négy érték , átlaga , torzított varianciája . A normalizált értékek:
A csatornán az értékek , átlaga , varianciája , így a normalizált értékek:
LayerNorm . A -adik minta négy értéke , átlaga , varianciája . A normalizált minta:
Az -es minta értékei , átlaga , varianciája szintén , normalizált alakja:
A két réteg kimenete már ebből a kis példából láthatóan nem azonos, mert a BatchNorm a batch mentén, a LayerNorm az egyes mintákon belül aggregál.
Szélsőeset. és esetén a BatchNorm egy csatornára egyetlen értékkel rendelkezik, így a normalizált érték nulla, és a kimenet lesz. Ha viszont vagy nagyobb, a BatchNorm esetén is rendelkezik térbeli mintákkal, tehát nem degenerálódik automatikusan.
Munkakönyvtár: lab/n071. Az alábbi kód a fenti kézi
példát ellenőrzi PyTorchban. A gyári
inicializálás miatt a normalizált értékeket látjuk.
import torch
import torch.nn as nn
torch.manual_seed(0)
# N=2, C=2, H=1, W=2
x0 = torch.tensor([[[[1.0, 3.0]], [[5.0, 7.0]]]])
x1 = torch.tensor([[[[-3.0, -1.0]], [[-7.0, -5.0]]]])
X = torch.cat([x0, x1], dim=0)
bn = nn.BatchNorm2d(2, eps=1e-5, momentum=0.1)
ln = nn.LayerNorm([2, 1, 2], eps=1e-5)
gn = nn.GroupNorm(1, 2, eps=1e-5) # G=1: mintánkénti normalizálás
bn.train()
print("BN train:")
print(bn(X))
print("running_mean:", bn.running_mean.tolist())
print("running_var :", bn.running_var.tolist())
ln.eval()
print("LN:")
print(ln(X))
gn.eval()
print("GN, G=1:")
print(gn(X))
# Eval BatchNorm: a running bufferrel normalizál
bn.eval()
print("BN eval, első minta:")
print(bn(X[:1]))A kézi levezetésből számított kulcspontok: a BN train kimenete a
csatornán
,
a
csatornán
.
A futó puffer az első train forward után
esetén
és
,
esetén
.
A PyTorch BatchNorm2d a tanítási forward normalizálásához a
torzított varianciát, a running_var frissítéséhez viszont a
korrigált mintavarianciát használja; emiatt az 5 és 37 közvetlen beírása
a pufferfrissítésbe hibás volna. A kézi normalizálás
ideális határesetet használ, a futtatható kód stabilitásból
-öt,
ezért az értékek kis mértékben eltérnek. A LayerNorm és a
GroupNorm mintánként a fenti normalizált értékeket adja. A fenti számok
a képletből számított várt értékek; a labor tényleges futtatását a
hallgató végzi.
A labor kérdése: hogyan változik egy rögzített minta kimenete, ha egyedül vagy egy másik mintával egy batchben dolgozzuk fel, BatchNorm és GroupNorm esetén?
Bemenet: determinisztikus
,
torch.randn seeddel. Két réteg:
BatchNorm2d(8, momentum=0.1) és
GroupNorm(2, 8). Módok: train, eval. Négy forward mindkét
rétegre: teljes batch, majd csak az első minta. Ellenőrzés
torch.allclose és a csatornaátlagok összehasonlítása.
import torch
import torch.nn as nn
torch.manual_seed(123)
X = torch.randn(2, 8, 4, 4, dtype=torch.float32)
bn = nn.BatchNorm2d(8, momentum=0.1)
gn = nn.GroupNorm(2, 8)
bn.train(); gn.train()
Y_bn_batch = bn(X)
Y_bn_single = bn(X[:1])
Y_gn_batch = gn(X)
Y_gn_single = gn(X[:1])
print("train, batch==single head?",
torch.allclose(Y_bn_batch[0], Y_bn_single[0], atol=1e-6),
torch.allclose(Y_gn_batch[0], Y_gn_single[0], atol=1e-6))
bn.eval(); gn.eval()
Y_bn_ev_batch = bn(X)
Y_bn_ev_single = bn(X[:1])
Y_gn_ev_batch = gn(X)
Y_gn_ev_single = gn(X[:1])
print("eval, batch==single head?",
torch.allclose(Y_bn_ev_batch[0], Y_bn_ev_single[0], atol=1e-6),
torch.allclose(Y_gn_ev_batch[0], Y_gn_ev_single[0], atol=1e-6))A várt, a tengelyválasztásból következő eredmény: train módban a
BatchNorm első mintája a teljes batchben más statisztikát használ, mint
az egyedi feldolgozásnál, ezért a két fej nem egyezik. A GroupNorm
kimenete viszont mintánként független, így a fejek közel azonosak. Eval
módban a BatchNorm a futó buffert használja, ezért a batch- és egyedi
feldolgozás feje közel azonos lesz; a GroupNorm eleve batchfüggetlen. A
numerikus egyezés toleranciája atol=1e-6 a determinisztikus
CPU-s float32 számításra.
Az átadandó eredmény: a futtatott script, a nyomtatott logikai értékek és egy rövid értelmezés, amely megnevezi, hogy a train módbeli BatchNorm-eltérés oka a batchstatisztika. Ez az állítás a definícióból levezetett tulajdonság, nem mért benchmark; a konkrét futási idő és bitazonosság a helyi környezettől függ.
Az alábbi feladatok mindegyikéhez add meg a bemenetet, a nehézséget és az elvárt átadandó eredményt.
F1 (fogalmi, nehézség 1). Adott
.
Határozd meg szövegesen, milyen indexhalmaz felett számol átlagot és
varianciát a BatchNorm2d, a LayerNorm([4,3,3]) és a
GroupNorm(2,4). Add meg mindhárom esetben a normalizált
statisztika kiszámításának darabszámát is.
F2 (levezetési, nehézség 2). Legyen a következő: és . Vezesd le a BatchNorm2d és a LayerNorm kimenetét , , mellett. Ellenőrizd, hogy a BatchNorm kimenetén minden csatornán nulla az átlag és egységnyi a torzított variancia, a LayerNorm kimenetén pedig a minta négy elemének átlaga nulla, becsült varianciája egy.
F3 (implementációs, nehézség 2). Készíts PyTorch
nn.Module osztályt PreActBlock néven, amely
csatornán
-as,
padding 1, stride 1 konvolúciókat használ a BN–ReLU–conv sorrenddel,
bias nélkül. Az osztály kimenete
.
Ellenőrizd a kimeneti alakot
,
,
mellett.
F4 (hibakeresési, nehézség 2). A következő hívás
hibát okoz: nn.GroupNorm(2, 2) olyan bemenetre, amelynek
csatornaszáma 3. Mi a hiba, mi a következménye, és hogyan javítható úgy,
hogy a csatornák száma 3 maradjon? Válaszodban térj ki arra, miért nem
oldható meg
-vel
az oszthatóság megsértése nélkül.
F5 (kísérleti transzfer, nehézség 3). Tervezz kísérletet a GlyphLab adaton: hasonlítsd össze ugyanazt a kis CNN-t BatchNorm és GroupNorm normalizálással, miközben a batch méretet 32-ről 4-re csökkented. Add meg a protokollt, a megfigyelési egységet, a baseline-t, a metrikát és a következtetés érvényességi határát. Valós futtatást nem várunk el; a tervnek tartalmaznia kell a seed, split és validációs választás rögzítését.
F1 megoldás. A BatchNorm2d minden csatornára az elemből számol statisztikát, tehát csatornánként egy átlag és egy variancia, összesen pár. A LayerNorm minden mintára a elemből számol, tehát pár. A GroupNorm esetén csatorna alkot egy csoportot; minden mintára és csoportra elem felett aggregál, összesen pár. A paraméteres affin transzformáció ezután a normalizált értékekre broadcastolódik.
F2 megoldás. A BatchNorm2d esetén a térbeli elemből számol csatornánként. A csatorna értékei , átlaga , torzított varianciája , normalizált értékek . A csatorna értékei , átlaga , varianciája , normalizált értékek . A BN kimenet tehát . A LayerNorm a minta mind a négy értékére számol: átlag , variancia . A normalizált kimenet . Ellenőrzés: a BN csatornaátlagai , a csatornavarianciák ; az LN négy elemén az átlag , a torzított variancia .
F3 megoldás.
import torch
import torch.nn as nn
import torch.nn.functional as F
class PreActBlock(nn.Module):
def __init__(self, C):
super().__init__()
self.bn1 = nn.BatchNorm2d(C)
self.conv1 = nn.Conv2d(C, C, 3, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(C)
self.conv2 = nn.Conv2d(C, C, 3, padding=1, bias=False)
def forward(self, x):
out = self.conv1(F.relu(self.bn1(x)))
out = self.conv2(F.relu(self.bn2(out)))
return x + out
torch.manual_seed(7)
block = PreActBlock(3)
x = torch.randn(2, 3, 8, 8, dtype=torch.float32)
y = block(x)
print(y.shape) # Várt: torch.Size([2, 3, 8, 8])Alakellenőrzés: a BN nem változtat alakot, a ReLU sem, a conv padding 1 és stride 1 mellett megőrzi a térbeli méretet. Az összegzés csak azonos alakú tenszorokra értelmes, ezért az és alakjának egyeznie kell.
F4 megoldás. A
GroupNorm(num_groups, num_channels) második argumentuma a
várt csatornaszám. Ha a bemenet
,
de a konstruktorban
szerepel, a PyTorch a bemeneti és várt csatornaszám eltérése miatt hibát
ad, mert a réteg 2 csatornát feltételez. A num_channels
értéket 3-ra kell állítani. A
csoportosítás ekkor azért hibás, mert
nem egész; a csoportok méretének egyenlőnek kell lennie. A lehetséges
javítás: nn.GroupNorm(1, 3) vagy
nn.GroupNorm(3, 3). Ellenőrzésképpen egy
forwardnak hiba nélkül kell lefutnia, és a kimenet alakjának meg kell
egyeznie a bemenetével.
F5 megoldásminta. Kérdés: a batch méret csökkenése
melyik normalizálást érinti jobban? Megfigyelési egység a
scene_id, mert a két nézet ugyanahhoz a jelenethez
tartozik. A split rögzített: train 240, val 60, test 60 kép. Baseline:
kis, azonos mélységű CNN, ugyanaz a random seed, BatchNorm rétegekkel,
batch méret 32. Változó tényező: a normalizálás típusa (BN vagy
GroupNorm), valamint a batch méret (32 és 4). Metrika: validációs
pontosság és a veszteség utolsó 10 epochra vett átlaga. A
modellválasztás csak a validációs veszteség alapján történik, a tesztet
egyszer, a végleges kiválasztás után értékeljük. Erőforrás: CPU,
float32. A következtetés korlátja: a GlyphLab szintetikus, 24×24-es,
egycsatornás adat, ezért az eredmény nem általánosítható természetes
képekre; továbbá a batch méret és a normalizálás hatása összefonódhat a
tanulási ráta érzékenységével, ezért az egyes konfigurációkhoz érdemes
rövid tanulásiráta-keresést is rögzíteni.
Gyakori tévedés, hogy a BatchNorm, LayerNorm és GroupNorm „ugyanazt a standardizálást végzi, csak más néven”. A fenti példa ezt cáfolja. A BatchNorm a csatornához a batch összes mintáját használja, ezért a értékekre normalizál; a LayerNorm a -adik minta értékeire. A kimenetek már a nulladik mintán is különböznek: a BN első eleme , az LN első eleme . A hiba következménye lehet, hogy a felhasználó eval módban a LayerNormtól batchfüggetlen futó statisztikát vár, vagy a BatchNormot egyetlen mintán úgy értelmezi, mint az összes csatorna normalizálását; mindkettő téves tengelyválasztásból fakad.
Ellenőrizd, hogy a normalizálási képletben az a varianciához adódik, nem a szóráshoz; a variancia torzított; a BN train egyszerre frissíti a futó buffert és használja a batchstatisztikát. A 18.2. Batch normalization train és eval módban alfejezetből visszaidézve a futó buffer momentumos rekursiója . A 6.3. Sztochasztikus és minibatch gradiens alfejezetből tudjuk, hogy a minibatchstatisztika a teljes adathoz képest zajos becslő, és ez a normalizáló réteg statisztikájára is vonatkozik. A 14.3. Paraméterszám, MAC és aktivációmemória paraméter- és MAC-számítását finomítja, hogy a BN/GN elemenkénti műveletei nem súly-MAC-ok, de a statisztikaredukció és a normalizálás szintén számítási költséggel jár. A 17.3. VGG és kis kernelek kompozíciója alfejezetben tárgyalt kis kernelű mély stackekben a normalizálás elhelyezése éppen a pre-aktivációs blokk kapcsán válik tervezési döntéssé.
A normalizáló rétegek közös vázat használnak, de eltérő tengelyeken aggregálnak. A BatchNorm2d csatornánként a batch és a térbeli pozíciók felett normalizál, tanulható paraméterekkel és futó bufferekkel; eval módban a buffert használja. A LayerNorm mintánként az összes csatorna és térbeli elem felett normalizál, paramétereinek alakja , nincs futó állapota. A GroupNorm csatornacsoportonként aggregál, paraméterekkel. A pre-aktivációs blokk a BN–ReLU–conv sorrendet használja, és a shortcutra nem kerül normalizálás; az előnyök azonban empirikus architektúraállítások, nem bizonyított konvergenciagaranciák. A feladatmegoldások és a labor a batchfüggést a tengelyválasztásból vezetik le.
A 18.4. Reziduális blokk ablatív vizsgálata egységben reziduális blokkok ablatív vizsgálatát végezzük el: ugyanazon a kis GlyphLab-feladaton, rögzített seedekkel mérjük össze a poszt-aktivációs és pre-aktivációs elrendezést, valamint a normalizálás elhagyásának hatását. Ott az itt definiált PreActBlock lesz az egyik változat, és a mérési állításokat kísérleti naplóval, nyers epochonkénti metrikákkal kell alátámasztani.