Ez az egység a klasszikus konvolúciós hálózatok kiindulópontját, a LeNet-családot és azon belül a LeNet-5 szerkezetét vizsgálja. A 12.4. III. ellenőrző pont: önállóan tanuló kis háló egységből a teljes tanítási ciklust, a 14.4. Csoportos és depthwise konvolúció egységből a csatornakapcsolatok korlátozását, a 16.4. IV. ellenőrző pont: a CNN-operátor teljes megértése egységből pedig a CNN-operátor alak-, gradiens- és szimmetriavizsgálatát ismerjük. Itt a cél az, hogy egy konkrét réteggráfon mutassuk meg: hogyan kapcsolódnak a térbeli szűrők, a csatornabővítés, az alulmintavételezés és az osztályozófej. Induló kérdés: egy 32×32-es bemenetre 5×5-ös kernel, stride 1 és padding 0 mellett hány térbeli kimeneti elem adódik, és miért beszélünk mégis „konvolúciós” rétegről akkor, amikor a keretrendszer keresztkorrelációt számol?
A [P01] tanulmány a LeNet-5 hálózatot a kézírásos dokumentumfelismerésre vezette be. Az eredeti architektúrát a cikk II.B szakasza (7–8. o., 2. ábra, I. táblázat, (5)–(7) egyenlet) írja le; az alábbi táblázat ebből készült, a jobb oldali oszlopok a labor saját, egyszerűsített TinyLeNet-Glyph változatát mutatják, amelyet a 4. szakasz vezet le. A saját változat nem az eredeti reprodukciója.
| Réteg | Eredeti LeNet-5 [P01] | Param. [P01] | TinyLeNet-Glyph (labor) | Param. |
|---|---|---|---|---|
| bemenet | ; háttér , előtér (átlag kb. 0, variancia kb. 1) | – | , | – |
| C1 | 6 térkép, konvolúció, kimenet | 156 | Conv2d(1,6,5), ReLU,
|
156 |
| S2 | , nem átfedő; a négy bemenet összege tanulható együttható tanulható bias, majd szigmoid | 12 | MaxPool2d(2), paraméter nélkül |
0 |
| C3 | 16 térkép, , részleges kötés az I. táblázat szerint | 1516 | Conv2d(6,16,5) teljes kötés, ReLU,
|
2416 |
| S4 | mint S2, | 32 | MaxPool2d(2),
|
0 |
| C5 | 120 térkép, konvolúció minden S4-térképen, kimenet | 48 120 | Linear(144,120), ReLU |
17 400 |
| F6 | 84 egység, teljes kötés C5-höz | 10 164 | Linear(120,84), ReLU |
10 164 |
| kimenet | 10 euklideszi RBF-egység, , kézzel választott, kezdetben rögzített kódokkal | – | Linear(84,3) logit, cross-entropy |
255 |
Az eredeti hálóban a C1–F6 rétegek súlyozott összegét a skálázott tangens hiperbolikus függvény nyomja össze: , , ([P01] (5)–(6) egyenlet; választását a cikk A. függeléke indokolja). A cikk szerint a teljes háló 340 908 kapcsolatot, de a súlymegosztás miatt csak mintegy 60 000 szabad paramétert tartalmaz.
A LeNet-5 szokásos rétegsora:
Itt egy 5×5-ös, 6 kimeneti csatornájú szűrőréteg, egy 2×2-es alulmintavételező réteg, 16 csatornát állít elő, ismét alulmintavételez, az 5×5-ös bemenetet 120 csatornára képezi, pedig 84 rejtett egységet tartalmazó affin réteg, amelyet a kimenet követ. A mai keretrendszerekben megvalósítható egyszerű affin rétegként is, mert a bemeneti térbeli kiterjedés itt már 5×5.
Konvolúciós réteg mint keresztkorreláció. A 13.1. Kép mint diszkrét többcsatornás jel NCHW-indexelését és a 13.2. Konvolúció és cross-correlation definícióját használjuk. Egyetlen output elem:
Ez kernelmegfordítás nélküli keresztkorreláció. A „konvolúciós réteg” elnevezés a szakirodalmi hagyományból jön; a tanulás szempontjából a megfordítás nem lényeges, mert a súlyokat amúgy is tanuljuk. A LeNet-ben a térbeli szűrés után nemlineáris aktiváció, az eredeti hálóban a tanh-családhoz tartozó telítődő függvény következik, nem a mai ReLU.
Subsampling réteg. A „subsampling” a LeNet kontextusában nem azonos a modern max-poolinggal. Az eredeti -rétegek a 2×2-es cellát tanulható súllyal és eltolással összegzik, majd telítődő aktivációt alkalmaznak; ez tehát tanulható paramétereket tartalmaz. A modern max-pool ezzel szemben paraméter nélküli, cellánkénti maximumot vesz:
ahol a LeNet-szerű beállításban . A két művelet nem reprodukciós értelemben sem felcserélhető: más a gradiens útja, és más a paraméterszám.
Classifier head. A konvolúciós rétegek által előállított feature mapokat a háló vége lapítja, majd affin rétegekre bízza az osztályozást. A LeNet-5 esetében ez a szakasz. A modern változatokban a kimenet előtti affin réteg véges logitokat ad; a softmax és a cross-entropy kapcsolatát a 7.2. Softmax és többosztályos cross-entropy tárgyalta, és a könyv főútja szerint a loss logitokat vár, nem softmaxolt valószínűséget.
Az eredeti LeNet-5 leírása és a mai könyvtári példák közötti eltérés gyakran elmosódik. A három legfontosabb különbség:
A „historical variant” kifejezés tehát nem csupán címke: a változat megnevezéséhez pontosan rögzíteni kell a bemeneti méretet, az aktivációt, a pooling típusát, a csatornakötést és a kimeneti réteg szerkezetét.
A laborhoz választott saját változat:
A térbeli kimeneti méret a 14.1. Stride, padding, dilation és outputméret formulájával:
A számítás lépésről lépésre:
| Réteg | Kimeneti alak | Paraméterképlet | Paraméterszám |
|---|---|---|---|
| C1 | 6×20×20 | 156 | |
| P1 | 6×10×10 | 0 | 0 |
| C2 | 16×6×6 | 2416 | |
| P2 | 16×3×3 | 0 | 0 |
| F1 | 120 | 17 400 | |
| F2 | 84 | 10 164 | |
| F3 | 3 | 255 |
A bias-tag minden kimeneti csatornán, illetve kimeneti egységen külön számít. Az összeg:
Ez nem a LeNet-5 eredeti paraméterszáma; ez a saját laborváltozaté. A fenti levezetés típusa egyszerű alak- és paraméterszámítás, nem tételbizonyítás; az ellenőrzés módja a képletek indexhatár-vizsgálata és a későbbi kód.
A labor nem tanít, hanem a modult és az adatbetöltést ellenőrzi.
Munkakönyvtár: lab/. Az alábbi kód futtatható, de a jelen
szövegben a kimenet jelölt szemléltetés, mert a kiírt értékek képletből
származnak; a hallgató a python tiny_lenet_check.py
paranccsal futtassa.
# lab/tiny_lenet_check.py
import numpy as np
import torch
import torch.nn as nn
torch.manual_seed(20261020)
device = torch.device("cpu")
dtype = torch.float32
class TinyLeNetGlyph(nn.Module):
"""LeNet-szeru, de nem az 1998-as LeNet-5 pontos reprodukcioja:
teljes konvolucios C3, ReLU, modern max-pool, harom GlyphLab-osztaly."""
def __init__(self, in_channels=1, num_classes=3):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(in_channels, 6, kernel_size=5, stride=1, padding=0),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, kernel_size=5, stride=1, padding=0),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.classifier = nn.Sequential(
nn.Flatten(),
nn.Linear(16 * 3 * 3, 120),
nn.ReLU(),
nn.Linear(120, 84),
nn.ReLU(),
nn.Linear(84, num_classes),
)
def forward(self, x):
return self.classifier(self.features(x))
images = np.load("data/images.npy").astype(np.float32)[:4] # (4,1,24,24)
x = torch.from_numpy(images).to(device=device, dtype=dtype)
model = TinyLeNetGlyph().to(device=device, dtype=dtype)
with torch.no_grad():
logits = model(x) # a CrossEntropyLoss logitsot var, softmax nelkul
n_params = sum(p.numel() for p in model.parameters())
assert logits.shape == (4, 3), logits.shape
assert n_params == 30391, n_params
assert torch.isfinite(logits).all()
print("logits shape:", tuple(logits.shape))
print("parameter count:", n_params)
print("first-batch logits finite:", bool(torch.isfinite(logits).all()))Ellenőrzés: a shape-nak
-nek,
a paraméterszámnak
-nek
kell lennie; a Flatten a features kimenet
lapításából
-et
ad. A seed a súlyok inicializálását rögzíti, de a konkrét
logit-értékeket a futtatási naplónak kell rögzítenie; a könyv itt nem
közöl előre nem mért számokat.
F1 (fogalmi, nehézség 1). Magyarázd el, miért nem
ugyanaz a művelet az eredeti LeNet-5 S2/S4
alulmintavételező rétege és a modern MaxPool2d(2,2). Adj
egy konkrét, 2×2-es számpéldát, amelyen a maximum és a tanulható
súlyozott összeg különböző viselkedését megmutatod; jelöld, melyik
melyik.
F2 (levezetési, nehézség 2). Egy új változat specifikációja: input ; conv1: 6 csatorna, 5×5, stride 1, padding 0; MaxPool 2×2, stride 2; conv2: 16 csatorna, 3×3, stride 1, padding 1; MaxPool 2×2, stride 2; fc1: 400→120; fc2: 120→84; kimenet: 84→3. Vezesd le minden réteg kimeneti alakját és a teljes paraméterszámot. Mutasd meg, miért tartja meg a padding=1 a 10×10-es térbeli méretet a 3×3-as kernelnél.
F3 (implementációs, nehézség 1). Implementáld a
TinyLeNetGlyph modult a szöveg 4. szakaszának
specifikációja szerint úgy, hogy a paraméterszám
legyen. Futtass rajta egy
-es
véletlen float32 tensort, és ellenőrizd a kimeneti alakot. A megoldáshoz
add meg a futtatható kódot.
F4 (hibakeresési, nehézség 2). Az alábbi töredékben
a második poolingréteg már szerepel, de a Linear(576,120)
bemeneti méretét nem igazították hozzá. Lokalizáld az alakhibát, írd le
a futtatási következményt, javítsd a kódot, és add meg azt az assertet,
amellyel a javítást visszaellenőrzöd.
self.features = nn.Sequential(
nn.Conv2d(1, 6, 5), nn.ReLU(), nn.MaxPool2d(2, 2),
nn.Conv2d(6, 16, 5), nn.ReLU(), nn.MaxPool2d(2, 2),
)
self.fc = nn.Linear(16 * 6 * 6, 120) # hibás: a bemenet valójában 16*3*3F5 (kísérleti/transzfer, nehézség 3). Tervezz kísérletet annak eldöntésére, hogy a GlyphLabon tanított kis LeNet-típusú modell első konvolúciós rétegének szűrői nagyobb térbeli lokalitással bírnak-e, mint ugyanazon seed melletti véletlen inicializálású súlyok. Add meg a hipotézist, a scene_id-alapú felosztást, a baseline-t, a lokalitás kvantitatív definícióját, a választási protokollt és az érvényességi határt.
F1 megoldás. Az eredeti -réteg a 2×2-es cella értékeit tanulható súllyal és eltolással összegzi, majd telítődő aktivációt alkalmaz; a max-pool ezzel szemben a -ot veszi, és nincs tanulható paramétere. Példa: . A max-pool kimenete ; egy súlyozott összeg kimenete , ami itt véletlenül egyezik, ezért jobb ellenpélda: -nél a max=, az átlagos összeg . Az aktiváció és a gradiens útja is eltérő. Pontosabb formula a teljes [P01] cikkből ellenőrizendő.
F2 megoldás. A -képletet használjuk. C1: , alak . P1: , alak . C2: , , tehát a padding éppen a kernel okozta 2 pixeles csökkenést kompenzálja: , ezért a térbeli méret 10×10 marad; alak . P2: , alak ; flatten . A paraméterek:
Összesen: . Alakellenőrzés: a lapítás valóban fc1 bemenete; az osztályozó minden ponton -soros marad.
F3 megoldás. A teljes kód az 5. szakaszban megadott
TinyLeNetGlyph; a kis különbség a batch méret. A modul
után:
import torch
torch.manual_seed(20261020)
x = torch.randn(2, 1, 24, 24, dtype=torch.float32)
model = TinyLeNetGlyph()
out = model(x)
assert out.shape == (2, 3)
assert sum(p.numel() for p in model.parameters()) == 30391A paraméterszám egzakt egész, ezért tűrésre nincs szükség; a shape-nál ugyanígy.
F4 megoldás. A második MaxPool2d(2,2)
után a konvolúciós rész kimenete
,
vagyis lapítva
elem. A hibás Linear(576,120) ezzel szemben
bemeneti elemet vár, ezért
batch esetén a továbbítás
RuntimeError: mat1 and mat2 shapes cannot be multiplied
kivételt eredményez. A javított rétegrész:
self.features = nn.Sequential(
nn.Conv2d(1, 6, 5), nn.ReLU(), nn.MaxPool2d(2, 2),
nn.Conv2d(6, 16, 5), nn.ReLU(), nn.MaxPool2d(2, 2),
)
self.fc = nn.Linear(16 * 3 * 3, 120)Ellenőrzés a modul forward metódusában alkalmazott
flatten után:
x = torch.randn(4,1,24,24); h = self.features(x).flatten(1); assert h.shape == (4,144); assert self.fc(h).shape == (4,120).
A teljes modell kimenete
.
F5 megoldásminta. Hipotézis: a tanított modell első
rétegének szűrői kevésbé véletlenszerűen oszlanak el a 5×5-ös kernelben,
mint a nem tanított baseline. Protokoll: a GlyphLab
metadata.csv scene_id oszlopa szerint 120
train/30 val/30 test jelenetet rögzítünk; a modell csak az
images.npy-t kapja. Baseline: azonos architektúra azonos
seeddel inicializálva, tanítás nélkül. Változó tényező: a train spliten
futtatott, például 20 epochos, Adam-léptetésű tanítás; a kiválasztáshoz
val loss-t használunk, a testet nem érintjük. Lokalitás: minden 5×5-ös
szűrőre
vagyis a középső 3×3-as mag által hordozott energia aránya. A metrika a hat szűrő mediánja. A bizonytalanságbecsléshez több, jelenetszinten újramintázott tanítóhalmazon újratanított modell–baseline párt használunk; egyetlen rögzített modellnél a tesztjelenetek újramintázása nem változtatná a szűrőstatisztikát, ezért abból nem származna értelmes intervallum. Átadandó: a tanított és baseline mediánok nyers értékei, valamint az újratanítások páros különbségének intervalluma. Érvényességi határ: a GlyphLab három orientációt tartalmaz, ezért az eredmény nem igazol általános „élreprezentációs” állítást; a lokalitásnövekedés egyetlen toy-adathalmazra vonatkozik.
A leggyakoribb tévedés, hogy egy modern, LeNet-szerű modellt
automatikusan „az eredeti LeNet-5 reprodukciójának” neveznek.
Ellenpélda: a fenti TinyLeNetGlyph 24×24-es bemenetet,
ReLU-t, max-poolingot, teljesen konvolúciós C3-at és 3 osztályos
logit-kimenetet használ; az eredeti LeNet-5 ezzel szemben 32×32-es
bemenetet, telítődő aktivációt, tanulható súlyozású subsamplinget,
C3-nál nem teljes csatornakötést és más kimeneti felépítést alkalmaz. Ha
valaki a laborváltozat
paraméterét az 1998-as cikk paraméterszámának állítaná be, téves
összehasonlítást tenne, mert a C3-kötési tábla eltérése miatt a két háló
súlymátrixa szerkezetileg sem azonos. A korrekt megfogalmazás:
„LeNet-típusú, modernizált változat”.
Önellenőrzés: a
-képletbe
a padding, stride és kernel értékek visszahelyettesítése; a
paraméterszámok kézi összegzése a sum(p.numel())
ellenőrzéssel szemben; annak ellenőrzése, hogy a lapítás mérete mindig a
feature map térfogata. Korábbi fogalom visszahívása: a 16.1. Eltolási equivariance és invariance szerint a
konvolúciós réteg eltolási ekvivarianciát ad, de invarianciát önmagában
nem; a LeNet-ben az invariancia felé az alulmintavételező rétegek
lépnek. Az 5.1. Esemény, feltétel és véletlen
változó tanulsága szerint a végső softmax kimenet normalizált
valószínűségeloszlás, a három logit önmagában nem az.
A LeNet-5 a konvolúciós hálózatok történeti alapváltozata: a P01 tanulmány a kézírásos dokumentumfelismerésre vezette be. A réteggráf három komponenst kapcsol össze: térbeli keresztkorrelációs rétegeket, 2×2-es alulmintavételező rétegeket és affin osztályozófejet. Az eredeti LeNet-5 32×32 bemenetet, telítődő aktivációt, súlyozott subsamplinget, C3-nál részleges csatornakötést és más kimenetet használ; a mai könyvtárakban gyakori max-poolos, ReLU-s, teljes konvolúciós változat ettől eltér, ezért nem nevezhető pontos reprodukciónak. A laborban megvalósított TinyLeNet-Glyph 24×24-es GlyphLab-bemenetre épül, 30 391 tanulható paramétere van, és logitokat ad három osztályra.
A következő, 17.2. AlexNet és a kísérleti kontextus egység az AlexNet hálózatot vizsgálja: ott a méretnövekedés, a kétutas GPU-elrendezés és a ReLU-alapú gyorsabb tanítás hoz új tervezési döntést.