A 13.1. Kép mint diszkrét többcsatornás jel egységben a képet a rácson definiált diszkrét jelként rögzítettük. Ebben az alfejezetben egyetlen szürkeárnyalatos képet vizsgálunk, tehát mátrixot; a batch- és csatornaindexeket most rögzített, nem változó dimenzióknak tekintjük, és az általános alak csak a 14.2. Több bemeneti és kimeneti csatorna egységben kerül elő.
Előismeret: a 13.1. Kép mint diszkrét
többcsatornás jel rácspont-, pixel- és NCHW-fogalma, valamint az 1.2. Szakmailag megalapozott modellezési állítások
állítástípus-osztályozása. Az induló kérdés: ha a
torch.nn.Conv2d nevében a „convolution” szó szerepel, akkor
valóban matematikai konvolúciót számol-e? A válasz nemmel kezdődik, és
ennek a nemleges válasznak a pontos tartalma adja az egység
gerincét.
Kernel. A kernel (szűrőmag, filter kernel) egy kis, méretű valós súlymátrix:
A kernel minden pozícióhoz egy skalár súlyt rendel; a gyakorlatban vagy gyakori, de az alábbi definíciók páratlan méretet nem követelnek meg. A kernel nem kép, hanem egy lokális súlyozási minta: a bemeneti kép minden pozíciójában ugyanazt a darab súlyt használjuk.
Valid cross-correlation. Rögzítsünk egy
bemenetet és egy
,
kernelt. A
kernellel vett, valid tartományú cross-correlation a
következő
kimenetet adja:
ahol
és
.
A kernel bal felső sarka mindig az input
rácspontjához igazodik; onnan az
indexek jobbra és lefelé futnak. A valid szó azt jelenti,
hogy a kernel teljes egészében az input belsejében marad, tehát nem lép
ki a kép határán, és nincs szükség peremfeltételezésre.
Matematikai konvolúció. Ugyanezen a bemeneten és
kernelen a diszkrét, valid tartományú matematikai
konvolúció definíciója az indexkonvenciónkban:
A különbség a bemeneti index irányában van: a kernel 180 fokkal el van forgatva a bemenethez képest. A kernel 180 fokos elforgatását külön operátorral rögzítjük:
A -es kernelre például felcseréli a sorokat és az oszlopokat is: .
Boundary, valid és padding. A fenti képletek a
legegyszerűbb határkezelést testesítik meg: a kernel nem lép ki a
képből, és minden olyan pozíciót kihagyunk, ahol a kernel túllógna. Ezt
nevezzük valid módnak. A gyakorlati rétegek gyakran
nullákkal töltik ki a képet a határon (zero padding): ha mind a négy
oldalon
sornyi ill. oszlopnyi nullát adunk hozzá, akkor a kiterjesztett input
,
és a valid cross-correlation kimeneti mérete
Az általános stride- és dilationfüggő képlet a 14.1. Stride, padding, dilation és outputméret egységben következik; itt mindenhol stride és dilation érvényes.
Bias. A konvolúciós rétegekben minden kimeneti csatornához egyetlen skalár tartozik, amelyet a cross-correlation eredményének minden térbeli elemére rögzített konstansként hozzáadunk:
A tehát nem függ sem a bemenettől, sem a térbeli pozíciótól. A bias nélküli cross-correlation lineáris leképezés: bemenethez tartozik. A bias hozzáadása affin leképezést eredményez; a lineáris és affin megkülönböztetés ugyanaz, mint a 3.2. Mátrixszorzás és affín réteg affin rétegében: lineáris leképezés a nullát nullába viszi, a biasos affin leképezés általában nem.
A gyakorlati könyvtárak dokumentációjának olvasásához szükséges állítást explicit tételként mondjuk ki.
Tétel (valid cross-correlation és konvolúció kapcsolata). Legyen , , , , és legyen a 180 fokos kernelforgatás a fenti definíció szerint. Ekkor minden érvényes kimeneti indexre
Bizonyítás. A matematikai konvolúció definícióját írjuk fel, majd hajtsunk végre indexcserét. Legyen és . Az leképezés bijekció a halmazon, ezért véges összegben az indexek átnevezése nem változtatja az összeg értékét:
A második egyenlőség az indexcserét, a harmadik a definícióját használja; a negyedik a cross-correlation definíciójából adódik. A kimeneti indexhalmaz mindkét oldalon azonos, mert a kernel mérete nem változott.
A tétel fordított iránya ugyanígy bizonyítható, és a F2 feladatban kérjük is. A tartalmi tanulság: a matematikai konvolúció nem más, mint a 180 fokkal elforgatott kernellel vett cross-correlation. A különbség tehát a kernel indexsorrendjében van, nem a bemenetben vagy a kimeneti rácsban.
Következmény. Ha , azaz a kernel 180 fokos forgásszimmetrikus, akkor a cross-correlation és a konvolúció ugyanazt a numerikus eredményt adja minden bemenetre. Az aszimmetrikus kernel a két operátort megkülönbözteti.
A [D10] hivatalos dokumentáció olvasott szakasza szerint a
torch.nn.Conv2d az
bemenet és a
súly cross-correlationját számítja, kernelmegfordítás
nélkül. A dokumentáció jelölésében a súlyt és bemenetet összekötő
a valid 2D cross-correlation operátor; a bias az egyes
kimeneti csatornákra adódik hozzá. Ez a megállapítás itt
szakirodalmi/definíciós állítás a dokumentáció olvasott
szakasza alapján, nem pedig empirikus általánosítás.
Az 1.2. Szakmailag megalapozott modellezési
állítások állítástípus-tanulsága itt közvetlenül alkalmazandó: a
Conv2d osztály neve nem definíció. Aki a matematikai
konvolúciót szeretné PyTorch-ban, annak a kernelt 180 fokban el kell
forgatnia, például torch.flip(K, dims=[2,3]) művelettel. A
tanult hálózatoknál a két konvenció közötti különbség nem „jó” vagy
„rossz”, hanem azt jelenti, hogy ugyanaz a leképezés a megfordított
kernel súlyozásával jelenik meg; előre rögzített, nem tanult szűrőknél
azonban a különbségtétel numerikus hibához vezethet, ahogy az alábbi
kézi példa mutatja.
Legyen a bemenet a 13.1. Kép mint diszkrét többcsatornás jel stílusában egy -as, -beli intenzitású kép:
A kernel aszimmetrikus, tehát a két operátor különbözni fog.
Cross-correlation. A mátrix elemei a fenti definícióval:
Tehát
Matematikai konvolúció. A fokkal elforgatott kernel
A tétel szerint , és ennek elemei:
Tehát
A két kimenet közötti különbség az aszimmetrikus kernel megfordításából adódik; az pozíció értéke véletlenül egybeesik, de a mátrix mégsem azonos. Ha biast adunk a cross-correlation réteghez, a kimenet
A labor a lab/n050/ munkakönyvtárban fut; a kód a
képletekből számított értékeket ellenőrzi, ezért az alábbi várt kimenet
a fenti levezetésből származik, nem pedig a szerző által futtatott
mérésből. A hallgató feladata a futtatás és a toleranciavizsgálat
elvégzése.
# lab/n050/check_convs.py
import numpy as np
X = np.array([[0.2, 0.5, 0.1],
[0.8, 0.3, 0.6],
[0.4, 0.9, 0.7]], dtype=np.float32)
K = np.array([[1.0, 0.0],
[-2.0, 3.0]], dtype=np.float32)
def corr2d_valid(X, K):
H, W = X.shape
kh, kw = K.shape
out = np.zeros((H - kh + 1, W - kw + 1), dtype=np.float32)
for i in range(H - kh + 1):
for j in range(W - kw + 1):
for a in range(kh):
for b in range(kw):
out[i, j] += K[a, b] * X[i + a, j + b]
return out
def conv2d_valid(X, K):
return corr2d_valid(X, np.rot90(K, 2)) # 180 fokos forgatás
Yc = corr2d_valid(X, K)
Yf = conv2d_valid(X, K)
print("cross-correlation:\n", Yc)
print("convolution:\n", Yf)
# Várt:
# cross-correlation: [[-0.5 1.7]
# [ 2.7 0.6]]
# convolution: [[-0.1 1.9]
# [ 2.7 0.4]]Futtatási parancs: python lab/n050/check_convs.py. A
dtype mindenütt float32, az eszköz CPU; random
seedre nincs szükség, mert a függvény determinisztikus,
véletlenszám-generátort nem hív.
A PyTorch-oldal ugyanezt az NCHW-indexeléssel ellenőrzi; a 13.1. Kép mint diszkrét többcsatornás jel permutációjának megfelelően az egycsatornás képet tensorré alakítjuk:
import torch
import torch.nn.functional as F
Xt = torch.tensor(X.reshape(1, 1, 3, 3), dtype=torch.float32)
Kt = torch.tensor(K.reshape(1, 1, 2, 2), dtype=torch.float32)
Yc_torch = F.conv2d(Xt, Kt) # NCHW, stride=1, padding=0
Kt_flipped = torch.flip(Kt, dims=[2, 3])
Yf_torch = F.conv2d(Xt, Kt_flipped) # matematikai konvolúció
print("torch Yc:", Yc_torch.squeeze())
print("torch Yf:", Yf_torch.squeeze())Az ellenőrzés kritériuma:
np.allclose(Yc, Yc_torch.numpy(), atol=1e-6) igaz, és
np.allclose(Yf, Yf_torch.numpy(), atol=1e-6) igaz. A
torch.flip a kernel 180 fokos elforgatását végzi; ez a
PyTorch-alakú kernel esetén a
dimenziók megfordítását jelenti.
F1 feladat — fogalmi, nehézség 1. Különböztesse meg
a valid cross-correlationt és a matematikai konvolúciót.
Adjon olyan kernelcsaládot, amelyre a két operátor minden bemenetre
azonos, és egy konkrét ellenpéldát, amelyre nem. Átadandó: definíciók, a
szimmetria feltétele, egy
-es
aszimmetrikus kernel és egy rövid indoklás.
F2 feladat — levezetési/bizonyítási, nehézség 2. Bizonyítsa be, hogy , majd ezt és a fenti tételt használva mutassa meg, hogy . Átadandó: lépésenkénti levezetés, az indexhalmaz bijektivitásának indoklása és a végeredmény alakellenőrzése.
F3 feladat — implementációs, nehézség 1.
Implementálja a corr2d_valid függvényt, és számítsa ki a
kimenetet a következő bemenetre és kernelre:
Átadandó: kód, a kimeneti mátrix, a kimeneti alak és a belső összegzések indoklása.
F4 feladat — hibakeresési, nehézség 2. Az alábbi
függvény a conv2d_valid nevet viseli, de hibás:
def conv2d_valid_bad(X, K):
H, W = X.shape
kh, kw = K.shape
out = np.zeros((H - kh + 1, W - kw + 1))
for i in range(H - kh + 1):
for j in range(W - kw + 1):
out[i, j] = np.sum(K * X[i:i+kh, j:j+kw])
return outAz elvárt viselkedés a matematikai konvolúció. Lokalizálja a hibát, indokolja a következményét a fenti példán, javítsa a kódot, és ellenőrizze a javítást a várt numerikus kimenettel. Átadandó: hiba oka, következmény, javított kód, ellenőrző futás eredménye.
F5 feladat — kísérleti/transzfer, nehézség 3.
Tervezzen rögzített szűrős kísérletet a GlyphLab
data/images.npy képeire: veszik-e észre a vertical és
horizontal osztályok közötti különbséget a
előre rögzített élkereső kernel és annak
elforgatottja különbözőképpen? Adjon protokollt: kérdés, hipotézis,
adat, megfigyelési egység, kiszámítandó metrika, baseline és
érvényességi korlát. Nem kell előre garantált győztest adni. Átadandó:
protokoll szövege, a kimeneti méret számítása 24×24-es inputra, és annak
indoklása, miért nem használható a test split a szűrő
kiválasztására.
F1 megoldás. A cross-correlation definíciója
,
a konvolúcióé
.
A fenti tétel szerint
;
ha
,
akkor a két kimenet azonos minden
-re,
valid tartományon. A 180 fokos forgásszimmetria feltétele
minden
-re;
ilyen például
Ellenpélda a fenti -es , mert , tehát a két operátor kimenete eltér.
F2 megoldás. A definícióját kétszer alkalmazva:
tehát . A tétel szerint minden kernelre ; válasszuk az kernelt. Ekkor
A kimenet mindkét oldalon
alakú, mert a kernel mérete
alatt változatlan. A bizonyítás az indexcserés levezetés közvetlen
alkalmazása; a tétel feltételei
(,
,
valid tartomány) azonosak, tehát a lépés megengedett.
F3 megoldás. A kód a laboréval azonos; a kimenet , , esetén alakú:
Ellenőrzés: a elem ; a elem . A elemei az összegzési definícióból következnek; a shape a valid képletből , .
F4 megoldás. A hiba oka: a függvény a kernel megfordítása nélkül képezi a és az inputablak elemenkénti szorzatösszegét, tehát valójában cross-correlationt számol, miközben a neve matematikai konvolúciót ígér. A fenti és bemeneten a hibás függvény a cross-correlation eredményét adja vissza a konvolúció várt értéke helyett, így az elvárt kimenettől való eltérés a kernel aszimmetriájából származik. A javítás a kernel 180 fokos forgatása a belső ablakozás előtt:
import numpy as np
def conv2d_valid_correct(X, K):
H, W = X.shape
kh, kw = K.shape
K_rot = np.rot90(K, 2)
out = np.zeros((H - kh + 1, W - kw + 1))
for i in range(H - kh + 1):
for j in range(W - kw + 1):
out[i, j] = np.sum(K_rot * X[i:i+kh, j:j+kw])
return outA várt eredmény a -as példán
az ellenőrző
np.allclose(conv2d_valid_correct(X, K), expected, atol=1e-6)
igaz.
F5 megoldás. Protokoll:
vertical és
horizontal osztályokat a GlyphLab képeken?vertical,
-é
a horizontal osztályon; a diagonal osztályon a
különbség iránya nem garantált.data/images.npy NCHW float32,
data/labels.npy int64; a képtensor indexelését a 13.1. Kép mint diszkrét többcsatornás jel
rögzítette.train részt használjuk.train és szükség
esetén val a leíró megfigyelésre; a test a
későbbi modellválasztás után marad zárva.A kimeneti méret:
,
tehát a valid kimenet
.
Az energia a negatív értékek négyzetre emelésével nem veszíti el az
iránykiértékelést a négyzetes aggregáció szintjén, de az előjeles átlag
is használható; a döntést a protokollban rögzíteni kell. A
test-értékelés kizárásának oka: ha a szűrőt vagy a küszöböt
a testen választanánk, az optimista torzítást vinne a későbbi
modellösszehasonlításba, ahogy az 1.2. Szakmailag
megalapozott modellezési állítások ellenpéldája is mutatja.
A leggyakoribb tévedés: „a torch.nn.Conv2d konvolúciót
számol, mert a neve ezt mondja.” Az ellenpélda a fenti
pár: a torch.nn.functional.conv2d a
mátrixot adja, a matematikai konvolúció viszont
-et,
és a két mátrix különbözik. A D10 dokumentáció ezt a
valid 2D cross-correlation kifejezéssel expliciten rögzíti.
A tévedés következménye: előre definiált szűrőknél (pl. élkereső kernel)
a rossz konvenció rossz numerikus eredményt ad; tanult kerneleknél pedig
az értelmezési dokumentáció lesz hibás, mert a megtanult súly a
megfordított párjával ekvivalens. A 12.2. Checkpoint
és folytathatóság tanulsága itt is érvényes: a checkpointban tárolt
state_dict['weight'] önmagában nem mondja meg, melyik
operátorkonvencióhoz tartozik; a konvenciót a kódverzióval és a
modellkártyával együtt kell nyilvántartani.
Conv2d mint cross-correlation
a dokumentáció olvasott szakaszából származó állítás, a definíció és a
mérési állítás nem felcserélhető.A cross-correlation a kernel bal felső sarkát az input aktuális
pozíciójához illesztve, kernelmegfordítás nélkül képez szorzatösszeget;
a matematikai konvolúció ugyanezt 180 fokkal elforgatott kernellel
végzi. A valid tartományon a két operátor kimeneti mérete azonos, és a
kapcsolatuk
,
ahol
a kernel 180 fokos forgatása. A bizonyítás egy indexcserés bijekcióra
épül. A PyTorch Conv2d dokumentációja cross-correlationt
specifikál; ez előre definiált szűrőknél numerikus különbséget, tanult
kerneleknél ekvivalens, de dokumentálandó konvenciót jelent. A labor a
3×3-as példát NumPy- és PyTorch-oldalon egyaránt ellenőrzi.
A 13.3. Lokális szűrők és tanult reprezentáció ezt a súlyozási műveletet tanult lokális reprezentációként értelmezi: a kernelek mint él-, textúra- és iránydetektorok jelennek meg, és a bias nélküli operátor linearitása a súlymegosztás előnyeinek levezetéséhez ad alapot.