37.1 Operátorspecifikáció és kézi referencia

37.1.1 1. Cél, előismeret és induló kérdés

Ez az egység a Projekt I első állomása: egy saját, kézzel írt konvolúciós reference operátort készítünk, amelynek minden indexét, alakját és érvényességi feltételét pontosan dokumentáljuk. A cél nem egy gyors PyTorch-utánzat, hanem egy olyan specifikáció, amelyhez később a backwardot (37.2. Backward és ellenőrzési lefedettség), az adjungált ellenőrzést (37.3. Linearitás, adjungált és határfeltétel) és a tanuló réteget (37.4. Rétegből kis tanulómodell) kapcsolni lehet. A 16.4. IV. ellenőrző pont: a CNN-operátor teljes megértése IV. ellenőrző pontjából ismert, hogy a numerikus egyezés önmagában nem bizonyíték; ezért itt először a definíciót rögzítjük, és csak utána engedünk kódot mellé.

Szükséges előismeret: a 13.4. Határkezelés és frekvenciaintuíció alfejezetben bevezetett cross-correlation és zero padding, a 14.4. Csoportos és depthwise konvolúció alfejezetben tárgyalt groups paraméter és annak korlátozása, a 15.4. Független gradiensellenőrzési protokoll alfejezetben kidolgozott float64-es gradiensellenőrzési protokoll, valamint a 36.4. IX. ellenőrző pont: lektorálható modellállítás alfejezetben áttekintett lektorálható állításforma. A 36.1. Állításból kísérleti terv kísérlettervezési elvét és a 33.1. Inference-szerződés és modellcsomag inference-szerződését csak érintőlegesen idézzük vissza, mert itt még nincs betanított modell.

Induló kérdés: adott egy 5×55\times5-ös bemenet, egy 3×33\times3-as kernel, stride 2, padding 1, dilation 1. Hány térbeli pozícióból áll a kimenet, és miért nem elegendő a „majd a könyvtár tudja” válasz egy referencia specifikációhoz?

37.1.2 2. Definíció: a támogatott keresztkorrelációs operátor

Az operátor a PyTorch Conv2d által használt térbeli keresztkorrelációt számolja: a kernel nem fordul meg semelyik tengely mentén. A matematikai konvolúció ettől a kernel 180∘180^\circ-os megfordításában tér el; ezt a 13.2. Konvolúció és cross-correlation pontosan tárgyalta. A jelen specifikáció kizárólag a következő részhalmazt támogatja:

A zero padding azt jelenti, hogy a bemeneti kép szélén kívülre eső pozíciók értéke nulla. Formálisan bevezetjük a paddingelt tömböt

X̃∈ℝN×Cin×(H+2ph)×(W+2pw), \tilde X \in \mathbb{R}^{N\times C_{in}\times (H+2p_h)\times (W+2p_w)},

ahol

X̃[n,c,α,β]={X[n,c,α−ph,β−pw],ha ph≤α<H+ph és pw≤β<W+pw,0,egyébként. \tilde X[n,c,\alpha,\beta] = \begin{cases} X[n,c,\alpha-p_h,\beta-p_w], & \text{ha } p_h\le\alpha<H+p_h \text{ és } p_w\le\beta<W+p_w,\\ 0, & \text{egyébként}. \end{cases}

Ezzel a forward egyetlen indexképlete:

Y[n,o,i,j]=b[o]+∑c=0Cin−1∑u=0kh−1∑v=0kw−1X̃[n,c,ish+udh,jsw+vdw]K[o,c,u,v], \boxed{ Y[n,o,i,j] = b[o] + \sum_{c=0}^{C_{in}-1}\sum_{u=0}^{k_h-1}\sum_{v=0}^{k_w-1} \tilde X[n,c,\; i s_h + u d_h,\; j s_w + v d_w]\; K[o,c,u,v], }

ahol 0≤i<Hout0\le i<H_{out}, 0≤j<Wout0\le j<W_{out}, és b[o]≡0b[o]\equiv0, ha a bias ki van kapcsolva. A stride tehát csak a kimeneti pozíciót lépteti, a dilation pedig a kernel pontjainak bemeneti távolságát növeli: az uu-adik kernelsor a paddingelt kép ish+udhi s_h+u d_h sorát olvassa.

Következmény (az output alak). A kimeneti magasság HoutH_{out} a legnagyobb olyan ii index plusz egy, amelyre a kernel utolsó, (kh−1)(k_h-1)-edik sorának pozíciója még a paddingelt tömbön belül van:

ish+(kh−1)dh≤(H+2ph)−1. i s_h + (k_h-1)d_h \le (H+2p_h) - 1.

Átrendezve

i≤H+2ph−(kh−1)dh−1sh. i \le \frac{H+2p_h-(k_h-1)d_h-1}{s_h}.

A legnagyobb ilyen egész a jobb oldal alsó egészrésze, ezért

Hout=⌊H+2ph−(kh−1)dh−1sh⌋+1. H_{out} = \left\lfloor \frac{H+2p_h-(k_h-1)d_h-1}{s_h} \right\rfloor + 1.

A szélességre ugyanígy, hh helyett ww indexekkel. Ez pontosan a JELOLESEK.md képlete, itt levezetve a paddingelt definícióból. A két térbeli dimenzió független, mert a padding szimmetrikus és a stride/dilation tengelyenként külön adható meg.

Érvényességi feltétel. A kimenet csak akkor értelmes, ha Hout≥1H_{out}\ge1 és Wout≥1W_{out}\ge1. Ha a számláló negatív, az operátor hibát dob: a kernel effektív kiterjedése dh(kh−1)+1d_h(k_h-1)+1 nagyobb, mint a paddingelt bemenet. Ez a feltétel az implementáció bemeneti validációjának része, nem a felhasználó felelőssége.

A definícióban a bias dimenziója CoutC_{out}, és minden térbeli pozícióhoz ugyanaz a skalár adódik: a bias csatornánként egyetlen paraméter, nem térben változó.

37.1.3 3. A specifikáció mint matematikai állítás

A fenti definícióval a referencia operátor pontosan meghatározott függvény: adott X,K,bX,K,b és a paraméterek mellett YY egyértelmű. A levezetési kötelezettség teljesült: az output alak a paddingelt ablak illeszkedési feltételéből következik, és a képlet minden indexhatárt megad. A bizonyítás státusza: saját, teljes levezetés; nincs átvett nagy tétel.

A specifikáció nem állítja, hogy ez az operátor minden Conv2d-opciót támogat. Kimarad például az aszimmetrikus padding, a padding_mode='reflect'/'replicate'/'circular', a groups>1, az output_padding, és a transzponált művelet. A 15.3. Transposed convolution mint adjungált alfejezetben tárgyalt ConvTranspose2d adjungált szerepe sem tartozik ide; a D11 forrás ehhez kiinduló URL, tartalma e kézirat írásakor nem ellenőrzött. A referencia célja a tanulási főút pontos alapja, nem a keretrendszer helyettesítése.

37.1.4 4. Eredeti kézi kis példa

Vegyünk egy 3×33\times3-as bemenetet, egy nemszimmetrikus 2×22\times2-es kernelt, stride 1, padding 0, dilation 1, egyetlen bemeneti és kimeneti csatornát, bias 1010:

X=[123456789],K=[1234]. X=\begin{bmatrix}1&2&3\\4&5&6\\7&8&9\end{bmatrix},\qquad K=\begin{bmatrix}1&2\\3&4\end{bmatrix}.

A kernel azért nemszimmetrikus, mert a főátlóra tükrözve 2≠32\ne3; így a keresztkorreláció és a konvolúció eredménye különbözni fog. A definíció szerint például

Y[0,0,0,0]=1⋅1+2⋅2+4⋅3+5⋅4+10=47. Y[0,0,0,0] = 1\cdot1+2\cdot2+4\cdot3+5\cdot4+10 = 47.

A teljes kimenet:

Y=[47577787]. Y = \begin{bmatrix}47&57\\77&87\end{bmatrix}.

Ellenőrzésképp a jobb alsó elem:

Y[0,0,1,1]=5⋅1+6⋅2+8⋅3+9⋅4+10=87. Y[0,0,1,1] = 5\cdot1+6\cdot2+8\cdot3+9\cdot4+10 = 87.

Ugyanez a bemenet matematikai konvolúcióval a megfordított kernel [4321]\begin{bmatrix}4&3\\2&1\end{bmatrix} alkalmazását jelentené, ami más mátrixot adna; ez az F1 feladatban tér vissza.

A következő minimális kód a referencia függvényt mutatja be, és ezen a kézi példán ellenőrzi az eredményt. A teljes labor-fájl az 5. pontban található; itt csak a függvény vázát adjuk:

import numpy as np

def conv2d_reference(X, K, b=None, stride=(1,1), padding=(0,0), dilation=(1,1)):
    # X: (N, C_in, H, W) float64
    # K: (C_out, C_in, k_h, k_w) float64
    # b: (C_out,) float64 vagy None
    ...
    return Y

# kézi példa
X = np.array([[[[1,2,3],[4,5,6],[7,8,9]]]], dtype=np.float64)  # (1,1,3,3)
K = np.array([[[[1,2],[3,4]]]], dtype=np.float64)              # (1,1,2,2)
Y = conv2d_reference(X, K, b=np.array([10.0], dtype=np.float64))
print(Y)  # várható [[[[47.,57.],[77.,87.]]]]

A kód np.pad-et használ konstans nulla üzemmódban, majd explicit ciklusokkal számol. A ciklusok lassúak, de a referencia célja a helyesség, nem a sebesség.

37.1.5 5. Labor: kézi kontroll és negatív teszt

A labor két fájlból áll. A reference_conv2d.py a specifikációt valósítja meg; a test_reference.py a kézi példát, az alakellenőrzéseket és a hibás bemenetek negatív tesztjeit futtatja. A labor a lab/n145_reference/ munkakönyvtárban található. A futtatás:

cd lab/n145_reference
python -m pytest test_reference.py -q

A kimenet a kézirat írásakor nem mért, ezért jelölt szemléltetés; a tényleges eredményt a futtatás után a hallgató rögzíti a kísérleti naplóban.

A referencia függvény teljes kódja:

# reference_conv2d.py
import numpy as np

def conv2d_reference(X, K, b=None, stride=(1,1), padding=(0,0), dilation=(1,1)):
    """
    Saját NCHW keresztkorrelációs referencia.
    Támogatott: groups=1, szimmetrikus zero padding, stride, dilation,
                opcionális bias. dtype: float64.
    """
    if not (isinstance(X, np.ndarray) and isinstance(K, np.ndarray)):
        raise TypeError("X és K NumPy tömb legyen")
    if X.dtype != np.float64 or K.dtype != np.float64:
        raise TypeError("X és K dtype-ja float64 legyen")
    if b is not None and (not isinstance(b, np.ndarray) or b.dtype != np.float64):
        raise TypeError("b dtype-ja float64 legyen")

    N, C_in, H, W = X.shape
    C_out, C_in_k, k_h, k_w = K.shape
    if C_in_k != C_in:
        raise ValueError(f"K bemeneti csatornája {C_in_k}, de X-é {C_in}")
    if b is not None and b.shape != (C_out,):
        raise ValueError(f"b alakja {b.shape}, várt ({C_out},)")

    s_h, s_w = stride
    p_h, p_w = padding
    d_h, d_w = dilation
    if s_h < 1 or s_w < 1:
        raise ValueError("stride >= 1")
    if p_h < 0 or p_w < 0:
        raise ValueError("padding >= 0")
    if d_h < 1 or d_w < 1:
        raise ValueError("dilation >= 1")
    if k_h < 1 or k_w < 1:
        raise ValueError("kernel méret >= 1")

    H_out = (H + 2*p_h - (k_h-1)*d_h - 1) // s_h + 1
    W_out = (W + 2*p_w - (k_w-1)*d_w - 1) // s_w + 1
    if H_out < 1 or W_out < 1:
        raise ValueError("A kimeneti méret nem pozitív; ellenőrizze a paramétereket")

    # paddingelt tömb
    X_pad = np.pad(X, ((0,0),(0,0),(p_h,p_h),(p_w,p_w)), mode='constant')
    Y = np.zeros((N, C_out, H_out, W_out), dtype=np.float64)

    for n in range(N):
        for o in range(C_out):
            for i in range(H_out):
                for j in range(W_out):
                    s = 0.0
                    for c in range(C_in):
                        for u in range(k_h):
                            for v in range(k_w):
                                s += X_pad[n, c, i*s_h + u*d_h, j*s_w + v*d_w] * K[o, c, u, v]
                    Y[n, o, i, j] = s
            if b is not None:
                Y[n, o, :, :] += b[o]  # bias csatornánként
    return Y

A tesztfájl a kézi példát és a negatív eseteket tartalmazza:

# test_reference.py
import numpy as np
import pytest
from reference_conv2d import conv2d_reference

def test_manual_nonsymmetric():
    X = np.array([[[[1,2,3],[4,5,6],[7,8,9]]]], dtype=np.float64)
    K = np.array([[[[1,2],[3,4]]]], dtype=np.float64)
    Y = conv2d_reference(X, K, b=np.array([10.0], dtype=np.float64))
    expected = np.array([[[[47.0,57.0],[77.0,87.0]]]], dtype=np.float64)
    assert np.allclose(Y, expected, rtol=0, atol=1e-12)

def test_wrong_dtype():
    X = np.zeros((1,1,3,3), dtype=np.float32)
    K = np.zeros((1,1,2,2), dtype=np.float32)
    with pytest.raises(TypeError):
        conv2d_reference(X, K)

def test_channel_mismatch():
    X = np.zeros((1,2,3,3), dtype=np.float64)
    K = np.zeros((1,3,2,2), dtype=np.float64)
    with pytest.raises(ValueError):
        conv2d_reference(X, K)

def test_zero_output():
    X = np.zeros((1,1,2,2), dtype=np.float64)
    K = np.zeros((1,1,5,5), dtype=np.float64)
    with pytest.raises(ValueError):
        conv2d_reference(X, K)

def test_bad_stride():
    X = np.zeros((1,1,4,4), dtype=np.float64)
    K = np.zeros((1,1,2,2), dtype=np.float64)
    with pytest.raises(ValueError):
        conv2d_reference(X, K, stride=(0,1))

A negatív teszt célja annak ellenőrzése, hogy a függvény hibás bemenetnél nem ad csendes, hamis eredményt. Ez a 14.4. Csoportos és depthwise konvolúció alfejezetben jelzett „csendes elnyelés” ellenpontja.

37.1.6 6. Feladatok

37.1.6.1 F1 (fogalmi, nehézség 1)

Magyarázza el, mi a különbség a keresztkorreláció és a matematikai konvolúció között a jelen specifikációban. Adjon meg egy 2×22\times2-es, nemszimmetrikus kernelt, és a 4. pontban szereplő 3×33\times3-as bemenettel számítsa ki mindkét művelet kimenetét stride 1, padding 0, dilation 1, bias nélkül. Elvárt átadandó: a két definíció képlete, a konkrét mátrixok.

37.1.6.2 F2 (levezetési, nehézség 2)

Legyen H=4,W=5H=4,W=5, kernel 3×33\times3, stride (2,2)(2,2), padding (1,1)(1,1), dilation (2,2)(2,2). Vezesse le HoutH_{out} és WoutW_{out} értékét az output alakképletből, és sorolja fel azokat a bemeneti (n,c,α,β)(n,c,\alpha,\beta) indexhármasokat, amelyek a Y[n,o,0,1]Y[n,o,0,1] elemhez tartozó összegben nemzérus súllyal járulhatnak hozzá. Adja meg a pontos indexfeltételt.

37.1.6.3 F3 (implementációs, nehézség 2)

Töltse le vagy írja be a reference_conv2d.py fájlt, futtassa a test_reference.py tesztet, és rögzítse a kimenetet. Ezután egészítse ki egy olyan esettel, ahol N=2,Cin=2,Cout=3N=2,C_{in}=2,C_{out}=3, batchbeli képek 6×76\times7-esek, kernel 2×22\times2, stride (1,2)(1,2), padding (0,1)(0,1), dilation (1,2)(1,2), bias bekapcsolva; a bemenetet np.random.default_rng(20261020).random(...) generálja. A teszt csak a shape-t és a dtype-ot ellenőrizze, értéket ne (a random miatt). Elvárt átadandó: a módosított teszt és a futtatás eredménye.

37.1.6.4 F4 (hibakeresési, nehézség 2)

Az alábbi kódrészlet a forwardot számolja, de hibás:

H_out = (H + 2*p_h - k_h*d_h) // s_h + 1
...
Y[n,o,i,j] += X[n,c, i*s_h + u*d_h - p_h, j*s_w + v*d_w - p_w] * K[o,c,u,v]

Keresse meg a hibá(ka)t, magyarázza el a következményt a kimeneti méretre és az indexelésre, javítsa a kódot, és a 4. pont kézi példáján igazolja, hogy a javítás után a várt 47,57,77,8747,57,77,87 értékek adódnak. Elvárt átadandó: a hibás sorok megnevezése, a javított sor, és a rövid verifikáció.

37.1.6.5 F5 (kísérleti/transzfer, nehézség 3)

Tervezzen kísérletet a conv2d_reference és a PyTorch torch.nn.functional.conv2d kimenetének összehasonlítására. A terv tartalmazza: a paraméterrácsot (legalább 6 különböző konfiguráció, beleértve paddinget, stride-ot, dilationt, több csatornát és bias nélküli esetet), a bemeneti adat generálását rögzített seeddel, a numerikus toleranciát, és annak indoklását, miért kell a PyTorch bemenetet is float64-re konvertálni. Elvárt átadandó: a protokoll szöveges leírása, a lehetséges buktatók, és legalább egy konfiguráció kézzel számolt várt kimeneti shape-je.

37.1.7 7. Megoldások és indoklás

37.1.7.1 F1 megoldás

A keresztkorreláció a kernel megfordítása nélkül:

Y[i,j]=∑u,vX[i+u,j+v]K[u,v], Y[i,j]=\sum_{u,v}X[i+u,j+v]K[u,v],

a konvolúció pedig a megfordított kernellel:

(C*K)[i,j]=∑u,vX[i+u,j+v]K[kh−1−u,kw−1−v]. (C*K)[i,j]=\sum_{u,v}X[i+u,j+v]K[k_h-1-u,k_w-1-v].

A 4. pont kernelével K=[1234]K=\begin{bmatrix}1&2\\3&4\end{bmatrix} a keresztkorreláció (bias nélkül):

Ycc=[37476777]. Y_{cc}=\begin{bmatrix}37&47\\67&77\end{bmatrix}.

A konvolúcióhoz a kernel megfordítottja [4321]\begin{bmatrix}4&3\\2&1\end{bmatrix}, így

Yconv=[1⋅4+2⋅3+4⋅2+5⋅12⋅4+3⋅3+5⋅2+6⋅14⋅4+5⋅3+7⋅2+8⋅15⋅4+6⋅3+8⋅2+9⋅1]=[23315361]. Y_{conv}=\begin{bmatrix}1\cdot4+2\cdot3+4\cdot2+5\cdot1 & 2\cdot4+3\cdot3+5\cdot2+6\cdot1\\ 4\cdot4+5\cdot3+7\cdot2+8\cdot1 & 5\cdot4+6\cdot3+8\cdot2+9\cdot1\end{bmatrix} =\begin{bmatrix}23&31\\53&61\end{bmatrix}.

A két eredmény különbözik, mert a kernel nem forgásszimmetrikus.

37.1.7.2 F2 megoldás

Hout=⌊(4+2−2⋅2−1)/2⌋+1=⌊(4+2−4−1)/2⌋+1=⌊1/2⌋+1=0+1=1H_{out}=\lfloor(4+2-2\cdot2-1)/2\rfloor+1=\lfloor(4+2-4-1)/2\rfloor+1=\lfloor1/2\rfloor+1=0+1=1. Hasonlóan Wout=⌊(5+2−2⋅2−1)/2⌋+1=⌊(5+2−4−1)/2⌋+1=⌊2/2⌋+1=1+1=2W_{out}=\lfloor(5+2-2\cdot2-1)/2\rfloor+1=\lfloor(5+2-4-1)/2\rfloor+1=\lfloor2/2\rfloor+1=1+1=2. Tehát YY térbeli alakja 1×21\times2.

A Y[n,o,0,1]Y[n,o,0,1] elemhez tartozó összeg a j=1j=1 kimeneti oszlop miatt a paddingelt kép 1⋅sw+vdw=2+v⋅21\cdot s_w+v d_w=2+v\cdot2 oszlopait olvassa, v=0,1,2v=0,1,2 esetén: 2,4,62,4,6. A paddingelt szélesség 5+2=75+2=7, tehát az oszlopindexek érvényesek (2,4,62,4,6 mind <7). A paddingelt oszlopindexekből az eredeti bemeneti oszlopindexek β−pw=1,3,5\beta-p_w=1,3,5. A magassági rész: i=0i=0, u=0,1,2u=0,1,2, paddingelt sorok 0⋅2+u⋅2=0,2,40\cdot2+u\cdot2=0,2,4, ebből eredeti sorindexek −1,1,3-1,1,3. Az első sorindex −1-1 a padding zónába esik, ezért a hozzájárulás nulla; a ténylegesen használt bemeneti sorok: 11 és 33. Összefoglalva: adott n,cn,c mellett a lehetséges nemzérus bemeneti pozíciók (α,β)∈{(1,1),(1,3),(1,5),(3,1),(3,3),(3,5)}(\alpha,\beta)\in\{(1,1),(1,3),(1,5),(3,1),(3,3),(3,5)\}, feltéve, hogy a megfelelő K[o,c,u,v]K[o,c,u,v] súly nem nulla.

37.1.7.3 F3 megoldás

A módosított teszt:

def test_random_shape_dtype():
    rng = np.random.default_rng(20261020)
    X = rng.random((2,2,6,7), dtype=np.float64)
    K = rng.random((3,2,2,2), dtype=np.float64)
    b = rng.random((3,), dtype=np.float64)
    Y = conv2d_reference(X, K, b=b, stride=(1,2), padding=(0,1), dilation=(1,2))
    assert Y.shape == (2,3,5,3)  # H_out: (6+0-1-1)//1+1=5; W_out: (7+2-3-1)//2+1=3
    assert Y.dtype == np.float64

A pytest futtatás után a teszt sikeres, ha a shape és dtype illeszkedik. A képlet ellenőrzése: Hout=(6+0−1⋅(2−1)−1)//1+1=5H_{out}=(6+0-1\cdot(2-1)-1)//1+1=5, Wout=(7+2⋅1−2⋅(2−1)−1)//2+1=(7+2−2−1)//2+1=6//2+1=3W_{out}=(7+2\cdot1-2\cdot(2-1)-1)//2+1=(7+2-2-1)//2+1=6//2+1=3.

37.1.7.4 F4 megoldás

Az első hibás sorban a k_h*d_h szerepel a (k_h-1)*d_h helyett: ez eggyel nagyobb effektív kernelméretet feltételez, így HoutH_{out} túl kicsi lehet. A második sorban a - p_h kivonás jó, de hiányzik annak ellenőrzése, hogy a kivonás után az index [0,H−1][0,H-1] tartományban van-e; a paddingelt tömb használata nélkül a negatív vagy HH-n túli indexek hibás memóriaterületet vagy Python negatív indexelést okoznak. A helyes megoldás a paddingelt tömb bevezetése, ahogy a reference_conv2d.py teszi: X_pad-del számolunk, és a kernel effektív mérete (k_h-1)*d_h+1.

Javított sorok:

H_out = (H + 2*p_h - (k_h-1)*d_h - 1) // s_h + 1
...
s += X_pad[n, c, i*s_h + u*d_h, j*s_w + v*d_w] * K[o, c, u, v]

A kézi példán a javított kód a 4. pontban megadott [[47,57],[77,87]][[47,57],[77,87]] mátrixot adja, 1e−121e-12 abszolút tűréssel egyezően.

37.1.7.5 F5 megoldás

Protokollterv:

  1. Cél: a referencia és a PyTorch F.conv2d azonossága a támogatott részhalmazon.
  2. Adat: minden esethez np.random.default_rng(20261020).random(shape) float64-ben; a PyTorch tenzort torch.from_numpy(...).double()-ként hozzuk létre.
  3. Paraméterrács: (a) H=W=8H=W=8, k=3k=3, s=1, p=1, d=1; (b) H=5,W=6H=5,W=6, k=2k=2, s=1, p=0, d=2; (c) H=10,W=7H=10,W=7, k=3k=3, s=2, p=1, d=1; (d) N=2,Cin=2,Cout=3N=2,C_{in}=2,C_{out}=3, k=2k=2, s=2, p=1, d=1; (e) ugyanez bias nélkül; (f) H=7,W=7H=7,W=7, k=1k=1, s=1, p=0, d=1.
  4. Bias: a PyTorch F.conv2d bias argumentumát külön adjuk át, vagy None-t, a referenciával összhangban.
  5. Tolerancia: mivel mindkét oldal float64, az eltérés zérus legyen; numerikus pontosságból adódóan rtol=1e-12, atol=1e-12 elfogadható.
  6. Kiértékelés: minden konfigurációra np.allclose, a maximális abszolút eltérés rögzítése.
  7. Buktatók: a PyTorch alapértelmezett float32 numerikus eltérést okoz; a csatornák sorrendje NCHW mindkettőnél; a dilation a PyTorchban ugyanúgy a kernel pontjai közötti lépés, nem a stride; a padding szimmetrikus, ezért a PyTorch padding=(p_h,p_w) formája használandó.
  8. Korlát: az egyezés csak a fenti konfigurációkra bizonyított; nem állít általános implementációs ekvivalenciát.

Példa shape az (a) esetre: bemenet (1,1,8,8)(1,1,8,8), kernel (1,1,3,3)(1,1,3,3), s=1, p=1, d=1, így Hout=Wout=(8+2−2⋅1−1)/1+1=8H_{out}=W_{out}=(8+2-2\cdot1-1)/1+1=8. A kimenet (1,1,8,8)(1,1,8,8).

37.1.8 8. Tipikus tévedés és ellenpélda

„A referencia implementáció minden Conv2d-opciót támogat.” Ez hamis. A jelen kód például nem támogat groups>1-et, aszimmetrikus paddinget, más padding módokat, output_padding-et vagy a transzponált műveletet. Ellenpélda: ha a felhasználó groups=2-vel hívná meg, a függvény nem is fogad ilyen paramétert, és a specifikáció ezt kifejezetten kizárja. Egy részleges referencia csak a deklarált tartományon érvényes; aki ennél többet állít, az a 13.4. Határkezelés és frekvenciaintuíció alfejezetben tárgyalt „csendes elnyeléshez” hasonló hibát követ el, csak éppen a dokumentáció szintjén.

Önellenőrzés. A hallgató tegye fel magának: (1) a kernel effektív kiterjedését (k−1)d+1(k-1)d+1-ként számolta-e, nem kdkd-ként? (2) A paddingelt tömb indexelésekor a padding hozzáadása után a stride és dilation sorrendje helyes? (3) A bias dimenziója CoutC_{out}, és a kimenet minden térbeli pozíciójához hozzáadódik? (4) A dtype mindenütt float64, és a hibás bemenetek kivételt dobnak? (5) A kimeneti méret képlete a floor függvényt használja, és a nullánál kisebb kimenet hibát ad?

Visszahívás. A 13.4. Határkezelés és frekvenciaintuíció alfejezetben a zero padding és a circular padding közötti különbség azt mutatta, hogy a határfeltétel a definíció része; itt a zero paddingot explicitté tettük a X̃\tilde X bevezetésével. A 14.4. Csoportos és depthwise konvolúció alfejezetben a groups=1 azt jelentette, hogy a súly alakja Cout×Cin×kh×kwC_{out}\times C_{in}\times k_h\times k_w; ezt most a specifikáció rögzíti.

37.1.9 Rövid összefoglaló

A referencia operátor specifikációja az NCHW keresztkorrelációt egyetlen indexképlettel definiálja: a paddingelt bemenetből stride és dilation szerint olvasott kernelablakok lineáris kombinációja, csatornánkénti bias hozzáadásával. A kimeneti méret levezetése a kernel utolsó pozíciójának illeszkedési feltételéből adódik, és a floor-képletben összegződik. A támogatott tartományt pontosan rögzítettük: groups=1, szimmetrikus zero padding, stride, dilation, opcionális bias, float64. A labor egy nemszimmetrikus kernelű kézi kontrollt és hibás alakokra negatív teszteket tartalmaz. A következő egység a backward pontos levezetését kezdi, amelyben ugyanezt a specifikációt használjuk a súly- és bemeneti gradienshez.

37.1.10 Felhasznált források