Ez az egység a 13.2. Konvolúció és cross-correlation alfejezetben definiált cross-correlation műveletet lokális, tanulható reprezentációként értelmezi. A rögzített él- és simító kernelek olyan kitüntetett pontok a tanulható kernelterében, amelyek a tanulás nélküli, kézzel megadott súlyozás szerepét játsszák. Előismeret: a cross-correlation és a matematikai konvolúció megkülönböztetése, a valid tartomány és a kernel fogalma a 13.2. Konvolúció és cross-correlation alfejezetből. A labor nem osztályozási kísérlet: ugyanazon bemeneteken vetjük össze két rögzített szűrő válaszát, predikciós állítás nélkül.
Induló kérdés: egy 4×4-es lépcsős mintán egy 2×2-es kernel 3×3-as választ ad. Hány megosztott paraméterrel áll elő ez a 9 kimeneti érték, és mit mondhatunk a kernel súlyösszegéből konstans bemenet esetén?
A [S01] kurzusjegyzet terminológiájában a konvolúciós réteg három szervező gondolata a lokális kapcsolat, a súlymegosztás és az egy kernelhez tartozó feature map fogalma. Mindháromhoz pontos definíciót adunk a mi jelölésünkkel.
Legyen egy bemeneti tensorelem, a kernel, és tekintsük a valid, stride 1, nulla padding, dilation 1 esetet. Ekkor a kimenet alakja
és a rögzített cross-correlation definíciója
A PyTorch Conv2d dokumentációja ezt a kernelmegfordítás
nélküli műveletet specifikálja, miközben convolutionnek nevezi [D10]; mi
a matematikai és programozói konvenciót továbbra is
megkülönböztetjük.
Definíció (lokális recepciós mező). Az (1)-beli kimeneti érték lokális recepciós mezője azon bemeneti indexhármasok halmaza, amelyek a kimeneti értékhez ténylegesen hozzájárulnak:
A halmaz mérete , függetlenül a teljes kép méretétől. Például egy 24×24-es bemenet, egy csatorna és 3×3-as kernel esetén minden kimeneti pixel mindössze 9 bemeneti pixelt lát: a kapcsolat lokális, nem teljesen összekötött.
Definíció (súlymegosztás). A kernel elemei nem függnek az kimeneti pozíciótól. Ugyanaz a súly szorzódik minden olyan pozícióban, ahol a kernel ablak ugyanazt a relatív eltolást vizsgálja. Emiatt a paraméterek száma
ahol az utolsó tag a kimeneti csatornánkénti bias. Ez nem függ -tól és -től, szemben egy olyan affin réteggel, amely a bemenet minden pozíciójához külön súlyt rendelne.
Definíció (feature map). Egy rögzített kimeneti csatornához tartozó térbeli tömb az -adik feature map. A kernel minden térbeli pozícióra ugyanazt a lokális súlyozást alkalmazza, ezért a feature map a kernel és a kép lokális hasonlóságának térbeli eloszlását írja le.
A három fogalom kapcsolata: a lokális recepciós mező azt mondja meg, mely inputokból számolunk, a súlymegosztás azt, hogy ugyanazzal a súlymintával minden pozícióban, a feature map pedig az így kapott térbeli választ.
A simító (smoothing) kernel olyan súlyozott helyi átlag, amely a szomszédos intenzitások különbségét csökkenti. A fejezet kézi példájában a 2×2-es box kernel
Súlyai nemnegatívak, összegük 1. A élkereső (edge) kernel ezzel szemben pozitív és negatív súlyokat is tartalmaz, összege 0; a példánkban a vízszintes átmenetet mérő kernel
A súlyösszeg szerepét rögzíti az alábbi lemma.
Lemma. Ha minden pixele azonos értékű, akkor bármely kernelre
Bizonyítás. Az (1) képletbe -t helyettesítve
ami a kívánt alak. A több csatornára vonatkozó változatban minden csatorna saját konstansával és a csatornánkénti súlyösszegekkel kell számolni. ■
A lemma következménye, hogy konstans képen a bemeneti értéket adja vissza, mert súlyösszege 1; konstans képen azonosan 0 választ ad, mert súlyösszege 0. Az „él” így operatívan definiálható: a kernel konstans tartományt kiolt, és csak intenzitásváltozásra ad nemnulla választ.
A rögzített szűrő a tanulható kernel előképe a következő értelemben: ugyanaz a cross-correlation paramétertér egy pontja. Ha egy tanulás előtti háló kernelét -re állítjuk, akkor a háló a tanulás nulla lépése mellett már egy érdemi, értelmezhető leképezést valósít meg. A tanulás ezután elmozdíthatja a kerneleket, és a megtanult súlyok már nem kötelesek megőrizni sem a nulla súlyösszeget, sem a vizuálisan „élszerű” mintázatot. A rögzített kernel tehát nem a tanulás célja, hanem kontrollált kiindulópont a tanulható leképezések terében.
A tanulható kernel előképének matematikai tulajdonságához először a rögzített kernelű operátor szerkezetét kell tisztázni.
Definíció (rögzített cross-correlation operátor). Rögzített mellett legyen
ahol -t az (1) képlet definiálja. A biasos változat minden kimeneti csatornához egy skalárt ad:
A bias térbelileg az egész feature mapon osztozik.
Tétel (linearitás). A bias nélküli lineáris leképezés: minden bemenetre és minden skalárra
Bizonyítás. Rögzített kimeneti koordinátára a definícióból:
A második egyenlőség a tensorelemek skalárként való összeadását, a harmadik a véges összeg eloszlási tulajdonságát használja. Mivel a koordinátánkénti egyenlőség minden kimeneti pozícióra teljesül, az operátorok egyenlők. ■
A tétel feltételei közül fontos, hogy rögzített: ha maga is tanulható paraméter, akkor a leképezés a párban bilineáris, de -ben továbbra is lineáris. A tétel nem állítja, hogy a tanulási folyamat lineáris; az optimalizáló lépés a kernel súlyainak nemlineáris frissítését jelenti.
Biasos ellenpélda. A biasos nem lineáris, ha valamely . Legyen minden kimeneti csatornára. Ekkor , míg egy lineáris leképezés a zérus vektort kötelezően zérusba viszi, hiszen . Mivel , a leképezés nem lineáris. Az additivitás közvetlenül is sérül:
és
,
ha
.
A biasos operátor tehát affin leképezés: egy lineáris leképezés
és egy konstans eltolás összege. Programozói szempontból a PyTorch
Conv2d alapértelmezésben biasos, ezért önmagában nem
lineáris réteg, míg bias=False mellett a fenti tétel
értelmében az.
Legyen a bemenet
A kernel valid válasza a 3×3-as
mert például az pozíció ablakában
és a konstans felső, illetve alsó sorokban a pozitív és negatív tagok kioltják egymást. A vízszintes él tehát az átmenet sorában ad erős negatív választ; előjele a sötétből világosba való átmenet irányát rögzíti.
A kernel ugyanezen a bemeneten
mert az átmenet sorában a 2×2-es ablak két 0 és két 1 értéket tartalmaz, így átlaga 0.5. A két kernel ugyanazt a lokális ablakot vizsgálja, mégis más invariáns tulajdonságot emel ki: a simító kernel a lokális szintet, az él kernel a lokális különbséget.
Biasos változatként mellett
A bias itt a nulla bemenetre is 1 választ ad, ami a fenti ellenpéldának megfelelően megtöri a linearitást.
A munkakönyvtár a lab/. Az alábbi NumPy-függvény a valid
cross-correlationt számolja, és ellenőrzi a kézi példát.
import numpy as np
def valid_cross_corr2d(x, k):
# x: (H, W), k: (kh, kw), float32; valid cross-correlation
H, W = x.shape
kh, kw = k.shape
y = np.zeros((H - kh + 1, W - kw + 1), dtype=np.float32)
for i in range(y.shape[0]):
for j in range(y.shape[1]):
y[i, j] = np.sum(x[i:i+kh, j:j+kw] * k,
dtype=np.float32)
return y
x_step = np.array([[0,0,0,0],
[0,0,0,0],
[1,1,1,1],
[1,1,1,1]], dtype=np.float32)
k_edge = np.array([[1,1],[-1,-1]], dtype=np.float32)
k_smooth = (1.0/4.0) * np.ones((2,2), dtype=np.float32)
y_edge = valid_cross_corr2d(x_step, k_edge)
y_smooth = valid_cross_corr2d(x_step, k_smooth)
assert y_edge.shape == (3,3)
assert y_smooth.shape == (3,3)
assert np.allclose(y_edge, [[0,0,0],[-2,-2,-2],[0,0,0]],
atol=1e-6)
assert np.allclose(y_smooth, [[0,0,0],[0.5,0.5,0.5],[1,1,1]],
atol=1e-6)
print("shape check OK")A kód dtype=np.float32 bemenettel és akkumulációval
számol; a 0.5 pontosan reprezentálható, a -2 is, így az
atol=1e-6 tolerancia elegendő. A np.sum soron
belül dtype=np.float32 megadása azt biztosítja, hogy a
közbülső összeg sem lép át váratlanul float64-be, bár itt a
kis értékek miatt a numerikus különbség nem lenne érdemi.
A labor célja nem predikció, hanem ugyanazon bemenetek két
leképezésének összehasonlítása. Input: data/images.npy
alakja (360,1,24,24), float32, a GlyphLab
szerződés szerint. A kernel mérete 3×3; a valid kimenet 22×22. A két
kernel:
A mérés a válaszok abszolútérték-összege képenként; ez deskriptív
mennyiség, nem osztályozási pontszám. A szkriptet a
lab/n051_filter_response.py fájlba mentve futtatjuk:
python n051_filter_response.py \
--images data/images.npy \
--labels data/labels.npy \
--metadata data/metadata.csvA teljes szkript:
"""Két rögzített szűrő deskriptív válasza GlyphLab mintákon.
A script nem végez predikciót; a válaszabszolútösszeg csak
ugyanazon bemenet két leképezésének összevetésére szolgál.
Seed nem szükséges, mert nincs véletlen választás; a dtype
float32, az eszköz CPU.
"""
import argparse
import numpy as np
SEED = 20261020 # nem használt: determinisztikus kiválasztás
DTYPE = np.float32
DEVICE = "cpu"
def valid_cross_corr2d(x, k):
H, W = x.shape
kh, kw = k.shape
y = np.zeros((H - kh + 1, W - kw + 1), dtype=DTYPE)
for i in range(y.shape[0]):
for j in range(y.shape[1]):
y[i, j] = np.sum(x[i:i+kh, j:j+kw] * k,
dtype=DTYPE)
return y
def main():
p = argparse.ArgumentParser()
p.add_argument("--images", default="data/images.npy")
p.add_argument("--labels", default="data/labels.npy")
p.add_argument("--metadata", default="data/metadata.csv")
args = p.parse_args()
images = np.load(args.images).astype(DTYPE) # (360,1,24,24)
labels = np.load(args.labels) # (360,) int64
k_smooth = (1.0/9.0) * np.ones((3,3), dtype=DTYPE)
k_edge = np.array([[1,1,1],[0,0,0],[-1,-1,-1]],
dtype=DTYPE)
# osztályonként az első három train képet választjuk;
# a feladat szempontjából elegendő, és nem képez predikciót
selected = []
for c in range(3):
idx = np.flatnonzero(labels == c)[:3]
selected.extend(idx.tolist())
print("idx smooth_abs edge_abs")
for idx in selected:
x = images[idx, 0]
ys = valid_cross_corr2d(x, k_smooth)
ye = valid_cross_corr2d(x, k_edge)
print(f"{idx:03d} {np.sum(np.abs(ys)):.4f} "
f"{np.sum(np.abs(ye)):.4f}")
if __name__ == "__main__":
main()Az aktuális kézirat ezt a kimenetet nem rögzíti mért futásként; a
hallgatói laborban a kimenetnek az alábbi szerkezeti ellenőrzésekkel
kell teljesülnie: minden sorban egy 0 és 359 közötti egész index, majd
két nemnegatív float32 összeg; az élválasz nulla lehet
akkor, ha a kép a 3×3-as ablakokon belül konstans. Az eredményértelmezés
során csak azt szabad állítani, amit a számított mennyiség mér: a simító
válasz abszolút összege a lokális intenzitást, az élválaszé a lokális
kontrasztot tömöríti. A két szám különböző normalizálású, ezért abszolút
összehasonlításuk csak ugyanazon a jeleneten, azonos normalizálás
mellett értelmes; osztálycímkére vonatkozó következtetést nem
hordoz.
F1 (fogalmi, nehézség 1). Definiálja röviden a lokális recepciós mezőt, a súlymegosztást és a feature mapot. Egy 6×6-os bemenetre, egy bemeneti csatornával és 3×3-as valid kernelre adja meg a kimenet alakját, az kimeneti elem recepciós mezőjének pontos indexhalmazát és a megosztott súlyok számát bias nélkül.
F2 (levezetési/bizonyítási, nehézség 2). A törzsben bizonyított linearitást alkalmazza új esetre. Legyen a fenti lépcsős minta, a csupa 1 értékű 4×4-es minta, , . Számolja ki mátrixát, majd a válaszát közvetlenül. Ellenőrizze, hogy az eredmény valóban , minden lépést és indexhatárt feltüntetve. Mutassa meg ezután, hogy a biasos leképezésre , és magyarázza meg, miért cáfolja ez a linearitást.
F3 (implementációs, nehézség 2). Írjon
valid_cross_corr2d(x,k) NumPy-függvényt
float32 bemenettel, tesztelje a 4×4-es példán az él- és
simító kernel várt kimenetét atol=1e-6 toleranciával, majd
adja meg a függvényt úgy, hogy az üres bemeneti szelet ne fordulhasson
elő akkor sem, ha H vagy W kisebb a kernelnél
(azaz ellenőrizze a valid tartomány pozitivitását használat előtt).
Futtatható parancs és teljes kód szükséges.
F4 (hibakeresési, nehézség 2). Az alábbi kód a válaszát próbálja kiszámolni a 4×4-es lépcsős mintán:
def bad_edge_response(x):
k = np.array([[1,1],[-1,-1]], dtype=np.float32)
y = np.zeros((3,3), dtype=np.float32)
for i in range(3):
for j in range(3):
y[i,j] = np.sum(x[i:i+2, i:i+2] * k,
dtype=np.float32)
return yLokalizálja a hibát, magyarázza meg a következményét, javítsa a kódot, és ellenőrizze a javított változatot a kézi várt mátrixszal.
F5 (kísérleti/transzfer, nehézség 3). Tervezzen
deskriptív kísérletet a GlyphLab train részhalmazán: mérje
a fenti 3×3-as simító és él kernel válaszainak abszolút összegét a három
orientációs osztályra, de ne vonjon le predikciós következtetést. Adja
meg a megfigyelési egységet, a rétegzést, a baseline-t, a metrikát és
legalább két érvényességi korlátot. A terv ne feltételezzen előre
győztest.
F1 megoldás. A lokális recepciós mező az adott kimeneti elemhez hozzájáruló bemeneti indexek halmaza; súlymegosztás esetén ugyanaz a kernel minden térbeli pozícióban szoroz; a feature map egy kimeneti csatorna térbeli tömbje. A 6×6 bemenet és 3×3 valid kernel kimenete . Az elem recepciós mezője, 0-indexeléssel,
vagyis a bemeneti ablak. Egy bemeneti csatorna és egy kimeneti csatorna esetén a megosztott súlyok száma bias nélkül . Ha , a halmaz elemű, a megosztott súlyok száma pedig kimeneti csatornánként.
F2 megoldás. elemei: a felső két sor , az alsó két sor , hiszen a lépcsős minta értékeit megduplázzuk, majd minden elemből kivonunk 1-et. Tehát
A valid válasza -on:
Tehát
A tétel szerint . A törzs példájában a középső sorban . Az csupa 1 bemenetre a lemma alapján , mert súlyösszege 0. Így a középső sorban, ami egyezik. Alakellenőrzés: mindhárom kimenet , a számítás minden pozícióra ugyanazt a kernel-ablak definíciót használja. A biasos esetén , egy lineáris leképezésre viszont mindig teljesül; ezért nem lineáris. Az itt a mátrix minden elemére hozzáadott skalár, nem tévesztendő össze a csupa 1 bemeneti mátrixszal.
F3 megoldás. A függvény a fenti
valid_cross_corr2d; a valid tartomány ellenőrzéséhez
érdemes kiegészíteni:
def valid_cross_corr2d(x, k):
x = np.asarray(x, dtype=np.float32)
k = np.asarray(k, dtype=np.float32)
H, W = x.shape
kh, kw = k.shape
if H < kh or W < kw:
raise ValueError(
f"input {H}x{W} smaller than kernel {kh}x{kw}; "
"valid output would be empty")
y = np.zeros((H - kh + 1, W - kw + 1), dtype=np.float32)
for i in range(y.shape[0]):
for j in range(y.shape[1]):
y[i, j] = np.sum(x[i:i+kh, j:j+kw] * k,
dtype=np.float32)
return yEz elkerüli az üres x[:, 0:0] típusú szeletet, mert a
hiba a ciklus előtt explicit ValueError-ként jelenik meg. A
teszt:
python - <<'PY'
import numpy as np
# a fenti függvény bemásolása után
x = np.array([[0,0,0,0],[0,0,0,0],[1,1,1,1],[1,1,1,1]], dtype=np.float32)
k = np.array([[1,1],[-1,-1]], dtype=np.float32)
print(valid_cross_corr2d(x, k))
PYvárt kimenet: a törzsbeli
mátrix. A simító kernelre a 0.5 értékek miatt az atol=1e-6
elegendő. A PyTorch F.conv2d-vel való összehasonlítás
bias=None, stride=1, padding=0 mellett ugyanezt a valid
cross-correlationt adja [D10], de a jelen feladat NumPy-referenciát
kér.
F4 megoldás. A hiba a x[i:i+2, i:i+2]
sorban van: a második index is i, nem j.
Következmény: a j ciklusváltozó nem befolyásolja az ablak
oszlopát, ezért a függvény minden j-re ugyanazt a főátló
menti ablakot számolja, és a várt 3×3-as élválasz csak az
pozíciókban helyes. Javítás:
def good_edge_response(x):
k = np.array([[1,1],[-1,-1]], dtype=np.float32)
y = np.zeros((3,3), dtype=np.float32)
for i in range(3):
for j in range(3):
y[i,j] = np.sum(x[i:i+2, j:j+2] * k,
dtype=np.float32)
return yEllenőrzés:
assert np.allclose(good_edge_response(x_step), y_edge, atol=1e-6).
Alakellenőrzés: a y tömb (3,3), a kernel
(2,2), a bemenet (4,4); a valid kimeneti méret
mindkét tengelyen.
F5 megoldásminta. Lehetséges protokoll:
train részhalmazon?data/images.npy,
train split, 240 kép, 120 független
scene_id.scene_id; a két view
ugyanahhoz a jelenethez tartozik, ezért a képenkénti szabadsági fokot
nem szabad kétszer számolni (1.3. Adatfelosztás és a
megfigyelési egység).A hallgatói jegyzőkönyvnek tartalmaznia kell a nyers táblázatot, a futtató parancsot és az eltérést a tervtől, ha volt. A negatív eredmény is elfogadható: ha a válaszok osztályonként nem különülnek el, az nem cáfolja a CNN tanulhatóságát, csupán azt mutatja, hogy ez a két fix kernel önmagában nem hordozza a címkét.
Gyakori tévedés. A tanult kernel vizualizációja vagy a rögzített él kernel „élszerű” képe nem általános szemantikai magyarázat arra, hogy a háló mit tanult. Ellenpélda: a kernel vizuálisan vízszintes él-detektornak látszik, mégis a 2×2-es sakktábla-mintán azonos nulla választ ad:
Ennek oka, hogy a kernel a sorok közötti egyirányú kontrasztot méri; a sakktábla minden ablakában a pozitív és negatív tagok kioltják egymást. Egyetlen kernel képe tehát nem definiálja a kimeneti szemantikát, és nem mondja meg, hogy a downstream rétegek hogyan kombinálják a feature mapokat. A vizuális interpretáció legfeljebb hipotézis, amelyet kontrollált perturbációval vagy más kísérlettel kell tesztelni.
Önellenőrzés. Ellenőrizze a következőket: a valid kimenet alakja és ; a súlyösszege 0, a súlyösszege 1; a bias nélküli operátor a nulla bemenetet nullába viszi, a biasos nem; a súlyok száma nem függ a kép méretétől. A laborban az élválasz abszolút összege nem címke-predikció: ez a 12.3. Sanity check és kis mintára túlillesztés sanity-check logikájának megfelelően egy mérési érvényességi határ, nem általánosítási ígéret. A 9.3. Expresszivitás és approximációs állítások approximációs tételéhez képest itt egyetlen rögzített lineáris szűrő nagyon alacsony kapacitású modell; az, hogy nem osztályoz jól, nem mond ellent semmilyen univerzális approximációs állításnak. Az 1.3. Adatfelosztás és a megfigyelési egység alfejezetből örökölt csoportos szerkezet miatt a GlyphLab elemzésben a scene_id-t kell független egységnek tekinteni, akkor is, ha a választ képenként számoljuk.
A cross-correlation réteg lokális recepciós mezőt, tértől független súlymegosztást és kimeneti csatornánkénti feature mapot kapcsol össze. A rögzített kernel olyan pont a tanulható paramétertérben, amelyet kézzel megadott él- vagy simító szűrő is reprezentálhat. A bias nélküli, rögzített kernelű operátor lineáris: a véges összeg linearitása koordinátánként bizonyítható. A bias hozzáadása ezt megtöri, mert a nulla bemenet képe nem nulla; a biasos művelet affin. A labor két fix szűrő deskriptív válaszát méri predikció nélkül. A kernel vizuális képe nem általános szemantikai magyarázat, amit a sakktábla-ellenpélda mutat. A következő egység a határkezelést és a frekvenciaintuíciót vizsgálja 13.4. Határkezelés és frekvenciaintuíció.