19.4 ConvNeXt és a recept/architektúra elválasztása

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

Ebben az alfejezetben a ConvNeXt-család [P11] alapgondolatát vizsgáljuk: egy korábbi, jól ismert CNN-architektúrát fokozatosan modernizálunk a vision transformer korszakában megszokott építőelemekkel és tanítási recepttel, miközben a modell továbbra is konvolúciós háló marad. A cél nem egy konkrét modellváltozat betanítása, hanem a recept és az architektúra fogalmának szétválasztása. Előismeret: a 19.3. EfficientNet és architektúraskálázás skálázási tengelyei, a 18.4. Reziduális blokk ablatív vizsgálata reziduális blokk és ablatív protokoll, valamint a 15.4. Független gradiensellenőrzési protokoll numerikus ellenőrzés gondolata. A 7.4. Numerikus stabilitás és jelentésmegőrző implementáció numerikus stabilitási eredményeire csak annyiban támaszkodunk, hogy a labor normálásait stabilan implementáljuk.

Induló kérdés: ha egy 2015-ös stílusú ResNet-50-szerű hálót változatlanul hagyunk, de a modern transformer-recepttel tanítjuk, mi változik? És ha csak egyetlen építőelemet cserélünk le, hogyan tudjuk eldönteni, hogy a javulás az új elemtől vagy a megváltozott recepttől származik?

19.4.2 Recept és architektúra: két külön szerződés

Definíció (architektúra). Egy konvolúciós háló architektúrájának nevezzük mindazokat a döntéseket, amelyek a forward függvényt írják le: a rétegek sorrendje, a kernelméretek, stride-ok, paddin­gek, csatornaszámok, aktivációk, normalizálások, skip-kapcsolatok és downsampling-rétegek. Az architektúra a paraméterek terét és a függvényosztályt határozza meg.

Definíció (tanítási recept, training recipe). A recept mindaz, ami a paraméterek keresését vezérli, de nem változtatja meg a forward függvényt: az optimalizáló (például AdamW a 6.4. Momentum és adaptív optimizálók szerint), a learning rate és annak ütemezése, a weight decay, az epochok száma, a batch méret, az augmentáció, a label smoothing, a stochastic depth, a keverési (mixup) stratégia, valamint az inicializálás és a kiértékelési protokoll.

A két fogalom azért kritikus, mert egy architektúra teljesítményét soha nem a recepttől függetlenül mérjük. A [P11] absztraktja szerint a munka egy standard ResNet-50-et modernizál fokozatosan, és a lépések között a tanítási recept cseréje is szerepel. A jelen kéziratban az absztrakt szintjén ellenőrzött állításokat idézzük; a pontos lépésenkénti eredményekhez a cikk teljes kísérleti szakasza szükséges.

Heurisztika (recept-architektúra szétválasztás). Ha egy új blokk bevezetésekor a baseline-t régi, az új modellt pedig új recepttel tanítjuk, akkor a mért különbség nem tulajdonítható egyetlen tényezőnek. A tisztességes összehasonlítás matched recipe-t igényel: mindkét modellt ugyanazzal a protokollal tanítjuk, és csak a vizsgált architekturális elemet változtatjuk. Ez ugyanaz az ablatív elv, amelyet a 18.4. Reziduális blokk ablatív vizsgálata egységben a skip-kapcsolatra bevezettünk.

19.4.3 A ConvNeXt-blokk formális leírása

A ConvNeXt-blokk a [P11] által vizsgált modernizálási lépésekből áll össze. A következő blokk a szakirodalomban ConvNeXt-blokk néven ismert sorrendet követi; a konkrét modellváltozat részletei a forrás szerinti konfigurációtól függhetnek.

Legyen a batchelt bemenet X∈ℝN×C×H×WX\in\mathbb R^{N\times C\times H\times W}, ahol CC a csatornaszám, H,WH,W a térbeli méret. Legyen Kdw∈ℝC×1×k×kK_{dw}\in\mathbb R^{C\times 1\times k\times k} a depthwise konvolúció súlya, azaz a PyTorch nn.Conv2d(dim, dim, kernel_size=k, padding=p, groups=dim, bias=False) réteg súlyalakja (Cout,Cin/g,kh,kw)(C_{\mathrm{out}}, C_{\mathrm{in}}/g, k_h, k_w) a [D10] szerint; itt g=Cg=C és a kimeneti csatornák száma is CC. A depthwise réteg kimenete

(DWk,p(X))n,c,i,j=∑a=0k−1∑b=0k−1Kdw[c,0,a,b]Xn,c,i+a−p,j+b−p, (\mathrm{DW}_{k,p}(X))_{n,c,i,j} =\sum_{a=0}^{k-1}\sum_{b=0}^{k-1} K_{dw}[c,0,a,b]\,X_{n,c,\,i+a-p,\,j+b-p},

ahol pp a szimmetrikus zero padding, és a kényelem kedvéért stride s=1s=1; a kimeneti méret ekkor Hout=HH_{\mathrm{out}}=H, ha p=(k−1)/2p=(k-1)/2.

Legyen LNC\mathrm{LN}_C a csatornadimenzióra vonatkozó LayerNorm: minden n,h,wn,h,w pozícióban a CC hosszúságú vektort normalizáljuk,

LNC(z)c=γczc−μ(z)σ2(z)+ϵ+βc,μ(z)=1C∑c=1Czc,σ2(z)=1C∑c=1C(zc−μ(z))2. \mathrm{LN}_C(z)_c =\gamma_c\frac{z_c-\mu(z)}{\sqrt{\sigma^2(z)+\epsilon}}+\beta_c, \qquad \mu(z)=\frac1C\sum_{c=1}^C z_c,\quad \sigma^2(z)=\frac1C\sum_{c=1}^C (z_c-\mu(z))^2.

A μ,σ2\mu,\sigma^2 itt a csatornadimenzió menti átlag és variancia, nem a mini-batch statisztika; ez a döntés eltér a BatchNorm 18.2. Batch normalization train és eval módban alfejezetben tárgyalt train/eval állapotkezelésétől. Legyen EE az expand arány, σ\sigma a GELU aktiváció, We∈ℝEC×C×1×1W_e\in\mathbb R^{EC\times C\times 1\times 1} és Wp∈ℝC×EC×1×1W_p\in\mathbb R^{C\times EC\times 1\times 1} két 1×1 konvolúció (bias nélkül), végül γ∈ℝC×1×1\gamma\in\mathbb R^{C\times 1\times 1} a LayerScale csatornánkénti szorzója. A blokk:

B(X)=X+γ⊙Wpσ(WeLNC(DWk,p(X))). B(X)=X+\gamma\odot W_p\,\sigma\!\big(W_e\,\mathrm{LN}_C(\mathrm{DW}_{k,p}(X))\big).

A ⊙\odot itt csatornánkénti, térben broadcastolt szorzás. A kifejezés dimenzióellenőrzése: XX alakja (N,C,H,W)(N,C,H,W); a depthwise kimenete (N,C,H,W)(N,C,H,W); LNC\mathrm{LN}_C ugyanekkora; WeW_e kimenete (N,EC,H,W)(N,EC,H,W); a GELU ezt őrzi meg; WpW_p visszavetíti (N,C,H,W)(N,C,H,W)-re; a γ\gamma-szorzás és a reziduális összeadás ugyanebben a térben marad. A skip tehát akkor identitás, ha a térbeli méret és a csatornaszám nem változik; downsampling esetén külön shortcut szükséges.

Figyeljünk a különbségre a 19.2. MobileNet és inverted residual Mob­ileNetV2 inverted residual blokkjához képest: ott a depthwise konvolúció a kiterjesztett, széles csatornatérben futott. Itt a depthwise réteg a keskeny CC csatornán kever térben, és az 1×1 rétegek végzik a csatornák keverését. A „large kernel” döntés azt jelenti, hogy a depthwise kernel tipikusan 7×7-es is lehet, mert a költsége Ck2C k^2 arányú, nem C2k2C^2 k^2.

19.4.4 Költség- és paraméterszámítás kézi példán

A blokk költségmodelljét explicit feltevésekkel vezetjük le. Feltesszük: stride 1, szimmetrikus padding, így Hout=HH_{\mathrm{out}}=H, Wout=WW_{\mathrm{out}}=W; a konvolúciós MAC-ot számoljuk, a GELU és LayerNorm szorzásait itt nem. A depthwise réteg MAC-igénye NHWCk2N H W C k^2, mert minden kimeneti pixelre csatornánként k2k^2 szorzás-összeadás jut. Az első 1×1 réteg NHWC(EC)N H W C (EC), a második NHW(EC)CN H W (EC) C MAC. Összesen:

MAC=NHWC(k2+2EC). \mathrm{MAC}=N H W C\big(k^2+2EC\big).

A paraméterszám bias nélkül: a depthwise súly Ck2C k^2, a két 1×1 súly C(EC)+(EC)C=2EC2C(EC)+(EC)C=2EC^2, a LayerNorm γ,β\gamma,\beta együtt 2C2C, a LayerScale CC; így

Npar=Ck2+2EC2+3C. N_{\mathrm{par}}=C k^2+2EC^2+3C.

Példa: legyen N=1N=1, C=2C=2, E=4E=4, H=W=8H=W=8, k=5k=5, p=2p=2. A MAC-összeg:

1⋅8⋅8⋅2(25+2⋅4⋅2)=128⋅(25+16)=128⋅41=5248. 1\cdot 8\cdot 8\cdot 2\,(25+2\cdot 4\cdot 2) =128\cdot(25+16)=128\cdot 41=5248.

A paraméterszám: 2⋅25+2⋅4⋅4+3⋅2=50+32+6=882\cdot25 + 2\cdot4\cdot4 + 3\cdot2 = 50+32+6=88. Az alakellenőrzés: bemenet (1,2,8,8)(1,2,8,8), kimenet ugyanekkora, mert p=2p=2 és stride 1.

A példa mutatja a depthwise döntés hatását: ha a k=5k=5 kernelt közönséges, g=1g=1 csoportú konvolúcióként használnánk, a súly alakja (2,2,5,5)(2,2,5,5) lenne, 100100 paraméterrel, és a MAC NHWC2k2=3200N H W C^2 k^2=3200 lenne csak a térbeli rétegben. A csoportosítás tehát a paraméter- és műveletszámot is csökkenti, de a kapacitást is korlátozza; ez heurisztikus kompromisszum, nem automatikus pontossági garancia.

19.4.5 A [P11] kísérleti összevetés feltételeinek jegyzéke

A levezetési kötelezettség itt nem egy tétel, hanem egy kísérleti állítás feltételeinek pontos rögzítése. Mivel a [P11] forrásstátusza „arXiv-rekord és absztrakt ellenőrizve”, a részletes módszertani állításokhoz a cikk releváns szakaszainak olvasása szükséges. Amit az absztrakt szintjén rögzíthetünk:

  1. A kiinduló modell egy standard ResNet-50-szerű konvolúciós háló.
  2. A modernizálás fokozatosan cseréli az építőelemeket a vision transformer tervezési nyelvéhez közelítve.
  3. Az összehasonlítási partner vision transformer architektúra, nem pedig csak korábbi CNN-baseline.

Amit egy pontos reprodukcióhoz rögzíteni kell, de a jelen kézirati státusz mellett nem idézünk:

A cikk absztraktjának állítását idézni csak azzal a korláttal szabad, hogy a kísérleti táblázatok részletei nem ellenőrzöttek ebben a kéziratban. A fejezet ezért nem tesz a cikkben szereplő kísérleti táblázatokból származó, itt nem igazolt számszerű állítást.

19.4.6 Labor: mini blokk és ablatív terv

A labor célja egy kisméretű, ConvNeXt-szerű blokk implementálása a GlyphLab méretosztályában, benchmark-szám kitalálása nélkül. A következő kód e kéziratban jelölt implementáció; az alak- és paraméterellenőrzést a fenti kézi számítással vetjük össze. Futtatás a lab/ munkakönyvtárból:

cd /path/to/lab
python n076_mini_block.py
import torch
import torch.nn as nn

torch.manual_seed(20261020)
device = torch.device("cpu")

class MiniConvNeXtBlock(nn.Module):
    """Kisméretű ConvNeXt-szerű blokk, bias nélküli konvolúciókkal.

    Alak: (N,C,H,W) -> (N,C,H,W), stride=1, szimmetrikus padding.
    """
    def __init__(self, dim, expand, kernel):
        super().__init__()
        assert kernel % 2 == 1, "a szimmetrikus paddinghez páratlan kernel kell"
        pad = kernel // 2
        self.dw = nn.Conv2d(dim, dim, kernel_size=kernel,
                            padding=pad, groups=dim, bias=False)
        self.norm = nn.LayerNorm(dim)  # NHWC-ben a csatornadimenzióra
        self.pw1 = nn.Conv2d(dim, dim * expand, kernel_size=1, bias=False)
        self.act = nn.GELU()
        self.pw2 = nn.Conv2d(dim * expand, dim, kernel_size=1, bias=False)
        self.gamma = nn.Parameter(torch.ones(dim, 1, 1))

    def forward(self, x):
        h = self.dw(x)                    # (N,C,H,W)
        N, C, H, W = h.shape
        h = h.permute(0, 2, 3, 1)        # NHWC
        h = self.norm(h)                 # (N,H,W,C)
        h = h.permute(0, 3, 1, 2)        # NCHW
        h = self.pw1(h)
        h = self.act(h)
        h = self.pw2(h)
        h = h * self.gamma
        return x + h

model = MiniConvNeXtBlock(dim=2, expand=4, kernel=5).to(device).to(torch.float32)
x = torch.randn(1, 2, 8, 8, device=device, dtype=torch.float32)
y = model(x)

weight = model.dw.weight
print("dw weight shape:", tuple(weight.shape))     # elvárt: (2,1,5,5)
print("output shape:", tuple(y.shape))             # elvárt: (1,2,8,8)
n_params = sum(p.numel() for p in model.parameters())
print("params:", n_params, "expected:", 88)
print("norm type:", type(model.norm).__name__,
      "| gamma shape:", tuple(model.gamma.shape))

A kód ellenőrzései: a depthwise súly alakja (2,1,5,5)(2,1,5,5), mert groups=2; a kimeneti alak (1,2,8,8)(1,2,8,8); az összesített paraméterszám 88. A nn.LayerNorm(dim) a permutálás után az NHWC utolsó dimenziójára, vagyis a csatornákra normalizál. A seed a súlyok reprodukálhatóságát szolgálja, de a számított kontroll a seedtől független.

A labor második része egy részletes ablatív terv, amelyet a jövőben futtatni lehet. Kérdés: a depthwise kernel mérete (3×3, 5×5, 7×7) hogyan hat a GlyphLab-pontosságra, ha minden más rögzített? Modell: patchify stem 4×4-es, stride 4 konvolúcióval 24×24→6×6, C=8C=8, két mini blokk, global average pooling, lineáris fej 3 osztályra. Változó tényező: csak a depthwise kernel mérete, a padding mindig a méretmegtartáshoz illesztett. Rögzített recept: AdamW, learning rate 10−310^{-3}, weight decay 0.050.05, 50 epoch, batch 32, seed 20261020, a GlyphLab train/val splitje; a legjobb checkpoint kiválasztása kizárólag val loss alapján. Metrika: val accuracy; a tesztet csak a protokoll végén értékeljük. Erőforrás: CPU. A terv nem a [P11] reprodukciója; a GlyphLab 360 képe és 24×24 felbontása más tartomány, ezért a nagy kernel előnye nem vihető át automatikusan.

19.4.7 Feladatok

F1 (fogalmi, nehézség 1). Magyarázza el saját szavaival, mi a különbség a tanítási recept és az architektúra között, és adjon egy konkrét példát arra, hogy két azonos architektúra eltérő recepttel más tanulási görbét adhat.

F2 (levezetési, nehézség 2). Legyen N=1N=1, C=16C=16, H=W=32H=W=32, E=4E=4, k=7k=7, stride 1, szimmetrikus padding. Számítsa ki a blokk MAC-összegét és paraméterszámát bias nélkül. Ellenőrizze a dimenziókat a depthwise réteg súlyán.

F3 (implementációs, nehézség 2). Futtassa a fenti labor kódot, majd módosítsa úgy, hogy dim=8, expand=4, kernel=7, bemenet (2,8,16,16)(2,8,16,16). Ellenőrizze, hogy a kimeneti alak (2,8,16,16)(2,8,16,16), és adja meg a paraméterszámot az itteni képlettel is.

F4 (hibakeresési, nehézség 2). Az alábbi kódrészletben a depthwise réteg definíciója hibás:

self.dw = nn.Conv2d(dim, dim, kernel_size=5, padding=2, bias=False)

Mi a hiba? Milyen következménnyel jár a paraméterszámra és a blokk szándékolt számítási szerkezetére nézve? Adja meg a javítást, és írja le a visszaellenőrzéshez használt súlyalakot.

F5 (kísérleti/transzfer, nehézség 3). Tervezzen matched-recipe ablatív kísérletet a GlyphLab adaton a normalizálás típusának (BatchNorm2d a csatornadimenzióra versus LayerNorm a csatornadimenzióra) összehasonlítására. Adja meg a protokollt: modell, split, baseline, változó tényező, rögzített tényezők, metrika, erőforrás és az érvényességi korlát. Nem kell futtatást végeznie; a tervnek ellenőrizhetőnek kell lennie.

19.4.8 Megoldások és indoklás

F1. Az architektúra a forward függvényt rögzíti: rétegek, kernelméretek, csatornaszámok, skip-kapcsolatok. A recept a paraméterkeresés protokollja: optimalizáló, ütemezés, augmentáció, regularizálás, epochszám. Példa: ugyanaz a háromrétegű MLP AdamW-vel, 100 epochig tanítva, és SGD-vel, rögzített tanulási rátával, 10 epochig tanítva, eltérő tanulási görbét ad; az architektúra azonos, tehát a különbség a receptből származik. Ellenpéldával igazolható a szétválasztás szükségessége: ha a baseline-t régi, az új modellt új recepttel vetjük össze, a hatás nem egyértelműen tulajdonítható az architektúrának.

F2. Adatok: N=1N=1, C=16C=16, H=W=32H=W=32, E=4E=4, k=7k=7. A szimmetrikus padding p=3p=3, így a kimenet 32×3232\times32. MAC:

1⋅32⋅32⋅16(49+2⋅4⋅16)=16384⋅(49+128)=16384⋅177=2,899,968. 1\cdot32\cdot32\cdot16\,(49+2\cdot4\cdot16) =16384\cdot(49+128)=16384\cdot177=2{,}899{,}968.

A paraméterszám: 16⋅49+2⋅4⋅162+3⋅16=784+2048+48=288016\cdot49+2\cdot4\cdot16^2+3\cdot16=784+2048+48=2880. A depthwise súly alakja (16,1,7,7)(16,1,7,7), mert g=16g=16; a két 1×1 súly (64,16,1,1)(64,16,1,1) és (16,64,1,1)(16,64,1,1). A GELU és a LayerNorm nem változtatja az alakot. A broadcast szorzás a γ\gamma (16,1,1)(16,1,1) tenzorral érvényes, mert a csatornadimenzió az első.

F3. A módosított labor kódban dim=8, expand=4, kernel=7, padding 7//2=3, bemenet (2,8,16,16)(2,8,16,16). A depthwise kimenet H=16+2⋅3−7+1=16H=16+2\cdot3-7+1=16, tehát a kimeneti alak (2,8,16,16)(2,8,16,16). A súlyalak (8,1,7,7)(8,1,7,7). A képlet szerint a paraméterszám 8⋅49+2⋅4⋅64+3⋅8=392+512+24=9288\cdot49+2\cdot4\cdot64+3\cdot8=392+512+24=928. A kódban sum(p.numel() for p in model.parameters()) ezzel egyezik. Numerikus tolerancia itt nem szükséges, mert egész számok; shape-ellenőrzés a futtatás kimenetében.

F4. A hiba: a groups argumentum hiánya, ezért a réteg közönséges konvolúció lesz, groups=1. A súly alakja ekkor (5,5,5,5)(5,5,5,5) lenne dim=5 esetén, általában (C,C,k,k)(C,C,k,k), nem pedig a szándékolt (C,1,k,k)(C,1,k,k). Következmény: a paraméterszám C2k2C^2 k^2 lesz Ck2C k^2 helyett, a MAC NHWC2k2N H W C^2 k^2 lesz, és a blokk nem depthwise separable. A javítás: groups=dim. Visszaellenőrzés: model.dw.weight.shape értéke (dim, 1, 5, 5), és a params visszatér a kézi képlettel számolt értékre.

F5. Egy lehetséges terv. Modell: patchify stem 4×4 stride 4, 8 csatorna, két blokk, GAP, lineáris fej 3 osztályra. A blokk norm rétege a változó tényező: vagy nn.BatchNorm2d(8) a depthwise után közvetlenül a NCHW tenzoron, vagy nn.LayerNorm(8) NHWC permutálással. Baseline: BatchNorm-változat, mert a klasszikus CNN-recepthez ez közelebb áll. Rögzített: AdamW, LR 10−310^{-3}, wd 0.050.05, 50 epoch, batch 32, seed 20261020, azonos train/val split, legjobb checkpoint val loss alapján. Metrika: val accuracy; a tesztet egyszer, a protokoll végén értékeljük. Megfigyelési egység: scene_id, a bootstrapot is jelenet-szinten végezzük 5.4. Függő képek, seedek és resampling szerint. Erőforrás: CPU. Érvényességi korlát: egy seed, 360 kép, 24×24 felbontás; az eredmény nem általánosítható nagyobb adatra, és a BN train/eval állapotkülönbségét a kiértékelésnél be kell tartani 18.2. Batch normalization train és eval módban. A negatív eredmény is elfogadható.

19.4.9 Tipikus tévedés, önellenőrzés és visszahívás

A leggyakoribb tévedés: a „korszerű” vagy „modern” címkét örök teljesítményrangsorként kezelni. A ConvNeXt-blokk előnye a [P11] vizsgálati feltételei mellett értelmezendő; e feltételek nélkül az állítás nem vihető át. Ellenpélda heurisztikaként: a 360 képes GlyphLab adaton egy nagy, 7×7 depthwise kernelt használó modern blokk több paraméterrel és nagyobb recepciós mezővel túl is illeszkedhet, miközben egy egyszerű, 3×3-as CNN jobb tesztpontosságot ér el. Ez nem mért eredmény, hanem címkézett gondolatkísérlet; éppen azt mutatja, hogy az adat mérete és a benchmark környezete része az állításnak. Önellenőrzés: minden képletben ellenőriztük a csoportos konvolúció súlyalakját, a szimmetrikus padding képletét és a reziduális összeadás broadcastját. Visszahívás: a 18.4. Reziduális blokk ablatív vizsgálata matched-recipe elve adja az ablatív kontroll alapját, a 15.4. Független gradiensellenőrzési protokoll numerikus ellenőrzés arra emlékeztet, hogy a kód egyezése nem bizonyítja a modell optimalitását, a 7.4. Numerikus stabilitás és jelentésmegőrző implementáció stabil normálás pedig a LayerNorm implementációjánál releváns.

19.4.10 Rövid összefoglaló

A ConvNeXt-példa azt mutatja, hogy a CNN-architektúra modernizálása és a tanítási recept cseréje két külön döntési szint, amelyet matched-recept ablációval lehet szétválasztani. A formális blokk depthwise nagy kernelt, csatorna-LayerNormot, GELU-t, 1×1 expand/project rétegeket és reziduális skipet kombinál; költsége NHWC(k2+2EC)N H W C(k^2+2EC) MAC és Ck2+2EC2+3CC k^2+2EC^2+3C paraméter. A [P11] kísérleti feltételeit csak absztrakt szinten idéztük, a részletes protokoll ellenőrzésre szorul. A labor egy 88 paraméteres mini blokk alak- és paraméterellenőrzését, valamint kernelméret-ablatív tervet ad benchmark-szám nélkül. A „modern” címke nem örök rangsor, hanem adott adat- és receptfeltételekhez kötött állítás.

19.4.11 Következő alkalmazás és források

A következő 20.1. Feature extractor és új fej egység a transzfertanulást kezdi: ott azt vizsgáljuk, hogy egy korszerű CNN-blokkból épített modell súlyait mikor és hogyan lehet más adatra átvinni. Az itteni recept-architektúra szétválasztás közvetlenül szükséges lesz, mert az előtanított modell és az új fej tanítása különböző protokollt igényel.

19.4.12 Felhasznált források