A 18.1. Reziduális blokk és identitásút egységben a reziduális blokkot alakban definiáltuk, és láttuk, hogy a shortcut identitás a gradiens terjedését segíti. A gyakorlatban a reziduális főág egyik leggyakoribb eleme a konvolúció utáni batch normalizálás. Ennek az egységnek a célja annak pontos szétválasztása, ami a BatchNorm-ban statisztikailag számított, ami tanulható paraméter, és ami pufferállapot, különösen a train és az eval mód eltérő viselkedésén keresztül.
Előismeret: a 14.2. Több bemeneti és kimeneti csatorna alfejezetben bevezetett NCHW alak és csatornaszerkezet, valamint a 6.2. Konvexitás és sima optimalizálási határ alfejezetben tárgyalt optimalizálási szemlélet. A dropout train/eval kapcsolóját a 17.2. AlexNet és a kísérleti kontextus AlexNet-kontextusában említettük; a BatchNorm ugyanezt a kapcsolót más okból igényli.
Induló kérdés: egy BN-t tartalmazó hálózatot egyetlen tesztképpel
futtatunk úgy, hogy elfelejtettük az eval() módot. A
BatchNorm az egyetlen kép csatornájából számít átlagot és varianciát.
Miért sérül ezzel az inference-szerződés akkor is, ha gradiensláépést
egyáltalán nem végzünk?
Legyen a réteg bemenete , az NCHW konvenció szerint. A BatchNorm2d minden kimeneti/bemeneti csatornához külön statisztikát és külön affin paraméterpárt rendel. A batch-méret a csatornán belüli összes megfigyelés:
A csatorna batch-átlaga:
A csatorna batch-varianciája a torzított, azaz -es konvencióval:
A PyTorch BatchNorm2d ezt a torzított varianciát
használja; ez a torch.var(..., unbiased=False) konvenció
[D12]. Ez nem statisztikai becslés az 5.2. Várható
érték, variancia és likelihood értelmében, ahol a Bessel-korrekció
az
-es
mintavarianciát adja. Itt a cél normalizálás, nem a populációvariancia
torzítatlan becslése; a két konvenció megkülönböztetése numerikus
eltérést okoz.
A tanulható affin paraméterek csatornánként egy skalárpár:
A PyTorch-objektumban ezek a weight és a
bias attribútumok, mindkettő
hosszú vektor. Az eps paraméter
numerikus stabilizátor, alapértelmezése
.
A train mód képlete három lépésből áll: számítás, normalizálás, affin transzformáció.
Definíció 1 (BatchNorm train forward). Legyen , . A train módú normalizált érték
a kimenet pedig
A és a fenti, az aktuális batchből számított statisztikák. Minden mennyiség a csatornán osztozik; a a térbeli pozíciók és a batch fölé broadcastolódik.
A shape-ellenőrzés: ; az , , mind .
Lemma 1 (a normalizált batch statisztikái). Ha , akkor train módban batch-átlaga minden csatornán . Ha , akkor torzított batch-varianciája .
Bizonyítás. Az összegzés linearitása miatt a normalizált értékek átlaga
A varianciához használjuk, hogy a konstans szorzó a négyzetes eltérésekből kiemelhető:
Mivel a normalizált átlag nulla, ez a második kifejezés éppen a torzított variancia.
Két következmény. Először, ha , a normalizált batch varianciája közel ; ha , akkor . Másodszor, az affin lépés után a batch-átlag , a torzított variancia .
A train mód minden forwardja frissíti a running
buffert, ha a track_running_stats=True
alapértelmezést használjuk. A normalizálás és a pufferfrissítés
két különböző varianciát használ. Jelölje
A train forward a torzított -szel normalizál (Definíció 1), a futó variancia viszont a torzítatlan becslést kapja [D12]. Legyen a frissítés előtti futó átlag , a frissítés előtti futó variancia , és legyen a momentum . Az egy batch utáni új értékek:
esetén (egyetlen elem csatornánként) a torzítatlan becslés nem értelmezett; a PyTorch ilyenkor train módban hibát jelez.
A PyTorch alapértelmezett
.
Ez exponenciális mozgóátlag: a régi puffer
,
az aktuális batch
súlyt kap. A buffer a running_mean és
running_var attribútumban él, és nem kap
gradienst; a state_dict() menti, az optimizáló
viszont nem frissíti. A running_var varianciát tárol, nem
szórást, ezért a forwardban gyököt kell vonni belőle.
A sorozat frissítése
batch után,
mellett gyorsan elfelejti a múltat: az első batch
,
a második
súlyt kap. Ha
-t
növeljük, a buffer érzékenyebb az aktuális minibatch zajára; ha
csökkentjük, a konvergencia lassul. A momentum=None opció a
dokumentáció szerint az
kumulatív átlagot használja [D12]; a főútban mi a rögzített
változatot tárgyaljuk.
Az eval() mód a BatchNorm számára azt jelenti, hogy a
batchstatisztikát nem számoljuk, és a buffert nem frissítjük. Ha a
running statisztika rendelkezésre áll, akkor a normalizálás a futó
értékekkel történik:
Definíció 2 (BatchNorm eval forward). Eval módban,
track_running_stats=True mellett, a BatchNorm2d a fenti
képletet használja, ahol
és
a train során felhalmozott futó statisztika. A
ugyanazok a tanulható paraméterek, mint train módban.
A train/eval különbség formálisan abban áll, hogy train módban a
statisztika a minibatch függvénye és a forward mellékhatással frissíti a
buffert, eval módban pedig a statisztika konstans, a forwardnak nincs
mellékhatása. Ez nem az autograd beállítása, hanem a modul állapota: a
torch.inference_mode() a gradiensszámítást kapcsolja ki, de
önmagában nem váltja át a BatchNormot.
Az eval mód azért szükséges, mert az inference alatt a kimenetnek nem szabad függnie attól, hogy egy képet egyedül vagy más képekkel egy batchben értékelünk ki. A batchstatisztika egyetlen képen is számolható, ha , de akkor minden kép önmagához normalizálódik, nem a tanítási populációhoz; kis batchben pedig a statisztika varianciája nagy. A running puffer a train alatt látott csatornaeloszlás exponenciális mozgóátlagát közelíti, és így egy előre rögzített normalizálási konstanst ad az inference-hez.
Legyen , , , tehát . A bemenet
A nyolc pixel: . Az összeg , így
A négyzetes eltérések összege , tehát
mellett . A normalizált értékek:
A második kép értékei rendre , , , . Ha az affin paraméter , akkor :
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | |||
| 6 | |||
| 8 |
A második kép 2-je és 4-ese a táblázat 2. és 4. sorával azonos -et és -t ad, mert a statisztika mind a nyolc pixelből készül.
Most a running frissítés egyetlen forward után, , kezdeti , mellett. A pufferbe a torzítatlan variancia kerül: . Így
Ha ugyanez a batch másodszor is megérkezik, a buffer és lesz. Aki a torzított -öt írná a pufferbe, -öt, illetve -öt kapna; a különbség kis mellett jelentős.
A munkakönyvtár lab/. A szkript
bn_manual.py; futtatás:
python bn_manual.pyA bemenetet float32-ben, CPU-n adjuk meg, a főút szerint:
import torch
import torch.nn as nn
torch.manual_seed(20261020) # a BN inicializálása determinisztikus; a seed a szerződést rögzíti
dtype = torch.float32
device = "cpu"
X = torch.tensor(
[[[[1.0, 2.0], [3.0, 4.0]]],
[[[2.0, 4.0], [6.0, 8.0]]]],
dtype=dtype, device=device
) # (2,1,2,2)
bn = nn.BatchNorm2d(
num_features=1, eps=1e-5, momentum=0.1,
affine=True, track_running_stats=True
).to(device)
# Kézi statisztika a (N,H,W) dimenziókon, ugyanazzal a torzított varianciával:
mu = X.mean(dim=(0, 2, 3), keepdim=True) # (1,1,1,1)
var = X.var(dim=(0, 2, 3), unbiased=False, keepdim=True)
bn.train()
Y = bn(X)
manual = (X - mu) / torch.sqrt(var + 1e-5) # gamma=1, beta=0
print("max_abs_diff train:", (Y - manual).abs().max().item())
# Kézi pufferfrissítés: a futó variancia a torzítatlan becslést kapja (M=8).
M = X.numel() // X.shape[1]
var_unbiased = var * M / (M - 1)
rm_manual = 0.9 * 0.0 + 0.1 * mu.flatten()
rv_manual = 0.9 * 1.0 + 0.1 * var_unbiased.flatten()
print("running_mean:", bn.running_mean.item(), "kézi:", rm_manual.item())
print("running_var:", bn.running_var.item(), "kézi:", rv_manual.item())
assert torch.allclose(bn.running_mean, rm_manual) and torch.allclose(bn.running_var, rv_manual)
# Kézi levezetés: running_mean = 0.375, running_var = 37.5/70 + 0.9 = 1.435714...A kézi és a könyvtári forward közötti eltérés a fenti,
elemű példán float32-ben legfeljebb
nagyságrendű abszolút hiba, mert a képletek ugyanazok; a labor ezt
max_abs_diff formában írja ki. A futtatás után a bufferek a
fenti, torzítatlan varianciával számított értékeket adják; a szkript ezt
assert-tel ellenőrzi. Lab-profilban (PyTorch 2.14.1+cpu)
mért kimenet: running_mean
,
running_var
,
a train-forward eltérése a kézi képlettől gépi pontosságú.
Az eval-út ellenőrzése ugyanabban a szkriptben folytatódik:
bn.eval()
Y_eval = bn(X)
manual_eval = (X - rm_manual.view(1, -1, 1, 1)) / torch.sqrt(rv_manual.view(1, -1, 1, 1) + 1e-5)
print("max_abs_diff eval:", (Y_eval - manual_eval).abs().max().item())
assert torch.allclose(Y_eval, manual_eval, atol=1e-5)A bn.eval() hívás után a forward már a buffert
használja. Az
pixel eval-normalizált értéke
,
az
pixelé
;
a lab-profilban a könyvtári eval-forward mind a nyolc pixelre ezt adta.
A kimeneti összevetés akkor igazolja az eval utat, ha a könyvtár és a
kézi képlet mind a 8 pixelre egyezik.
Az eredeti tanulmány címe az internal covariate shift-et, azaz a rétegek bemeneti eloszlásának eltolódását nevezi meg motivációként [P07]. A PMLR-rekord és az absztrakt ellenőrzött; a cikk teljes mechanizmus-érvelése azonban ebben a kéziratban nem teljes szakaszolvasásból származik. A későbbi munka absztraktja szerint a BatchNorm javulása nem elsősorban az eloszlásstabilitáson, hanem az optimalizálási felület simításán keresztül magyarázható [P08]. Ebből nem következik, hogy az ICS-érv hamis, és az sem, hogy a P08-magyarázat minden architektúrára érvényes. A 6.2. Konvexitás és sima optimalizálási határ alfejezetben bevezetett -simaság lokális feltétel; a BN hálózatglobális konvergenciát vagy jobb generalizációt önmagában nem garantál. A helyes állításforma:
További gyakori hiba a momentum összekeverése a 6.4. Momentum és adaptív optimizálók
optimalizáló-momentumával. Az ottani momentum a gradienslépés
tehetetlensége; a BN momentum a futó statisztika
exponenciális simításának
súlya. A két paraméter egymástól független, és a
state_dict()-ben sem érintkeznek.
running_var varianciát és nem szórást
tárol, ezért kell gyököt vonni?eval() nélküli tesztfuttatás megváltoztatja
a későbbi inference-t?F1 (fogalmi, nehézség 1). Magyarázza el, miért nem használjuk a batchstatisztikát egyetlen tesztképre, és mit ad helyette a running puffer. Térjen ki arra az esetre is, amikor , de a GlyphLab -es képén. Elvárt eredmény: saját, három-négy mondatos érvelés, amely megkülönbözteti a batchstatisztikát a running statisztikától.
F2 (levezetési, nehézség 2). A fenti labor batchétől független új esetben legyen értéke és , , , . Számítsa ki , értékét a torzított konvencióval, majd mind a nyolc kimenetet. Elvárt eredmény: minden lépés, a shape megadása és a nyolc kimeneti érték két tizedesre kerekítve.
F3 (implementációs, nehézség 2). Írjon
batchnorm_forward_manual(X, gamma, beta, eps) függvényt,
amely a train forwardot a Definition 1 képleteivel számolja, a
statisztikát a
dimenziókon, unbiased=False varianciával képezi, és a
batchstat visszatérési értékben visszaadja a
párt is. Verifikálja a beépített nn.BatchNorm2d train
kimenetén a labor
batchén,
,
beállítással. Elvárt átadandó: teljes kód, a maximális abszolút eltérés
és a használt tolerancia.
F4 (hibakeresési, nehézség 2). Az alábbi töredék a labor modelljét egy tesztlistán futtatja:
best = SmallCNN()
best.load_state_dict(torch.load("best.pt", weights_only=True))
best.train() # a tanítási állapot megőrzése
preds = [best(x.unsqueeze(0)).argmax().item() for x in test_images]Azonosítsa a hibát, mondja ki a következményét a BatchNorm statisztikáira, és adja meg a javítást. Elvárt eredmény: a hiba oka, a következmény, a javított kódblokk és annak ellenőrzése, hogy két eltérő sorrendű tesztlistán azonos predikciók születnek.
F5 (kísérleti/transzfer, nehézség 3). Tervezzen
kísérletet a GlyphLab adaton arra a kérdésre, hogy a BatchNorm
beillesztése a kis CNN főágába javítja-e a validációs loss-t a
normalizálás nélküli baseline-hoz képest. Adjon protokollt: független
split, osztályonkénti jelenet-resampling nélkül, három seed, a val loss
alapú checkpoint-választás, a záróteszt a
corrupted_test.npy-n csak a kiválasztás után, páros
összehasonlítás. Elvárt eredmény: A hipotézis, a változó tényező, a
metrika, az érvényességi korlátok és annak kimondása, hogy a kis toy
adaton mért különbség nem általános mechanizmusbizonyíték.
F1. A batchstatisztika az adott minibatch függvénye. Egyetlen tesztkép esetén , így a csatorna átlaga és varianciája az adott kép pixeléből készülne: minden kép saját középértékéhez normalizálódna, nem a tanítás során látott csatornaeloszláshoz. A pixel elegendő lenne a számításhoz, de a normalizálás jelentése megváltozik: az inference kimenet attól függne, mely képek vannak éppen a batchben, és egy kép saját kontrasztja eltűnne. A running puffer az exponenciális mozgóátlaggal a train alatt megfigyelt statisztikát rögzíti, és rögzített, batchfüggetlen normalizálást ad az eval módhoz.
F2. A nyolc pixel: . Összegük , tehát
Az eltérések és négyzeteik: . Az összegük , így a torzított variancia
A nevező . A kimenet :
A kimeneti tensor shape-je . A eset azért ad pontosan -et, mert , tehát .
F3. Egy lehetséges teljes megoldás:
import torch
import torch.nn as nn
def batchnorm_forward_manual(X, gamma, beta, eps):
mu = X.mean(dim=(0, 2, 3), keepdim=True)
var = X.var(dim=(0, 2, 3), unbiased=False, keepdim=True)
xhat = (X - mu) / torch.sqrt(var + eps)
Y = gamma.view(1, -1, 1, 1) * xhat + beta.view(1, -1, 1, 1)
return Y, (mu, var)
X = torch.tensor(
[[[[1.0, 2.0], [3.0, 4.0]]],
[[[2.0, 4.0], [6.0, 8.0]]]], dtype=torch.float32
)
bn = nn.BatchNorm2d(1, eps=1e-5, momentum=0.1)
bn.weight.data.copy_(torch.tensor([2.0]))
bn.bias.data.copy_(torch.tensor([-1.0]))
bn.train()
Y_ref = bn(X)
Y_man, (mu, var) = batchnorm_forward_manual(
X, bn.weight, bn.bias, 1e-5
)
print("mu =", mu.view(-1).tolist()) # [3.75]
print("var =", var.view(-1).tolist()) # [4.6875]
print("max abs diff =", (Y_ref - Y_man).abs().max().item())A várt max abs diff float32-ben kisebb, mint
;
a laborban az atol=1e-4 értéknél szigorúbb,
atol=1e-5 tűrést érdemes használni, mert a képlet és a
könyvtár ugyanazt a torzított varianciát számolja.
F4. A hiba a best.train() hívás a
kiértékelés előtt. A BatchNorm train módban az aktuális tesztbatchből
számol statisztikát, és forwardonként frissíti a running puffert.
Következmény: a predikció batch-összetétel-függő lesz, a bejárási
sorrend megváltoztatása más logitokat adhat, és a mentett modell running
állapota felülíródik. A javítás:
best.eval()
with torch.inference_mode():
preds = [best(x.unsqueeze(0)).argmax().item() for x in test_images]Az ellenőrzés: a tesztlistát két különböző permutációban futtatva
eval() módban a predikciós lista a megfelelő indexeléssel
azonos. A torch.inference_mode() a gradiensszámítást
kapcsolja ki; önmagában nem váltja át a BatchNorm eval állapotát, ezért
a kettőt együtt kell használni.
F5. A protokoll rögzítése a futás előtt: a kérdés
az, hogy a BN a GlyphLab val loss-át csökkenti-e a normalizálás nélküli
azonos architektúrához képest. A megfigyelési egység a független
scene_id; a train/val/test splitet a generátor rögzíti, a
bootstrap vagy újraosztás nem keveri a nézeteket. Mindkét modellt három
seeddel tanítjuk, azonos inicializálási és optimalizáló-beállításokkal.
A modellek a BN jelenlétében térnek el; a val loss alapján választunk
legjobb checkpointot, a tesztet csak ezután, a párosított clean és
corrupt változaton értékeljük ki. A fő metrika a val loss, jelentésre a
test accuracy is megadható. A korlátok: a GlyphLab 240 tanítóképe nem
elegendő a mechanizmus általános igazolására; a három seed csak a
training zaj egy keskeny szórását mutatja; a P08 optimalizálási
magyarázatát ez a kísérlet nem erősíti vagy cáfolja, mert csupán egy
kis, egy feladatú adathalmazon mér.
A BatchNorm2d train módban minden csatornára kiszámítja a batch átlagát és torzított, -es varianciáját, normalizál, majd a tanulható és affin transzformációt alkalmazza. A forward mellékhatásként momentummal frissíti a futó átlag- és varianciabuffert. Eval módban a batchstatisztika helyett ez a rögzített running puffer normalizál, így az inference nem függ a batch összetételétől. A P07 ICS-motivációja és a P08 optimalizálási vizsgálata külön státuszú állítás; a bizonyított rész csupán a normalizált batch nulla átlagáról és közel egységnyi varianciájáról szól.
A 18.3. Pre-activation, kis batch és alternatív normalizálás egység a pre-activation elrendezést, a kis batch viselkedését és a BatchNorm alternatíváit tárgyalja. Ehhez itt azt visszük tovább, hogy a normalizálás helye a reziduális főágban nem közömbös: a shortcut megkerülheti a normalizálást, ezért a pre-activation és a post-activation változat statisztikai útja eltér.