Ez az alfejezet a reziduális blokkot, a mély hálózatok egyik legfontosabb szerkezeti elemét vezeti be. Az előismeret a 11.4. Egy teljes PyTorch tanítási iteráció alfejezetből a teljes tanítási lépés, a 14.4. Csoportos és depthwise konvolúció alfejezetből a csatornák közti kapcsolat korlátozása, a 16.4. IV. ellenőrző pont: a CNN-operátor teljes megértése alfejezetből a CNN-operátor specifikációja, a 17.4. Architektúra összehasonlítása kontrollált keretben alfejezetből pedig a kontrollált architektúra-összehasonlítás fogalma. A korábbi 17.1. LeNet és a lokális megosztott reprezentáció megmutatta, hogy a rétegek egymásra építése önmagában nem feltétlenül hoz jobb eredményt: a mélység növelése új optimalizálási és numerikus nehézségeket teremt. He, Zhang, Ren és Sun P06 3.1–3.2 szakasza a reziduális tanulást olyan blokkal vezeti be, amelyben a háló nem közvetlenül a kívánt leképezést, hanem annak egy eltérését tanulja egy identitásút mellett.
Induló kérdés: ha egy blokk kimenete , akkor mit tudunk mondani a blokk Jacobi-mátrixáról, és miért nem következik ebből, hogy minden reziduális háló jól kondicionált?
Legyen a blokk bemenete . A reziduális blokk kimenete
ahol a reziduális leképezés: általában két vagy három konvolúciós réteg, amelyet nemlinearitás és esetleg normalizálás követ, pedig a tanulandó paraméterek összessége. Az -et közvetlenül a kimenethez vezető utat identity shortcutnak vagy identitásútnak nevezzük. Az intuíció az, hogy ha a kívánt blokk kimenet közel van a bemenethez, akkor a hálónak elegendő -t tanulnia; a shortcut így egy előre adott, paramétermentes jelölt leképezést biztosít.
A tenzoralakú változatban , . Az összeadás csak akkor értelmes, ha
Ha a blokk csatornát vált, vagy stride-dal csökkenti a térbeli méretet, akkor a shortcutot is transzformálni kell. A projection shortcut egy -es konvolúcióval,
ahol és a stride megegyezik a főág stride-jával, így a kimenet
A projekció tehát nem egyszerű dimenziótoldás, hanem tanulható lineáris leképezés, amely a shortcut út és a főág alakját összehangolja a 14.1. Stride, padding, dilation és outputméret alfejezetben levezetett kimeneti méret szerint.
A P06 megkülönbözteti a basic blokkot és a bottleneck blokkot. A basic blokk két -as konvolúcióból áll, változatlan csatornaszámmal. A bottleneck blokk három rétegből épül fel: egy -es konvolúció csökkenti a csatornákat -ről -re, egy -as konvolúció a szűkebb csatornán dolgozik, majd egy második -es konvolúció visszaállítja a csatornát. A tipikus , választás mellett a paraméterszám bias nélkül:
| Réteg | Csatorna | Kernel | Paraméter (bias nélkül) |
|---|---|---|---|
| 1. konvolúció | |||
| 2. konvolúció | |||
| 3. konvolúció |
Összesen paraméter. Két közvetlen -as konvolúció csatornán ezzel szemben paramétert igényelne, tehát a bottleneck körülbelül tizenhetedére csökkenti a paraméterszámot, miközben a bemeneti és kimeneti csatornaszám változatlan marad.
A központi állítást absztrakt vektoros formában mondjuk ki. Legyen a blokk bemenete, a reziduális leképezés, és .
Lemma (lokális Jacobi-alak). Ha differenciálható az pontban, akkor a leképezés Jacobi-mátrixa ugyanabban a pontban
ahol az egységmátrix, pedig Jacobi-mátrixa a könyv 4.3. Jacobi-mátrix, láncszabály és VJP konvenciója szerint: a . sora és . oszlopa .
Bizonyítás. A leképezés . koordinátája . A parciális deriváltra minden esetén
ahol , ha , egyébként . A jobb oldal első tagja az egységmátrix eleme, a második pedig a mátrix eleme. Ezzel a mátrixegyenlőséget koordinátánként beláttuk. ∎
A lemma következménye a visszaterjesztésre: ha a blokk kimenetére érkező upstream gradiens , akkor a VJP-szabály szerint
Az első tag, , változatlanul átfolyik az identitásúton; a második tag a paraméterezett ágon keresztül visszaterjesztett gradiens. Ez a felbontás az, amit a szakirodalom gyakran „a gradiens az identitásúton is eljut a korábbi rétegekhez” formában említ.
Miért nem garantál ez minden hálón jó kondicionáltságot? A fenti lemma lokális állítás: egyetlen pontban írja le a blokk Jacobi-mátrixát. Egy mátrix kondíciószámát 2-es spektrális normában a hányados adja. Az mátrix kondíciószáma tetszőlegesen nagy lehet, mert sajátértékei a tanulás során közel kerülhetnek -hez. Szélsőséges lineáris ellenpéldaként legyen ; ekkor , és
A Jacobi-mátrix szinguláris, a kondíciószám végtelen, annak ellenére, hogy a shortcut jelen van. A lineáris esetben persze korlátos marad, ha sajátértékei elkerülik a -et, de a nemlineáris Jacobi-mátrixa pontról pontra változik, ezért egyetlen pontban számított kondíciószám nem ad globális korlátot a teljes tanítási pályára.
Mély háló esetén a láncszabály tovább nehezíti a helyzetet. Ha a háló blokk kompozíciója, , ahol , akkor
ahol a . blokk bemenete. Még ha minden egyes tényező adott pontban jól kondicionált is, a szorzat spektruma elfajulhat. Ha például minden tényező , akkor a szorzat : a kondíciószáma ugyan 1, de a gradiens normája exponenciálisan csillapodik. A skip tehát nem tünteti el a főág okozta csillapítást, csak hozzáad egy plusz utat. A P06 kísérleti javulást mutat, nem pedig kondicionáltsági vagy globális konvergenciatételt; az állítás típusa empirikus eredmény, nem matematikai garancia.
Legyen a bemenet és a reziduális leképezés lineáris:
Először a reziduális ág:
A blokk kimenete
A Jacobi-mátrix a lemma szerint
Ellenőrzésképpen a upstream gradienssel a VJP:
Ugyanez a felbontásból: az identitásút -t ad, a reziduális út pedig
és , egyezésben a direkt számolással.
A labor a skip mechanizmust izolálja, ezért a BatchNormot elhagyjuk;
a BN bevezetése a 18.2. Batch normalization train és
eval módban feladata. Az alábbi lab/residual_block.py
két blokkot valósít meg: azonos alakút és csatornát váltót. Az
ellenőrzéshez a 16.4. IV. ellenőrző pont: a
CNN-operátor teljes megértése alfejezetben megszokott háromszögelvet
követjük: a képletezett
összeadást közvetlenül összehasonlítjuk a blokk kimenetével.
Munkakönyvtár: lab/.
"""lab/residual_block.py"""
import torch
import torch.nn as nn
torch.manual_seed(20261020)
device = torch.device("cpu")
dtype = torch.float32
class BasicResBlock(nn.Module):
"""Azonos csatornaszámú, stride=1 reziduális blokk."""
def __init__(self, C, kernel=3):
super().__init__()
self.conv1 = nn.Conv2d(C, C, kernel, padding=1)
self.conv2 = nn.Conv2d(C, C, kernel, padding=1)
self.relu = nn.ReLU()
def forward(self, x):
F = self.relu(self.conv1(x))
F = self.conv2(F)
return x + F
class ProjectionResBlock(nn.Module):
"""Csatornát váltó blokk 1x1 projekciós shortcuttal."""
def __init__(self, C_in, C_out, stride=1):
super().__init__()
self.conv1 = nn.Conv2d(C_in, C_out, 3, stride=stride, padding=1)
self.conv2 = nn.Conv2d(C_out, C_out, 3, padding=1)
self.shortcut = nn.Conv2d(C_in, C_out, 1, stride=stride)
self.relu = nn.ReLU()
def forward(self, x):
F = self.relu(self.conv1(x))
F = self.conv2(F)
return F + self.shortcut(x)
def main():
N, C, H, W = 2, 3, 24, 24
x = torch.randn(N, C, H, W, device=device, dtype=dtype)
# Azonos alakú blokk: a kimenetnek x + F(x)-szel kell egyeznie.
block_same = BasicResBlock(C).to(device, dtype)
block_same.eval()
with torch.no_grad():
y = block_same(x)
F_ref = block_same.conv2(block_same.relu(block_same.conv1(x)))
err_same = (y - (F_ref + x)).abs().max().item()
print("azonos alak max hiba:", err_same)
# Csatornát váltó blokk: a shortcut 1x1 konvolúcióval projektál.
block_proj = ProjectionResBlock(C, 6).to(device, dtype)
block_proj.eval()
with torch.no_grad():
y2 = block_proj(x)
print("projekcios kimenet alakja:", tuple(y2.shape))
print("elvart alak: (2, 6, 24, 24)")
if __name__ == "__main__":
main()Futtatás a projekt gyökeréből:
python lab/residual_block.pyA várt kimenet szemléltetés, nem mért futás:
azonos alak max hiba: 0.0
projekcios kimenet alakja: (2, 6, 24, 24)
elvart alak: (2, 6, 24, 24)
Az első sor azt ellenőrzi, hogy a BasicResBlock valóban
az
összeadást számolja: a hiba float32 kerekítés mellett
,
a tűrés
.
A második sor a csatornaváltó blokk kimeneti alakját ellenőrzi:
-ból
lett, a térbeli méret
maradt, mert stride
és a
-as
kernel
paddingje megőrzi a méretet, összhangban a 14.1.
Stride, padding, dilation és outputméret formulájával:
A projekciós shortcut itt -es konvolúció, amely a csatornát -ra képezi le, így a főág és a shortcut alakja összeadhatóvá válik.
F1 (fogalmi, nehézség 1). Definiálja az identitás shortcutot és a projection shortcutot. Adjon egy konkrét bemeneti alakot, amelynél kimenete csatornát vált, és mondja meg, miért nem értelmes az összeadás projekció nélkül.
F2 (levezetési, nehézség 2). Legyen lineáris leképezés, . Bizonyítsa be a lemma alapján, hogy , és adjon olyan konkrét mátrixot, amelyre . Számolja ki a sajátértékeket vagy a szinguláris értékeket.
F3 (implementációs, nehézség 2). Implementáljon
ProjectionResBlock stílusú blokkot
,
,
-ös
kernel,
,
stride
mellett. Ellenőrizze a kimeneti alakot egy
bemeneten, és a shortcut nélküli főággal való összehasonlítással a max
hibát. Adja meg a futtatható kódot, a várt shape-et és a tűrést.
F4 (hibakeresési, nehézség 2). Az alábbi kód hibás:
class BrokenBlock(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(2, 4, 3, padding=1)
self.conv2 = nn.Conv2d(4, 4, 3, padding=1)
self.relu = nn.ReLU()
def forward(self, x):
return self.conv2(self.relu(self.conv1(x))) + xLokalizálja a hibát, magyarázza el a következményét, javítsa ki projekciós shortcuttal, és ellenőrizze a javítást egy bemeneten. Adja meg a hiba okát, a javított kódot és az ellenőrző futtatás eredményét.
F5 (kísérlettervezési, nehézség 3). Tervezzen kontrollált ablatív kísérletet a GlyphLab adaton, amely a skip út hatását méri az orientációosztályozási feladatra. Rögzítse a kérdést, a baseline-t, a spliteket, a paraméterkeretet, a választási szabályt, a metrikát, a seed-kezelést és az érvényességi korlátokat. Nincs előre megadott győztes; a 17.4. Architektúra összehasonlítása kontrollált keretben fair comparison keretét kövesse.
F1 megoldása. Az identitás shortcut a
képletben az
közvetlen átvezetése a kimenethez, paraméter nélkül. Ez akkor
megengedett, ha
és
alakja megegyezik. A projection shortcut
tanulható lineáris transzformáció, amely a shortcut utat a főág alakjára
hozza. Példa:
,
kimenete
.
Ekkor
nem értelmezhető, mert a csatornadimenzió
;
a PyTorch add művelet shape-mismatch hibát adna. A
projection
-es
konvolúcióval
csatornát képez, így mindkét tag
lesz.
F2 megoldása. A lemmából . Lineáris esetén minden -re, tehát . Válasszuk a diagonális mátrixot
Ekkor
A szinguláris értékek diagonális mátrixnál a főátló elemeinek abszolút értékei: , , tehát
Az ellenőrzés mutatja, hogy a shortcut jelenléte ellenére a Jacobi-mátrix tetszőlegesen rosszul kondicionált lehet, ha spektruma közelébe kerül.
F3 megoldása. A kód:
"""lab/residual_stride.py"""
import torch
import torch.nn as nn
torch.manual_seed(20261020)
device = torch.device("cpu")
dtype = torch.float32
class StrideResBlock(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 4, 5, stride=2, padding=2)
self.conv2 = nn.Conv2d(4, 4, 5, padding=2)
self.shortcut = nn.Conv2d(1, 4, 1, stride=2)
self.relu = nn.ReLU()
def forward(self, x):
F = self.relu(self.conv1(x))
F = self.conv2(F)
return F + self.shortcut(x)
def main():
x = torch.randn(2, 1, 24, 24, device=device, dtype=dtype)
block = StrideResBlock().to(device, dtype)
block.eval()
with torch.no_grad():
y = block(x)
F_ref = block.conv2(block.relu(block.conv1(x)))
err = (y - (F_ref + block.shortcut(x))).abs().max().item()
print("kimeneti alak:", tuple(y.shape))
print("max hiba:", err)
if __name__ == "__main__":
main()A kimeneti méret a 14.1. Stride, padding, dilation és outputméret formulával: , tehát a várt alak . A shortcut -es konvolúció stride -vel, így annak kimenete is . A max hiba tűrése ; a várt érték .
F4 megoldása. A hiba oka: a
BrokenBlock.forward az
bemenetet közvetlenül adja hozzá a főág kimenetéhez, de
csatornaszáma
,
a főág kimenetéé pedig
.
A + művelet PyTorchban shape-mismatch
RuntimeError-t eredményez. A következmény az, hogy a blokk
egyetlen forwardja sem fut le. Javítás: adjunk a konstruktorhoz egy
-es
projekciós konvolúciót, és a forwardban azt használjuk:
class FixedBlock(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(2, 4, 3, padding=1)
self.conv2 = nn.Conv2d(4, 4, 3, padding=1)
self.shortcut = nn.Conv2d(2, 4, 1)
self.relu = nn.ReLU()
def forward(self, x):
F = self.conv2(self.relu(self.conv1(x)))
return F + self.shortcut(x)Ellenőrzés
bemeneten: a kimenet alakja
,
és a (F + shortcut(x)) kifejezés közvetlen kiértékelésével
a max hiba
a
tűrés mellett.
F5 megoldása. Egy lehetséges protokoll:
train/val/test
felosztása, a scene_id mint független megfigyelési egység.
A validáció csak modellválasztásra szolgál; a tesztet a legjobb valós
checkpoint kiválasztása után egyszer értékeljük.A leggyakoribb tévedés az, hogy a skip út jelenlétét globális konvergenciabizonyításnak tekintik. A fenti lemma csak lokális Jacobi-azonosság, és a tetszőleges értéke miatt nem zárja ki a rossz kondicionáltságot. Konkrét ellenpélda: legyen . Ekkor a blokk kimenete minden -re. A shortcut jelen van, a Jacobi mégis , tehát a blokk a bemenet minden információját eltünteti. Egy ilyen blokkokból álló háló bármely mélységben konstans nullát ad, így semmilyen értelmes feladatot nem tanul meg. A skip tehát csak egy lehetséges gradiensutat biztosít; a tényleges tanulási dinamika és a konvergencia továbbra is a nemlineáris optimalizálás tulajdonságaitól függ. A P06 empirikus javulást közöl, nem tételként bizonyított globális garanciát.
Önellenőrző kérdések: (1) Mikor értelmes az
összeadás tenzoralakban? (2) Mi a projection shortcut szerepe
csatornaváltásnál? (3) Igaz-e, hogy ha minden blokk Jacobija
,
akkor a teljes háló Jacobija
?
(4) Mit ellenőriz a laborban a err_same érték? (5) Milyen
állítástípus a P06
javulásra vonatkozó megállapítása?
Korábbi fogalmak visszahívása: a 6.1. Objektív,
reguláris tag és gradienslépés alfejezetből a gradienslépés és a
loss-átlag skálázása; a 11.4. Egy teljes PyTorch
tanítási iteráció alfejezetből a
zero_grad–forward–backward–step
sorrend; a 14.1. Stride, padding, dilation és
outputméret alfejezetből a kimeneti méret formulája; a 14.4. Csoportos és depthwise konvolúció
alfejezetből a csoportos konvolúció csatornakorlátozása, amely a
bottleneck
-es
rétegeinél pontszerű, teljes csatornakapcsolatot jelent; a 16.4. IV. ellenőrző pont: a CNN-operátor teljes
megértése alfejezetből az operátor-specifikáció háromszöge; a 17.4. Architektúra összehasonlítása kontrollált
keretben alfejezetből pedig a fair comparison kerete. A BatchNorm,
amely a teljes P06
blokkban a konvolúciók után áll, a következő egység témája.
A reziduális blokk alakú, ahol a shortcut identitás vagy -es projekció, ha az alak nem egyezik. A blokk Jacobi-mátrixa differenciálható esetén , így a VJP-ben a gradiens az identitásúton és a reziduális ágon is terjed. Ez az azonosság lokális, és nem garantál globálisan jó kondicionáltságot: sajátértékei közel kerülhetnek -hez, a mély szorzat spektruma pedig akkor is elfajulhat, ha az egyes tényezők jól kondicionáltak. A labor két kis blokkot épít és ellenőriz. A BatchNorm-mechanizmus a következő egységben következik.
A 18.2. Batch normalization train és eval módban a BatchNorm tanítási és kiértékelési módját vezeti be a P07, P08 és D12 források alapján, és visszatér az itteni reziduális blokkra, amikor a normalizálást a főágba illeszti.