13.3 Lokális szűrők és tanult reprezentáció

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

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?

13.3.2 Lokális recepciós mező, súlymegosztás és feature map

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 X∈ℝCin×H×WX\in\mathbb R^{C_{in}\times H\times W} egy bemeneti tensorelem, K∈ℝCout×Cin×kh×kwK\in\mathbb R^{C_{out}\times C_{in}\times k_h\times k_w} a kernel, és tekintsük a valid, stride 1, nulla padding, dilation 1 esetet. Ekkor a kimenet alakja

Y∈ℝCout×Hout×Wout,Hout=H−kh+1,Wout=W−kw+1,Y\in\mathbb R^{C_{out}\times H_{out}\times W_{out}},\qquad H_{out}=H-k_h+1,\quad W_{out}=W-k_w+1,

és a rögzített cross-correlation definíciója

Y[o,i,j]=∑c=0Cin−1∑u=0kh−1∑v=0kw−1K[o,c,u,v]X[c,i+u,j+v].Y[o,i,j]=\sum_{c=0}^{C_{in}-1}\sum_{u=0}^{k_h-1}\sum_{v=0}^{k_w-1} K[o,c,u,v]\,X[c,i+u,j+v]. \tag{1}

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 Y[o,i,j]Y[o,i,j] kimeneti érték lokális recepciós mezője azon bemeneti indexhármasok halmaza, amelyek a kimeneti értékhez ténylegesen hozzájárulnak:

ℛi,j={(c,i+u,j+v):0≤c<Cin,0≤u<kh,0≤v<kw}.\mathcal R_{i,j}=\{(c,i+u,j+v):0\le c<C_{in},\;0\le u<k_h,\;0\le v<k_w\}.

A halmaz mérete CinkhkwC_{in}k_hk_w, függetlenül a teljes kép H,WH,W 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 KK kernel elemei nem függnek az (i,j)(i,j) kimeneti pozíciótól. Ugyanaz a K[o,c,u,v]K[o,c,u,v] 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

CoutCinkhkw+Cout,C_{out}C_{in}k_hk_w+C_{out},

ahol az utolsó tag a kimeneti csatornánkénti bias. Ez nem függ HH-tól és WW-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 oo kimeneti csatornához tartozó Y[o,⋅,⋅]∈ℝHout×WoutY[o,\cdot,\cdot]\in\mathbb R^{H_{out}\times W_{out}} térbeli tömb az oo-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.

13.3.3 Él- és simító szűrő mint rögzített kernel

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

Ksmooth=14(1111).K_{\mathrm{smooth}}=\frac14\begin{pmatrix}1&1\\1&1\end{pmatrix}.

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

Kedge=(11−1−1).K_{\mathrm{edge}}=\begin{pmatrix}1&1\\-1&-1\end{pmatrix}.

A súlyösszeg szerepét rögzíti az alábbi lemma.

Lemma. Ha X∈ℝ1×H×WX\in\mathbb R^{1\times H\times W} minden pixele azonos c∈ℝc\in\mathbb R értékű, akkor bármely K∈ℝ1×1×kh×kwK\in\mathbb R^{1\times1\times k_h\times k_w} kernelre

Y[0,i,j]=c∑u=0kh−1∑v=0kw−1K[0,0,u,v].Y[0,i,j]=c\sum_{u=0}^{k_h-1}\sum_{v=0}^{k_w-1}K[0,0,u,v].

Bizonyítás. Az (1) képletbe X[0,i+u,j+v]=cX[0,i+u,j+v]=c-t helyettesítve

Y[0,i,j]=∑u,vK[0,0,u,v]c=c∑u,vK[0,0,u,v],Y[0,i,j]=\sum_{u,v}K[0,0,u,v]\,c =c\sum_{u,v}K[0,0,u,v],

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 KsmoothK_{\mathrm{smooth}} konstans képen a bemeneti értéket adja vissza, mert súlyösszege 1; KedgeK_{\mathrm{edge}} 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 KedgeK_{\mathrm{edge}}-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.

13.3.4 A bias nélküli cross-correlation linearitása

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 KK mellett legyen

FK:ℝCin×H×W→ℝCout×Hout×Wout,FK(X)=Y,F_K:\mathbb R^{C_{in}\times H\times W}\to\mathbb R^{C_{out}\times H_{out}\times W_{out}}, \qquad F_K(X)=Y,

ahol YY-t az (1) képlet definiálja. A biasos változat minden kimeneti csatornához egy bo∈ℝb_o\in\mathbb R skalárt ad:

FK,b(X)[o,i,j]=FK(X)[o,i,j]+bo.F_{K,b}(X)[o,i,j]=F_K(X)[o,i,j]+b_o.

A bias térbelileg az egész feature mapon osztozik.

Tétel (linearitás). A bias nélküli FKF_K lineáris leképezés: minden X1,X2∈ℝCin×H×WX_1,X_2\in\mathbb R^{C_{in}\times H\times W} bemenetre és minden α,β∈ℝ\alpha,\beta\in\mathbb R skalárra

FK(αX1+βX2)=αFK(X1)+βFK(X2).F_K(\alpha X_1+\beta X_2)=\alpha F_K(X_1)+\beta F_K(X_2).

Bizonyítás. Rögzített o,i,jo,i,j kimeneti koordinátára a definícióból:

FK(αX1+βX2)[o,i,j]=∑c,u,vK[o,c,u,v](αX1+βX2)[c,i+u,j+v]=∑c,u,vK[o,c,u,v](αX1[c,i+u,j+v]+βX2[c,i+u,j+v])=α∑c,u,vK[o,c,u,v]X1[c,i+u,j+v]+β∑c,u,vK[o,c,u,v]X2[c,i+u,j+v]=αFK(X1)[o,i,j]+βFK(X2)[o,i,j]. \begin{aligned} F_K(\alpha X_1+\beta X_2)[o,i,j] &=\sum_{c,u,v}K[o,c,u,v]\,(\alpha X_1+\beta X_2)[c,i+u,j+v]\\ &=\sum_{c,u,v}K[o,c,u,v]\Big(\alpha X_1[c,i+u,j+v] +\beta X_2[c,i+u,j+v]\Big)\\ &=\alpha\sum_{c,u,v}K[o,c,u,v]X_1[c,i+u,j+v] +\beta\sum_{c,u,v}K[o,c,u,v]X_2[c,i+u,j+v]\\ &=\alpha F_K(X_1)[o,i,j]+\beta F_K(X_2)[o,i,j]. \end{aligned}

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 KK rögzített: ha KK maga is tanulható paraméter, akkor a leképezés a (K,X)(K,X) párban bilineáris, de XX-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 FK,bF_{K,b} nem lineáris, ha valamely bo≠0b_o\ne0. Legyen bo=1b_o=1 minden kimeneti csatornára. Ekkor FK,b(0)[o,i,j]=1F_{K,b}(0)[o,i,j]=1, míg egy lineáris leképezés a zérus vektort kötelezően zérusba viszi, hiszen L(0)=L(0⋅0)=0⋅L(0)=0L(0)=L(0\cdot0)=0\cdot L(0)=0. Mivel FK,b(0)≠0F_{K,b}(0)\ne0, a leképezés nem lineáris. Az additivitás közvetlenül is sérül:

FK,b(0+0)=b,FK,b(0)+FK,b(0)=2b,F_{K,b}(0+0)=b,\qquad F_{K,b}(0)+F_{K,b}(0)=2b,

és 2b≠b2b\ne b, ha b≠0b\ne0. 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.

13.3.5 Kézi példa: lépcsős minta és két filter

Legyen a bemenet

X1=(0000000011111111). X_1=\begin{pmatrix} 0&0&0&0\\ 0&0&0&0\\ 1&1&1&1\\ 1&1&1&1 \end{pmatrix}.

A KedgeK_{\mathrm{edge}} kernel valid válasza a 3×3-as

Yedge=(000−2−2−2000), Y_{\mathrm{edge}}=\begin{pmatrix} 0&0&0\\ -2&-2&-2\\ 0&0&0 \end{pmatrix},

mert például az (i,j)=(1,0)(i,j)=(1,0) pozíció ablakában

Y[1,0]=X1,0+X1,1−X2,0−X2,1=0+0−1−1=−2,Y[1,0]=X_{1,0}+X_{1,1}-X_{2,0}-X_{2,1} =0+0-1-1=-2,

é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 KsmoothK_{\mathrm{smooth}} kernel ugyanezen a bemeneten

Ysmooth=(0000.50.50.5111), Y_{\mathrm{smooth}}=\begin{pmatrix} 0&0&0\\ 0.5&0.5&0.5\\ 1&1&1 \end{pmatrix},

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 b=1b=1 mellett

Yedge,b=Yedge+1=(111−1−1−1111). Y_{\mathrm{edge},b}=Y_{\mathrm{edge}}+1 =\begin{pmatrix} 1&1&1\\ -1&-1&-1\\ 1&1&1 \end{pmatrix}.

A bias itt a nulla bemenetre is 1 választ ad, ami a fenti ellenpéldának megfelelően megtöri a linearitást.

13.3.6 Minimális ellenőrző kód

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.

13.3.7 Labor: két rögzített szűrő válasza a GlyphLab mintákon

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:

Ksmooth(3)=19𝟙3×3,Kedge(3)=(111000−1−1−1). K_{\mathrm{smooth}}^{(3)}=\frac19\mathbb 1_{3\times3},\qquad K_{\mathrm{edge}}^{(3)}=\begin{pmatrix} 1&1&1\\0&0&0\\-1&-1&-1 \end{pmatrix}.

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.csv

A 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.

13.3.8 Feladatok

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 Y[2,2]Y[2,2] 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 X1X_1 a fenti lépcsős minta, X2X_2 a csupa 1 értékű 4×4-es minta, α=2\alpha=2, β=−1\beta=-1. Számolja ki Y=2X1−X2Y=2X_1-X_2 mátrixát, majd a KedgeK_{\mathrm{edge}} válaszát közvetlenül. Ellenőrizze, hogy az eredmény valóban 2FK(X1)−FK(X2)2F_K(X_1)-F_K(X_2), minden lépést és indexhatárt feltüntetve. Mutassa meg ezután, hogy a biasos G(X)=FK(X)+1G(X)=F_K(X)+1 leképezésre G(0)≠0G(0)\ne0, é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 KedgeK_{\mathrm{edge}} 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 y

Lokalizá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.

13.3.9 Megoldások és indoklás

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 4×44\times4. Az Y[2,2]Y[2,2] elem recepciós mezője, 0-indexeléssel,

ℛ2,2={(r,s):2≤r<5,2≤s<5},\mathcal R_{2,2}=\{(r,s):2\le r<5,\;2\le s<5\},

vagyis a [2,5)×[2,5)[2,5)\times[2,5) bemeneti ablak. Egy bemeneti csatorna és egy kimeneti csatorna esetén a megosztott súlyok száma bias nélkül 3⋅3=93\cdot3=9. Ha Cin>1C_{in}>1, a halmaz Cin⋅9C_{in}\cdot9 elemű, a megosztott súlyok száma pedig Cin⋅9C_{in}\cdot9 kimeneti csatornánként.

F2 megoldás. Y=2X1−X2Y=2X_1-X_2 elemei: a felső két sor −1-1, az alsó két sor 11, hiszen a lépcsős minta értékeit megduplázzuk, majd minden elemből kivonunk 1-et. Tehát

Y=(−1−1−1−1−1−1−1−111111111). Y=\begin{pmatrix} -1&-1&-1&-1\\ -1&-1&-1&-1\\ 1&1&1&1\\ 1&1&1&1 \end{pmatrix}.

A KedgeK_{\mathrm{edge}} valid válasza YY-on:

Tehát

FK(Y)=(000−4−4−4000). F_K(Y)=\begin{pmatrix} 0&0&0\\-4&-4&-4\\0&0&0 \end{pmatrix}.

A tétel szerint FK(2X1−X2)=2FK(X1)−FK(X2)F_K(2X_1-X_2)=2F_K(X_1)-F_K(X_2). A törzs példájában FK(X1)F_K(X_1) a középső sorban −2-2. Az X2X_2 csupa 1 bemenetre a lemma alapján FK(X2)=0F_K(X_2)=0, mert KedgeK_{\mathrm{edge}} súlyösszege 0. Így 2(−2)−0=−42(-2)-0=-4 a középső sorban, ami egyezik. Alakellenőrzés: mindhárom kimenet 3×33\times3, a számítás minden pozícióra ugyanazt a kernel-ablak definíciót használja. A biasos G(X)=FK(X)+1G(X)=F_K(X)+1 esetén G(0)=1G(0)=1, egy lineáris LL leképezésre viszont L(0)=0L(0)=0 mindig teljesül; ezért GG nem lineáris. Az 11 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 y

Ez 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))
PY

várt kimenet: a törzsbeli YedgeY_{\mathrm{edge}} 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 i=ji=j 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 y

Ellenő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 4−2+1=34-2+1=3 mindkét tengelyen.

F5 megoldásminta. Lehetséges protokoll:

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.

13.3.10 Gyakori tévedés, önellenőrzés és korábbi fogalom

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 KedgeK_{\mathrm{edge}} kernel vizuálisan vízszintes él-detektornak látszik, mégis a 2×2-es sakktábla-mintán azonos nulla választ ad:

Xchecker=(0101101001011010),FKedge(Xchecker)=(000000000). X_{\mathrm{checker}}=\begin{pmatrix} 0&1&0&1\\1&0&1&0\\0&1&0&1\\1&0&1&0 \end{pmatrix}, \qquad F_{K_{\mathrm{edge}}}(X_{\mathrm{checker}}) =\begin{pmatrix}0&0&0\\0&0&0\\0&0&0\end{pmatrix}.

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 H−kh+1H-k_h+1 és W−kw+1W-k_w+1; a KedgeK_{\mathrm{edge}} súlyösszege 0, a KsmoothK_{\mathrm{smooth}} 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.

13.3.11 Rövid összefoglaló

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ó.

13.3.12 Források