14.2 Több bemeneti és kimeneti csatorna

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

Ez az alfejezet az egycsatornás konvolúciós műveletet terjeszti ki több bemeneti és kimeneti csatornára. A 14.1. Stride, padding, dilation és outputméret egységben már definiáltuk a kimeneti méretet stride, padding és dilation mellett; a 13.2. Konvolúció és cross-correlation egységben pedig a keresztkorrelációt mint kernelmegfordítás nélküli szorzatösszeget. Most arra a kérdésre válaszolunk: ha egy 24×24-es szürkeárnyalatos GlyphLab-kép helyett háromcsatornás bemenetünk lenne, hogyan változik a súlytenzor alakja, és pontosan milyen összegzés állítja elő a kimeneti jellemzőtérképeket?

A CNNe-építő programozó számára a fő veszély nem a képlet bonyolultsága, hanem a tengelyek összekeverése. Ezért minden állítást NCHW/OIHW alakkal, azaz batch–csatorna–magasság–szélesség, illetve kimeneti csatorna–bemeneti csatorna–kernelmagasság–kernelszélesség konvencióban adunk meg. A PyTorch Conv2d dokumentációja [D10] ugyanezt a súlyalakot specifikálja groups=1 esetén, és kifejezetten keresztkorrelációról beszél. A CS231n jegyzet [S01] a kernel bank fogalmát használja ugyanerre az objektumra; az itteni levezetés és számpélda saját.

14.2.2 Definíció és intuíció

Legyen a bemeneti köteg

X∈ℝN×Cin×Hin×Win, X \in \mathbb{R}^{N \times C_{in} \times H_{in} \times W_{in}},

ahol NN a batch méret, CinC_{in} a bemeneti csatornák száma, HinH_{in} és WinW_{in} a térbeli dimenziók. A súlytenzor

K∈ℝCout×Cin×kh×kw, K \in \mathbb{R}^{C_{out} \times C_{in} \times k_h \times k_w},

és a torzításvektor

b∈ℝCout. b \in \mathbb{R}^{C_{out}}.

A kimenet

Y∈ℝN×Cout×Hout×Wout. Y \in \mathbb{R}^{N \times C_{out} \times H_{out} \times W_{out}}.

Minden rögzített o∈{0,…,Cout−1}o \in \{0,\ldots,C_{out}-1\} kimeneti csatornához tartozó

K[o]∈ℝCin×kh×kw K[o] \in \mathbb{R}^{C_{in} \times k_h \times k_w}

szeletet kernel banknak nevezzük. Egy bank tehát CinC_{in} darab térbeli, kh×kwk_h \times k_w-s kernelből áll: minden bemeneti csatornához egy. A teljes súlytenzor CoutC_{out} darab ilyen bank egymásra helyezése.

Az intuíció a következő. Egy kimeneti jellemzőtérkép egyetlen neuronja nem csak a térbeli szomszédokat, hanem az összes bemeneti csatorna ugyanazon térbeli környezetét is látja. Először minden bemeneti csatornát külön-külön megszűrünk a bank megfelelő kernelével, majd ezeket a szűrt térképeket elemenként összeadjuk. Ez a csatornaösszegzés (channel sum). A torzítás ezután minden kimeneti csatornához egyetlen skalárt ad hozzá, amely térben és a batch mentén is konstans; ezért mondjuk, hogy a bias per output channel értendő.

14.2.3 A teljes többcsatornás forward levezetése

A levezetéshez a 14.1. Stride, padding, dilation és outputméret egységben bevezetett kimeneti méretet használjuk szimmetrikus zero paddinggel:

Hout=⌊Hin+2p−d(kh−1)−1s⌋+1, H_{out} = \left\lfloor \frac{H_{in}+2p-d(k_h-1)-1}{s} \right\rfloor + 1,

és WoutW_{out} analóg módon. Itt s≥1s\ge 1 a stride, p≥0p\ge 0 a padding, d≥1d\ge 1 a dilation. Feltesszük, hogy Hout≥1H_{out}\ge 1 és Wout≥1W_{out}\ge 1.

Lemma (többcsatornás keresztkorreláció). Legyen XX, KK, bb a fenti alakú, és legyen s,p,ds,p,d a fenti feltételű. Ekkor minden n∈{0,…,N−1}n\in\{0,\ldots,N-1\}, o∈{0,…,Cout−1}o\in\{0,\ldots,C_{out}-1\}, i∈{0,…,Hout−1}i\in\{0,\ldots,H_{out}-1\} és j∈{0,…,Wout−1}j\in\{0,\ldots,W_{out}-1\} indexre a kimenet

Y[n,o,i,j]=b[o]+∑c=0Cin−1∑u=0kh−1∑v=0kw−1X[n,c,is−p+ud,js−p+vd]K[o,c,u,v]. 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} X\!\left[n,c,\; i s - p + u d,\; j s - p + v d\right] \, K[o,c,u,v].

Bizonyítás. A bizonyítás a réteg definíciójából indul ki, és nem használja fel a bizonyítandó állítást.

  1. Egy bemeneti csatorna szűrése. Rögzítsünk egy cc bemeneti csatornát és egy oo kimeneti csatornát. A 13.2. Konvolúció és cross-correlation egység szerint a keresztkorreláció a kernel bal felső sarkát az input aktuális pozíciójához illeszti, kernelmegfordítás nélkül. A dd dilation miatt a kernel u.u. sorhoz tartozó bemeneti sora is−p+udi s - p + u d, a v.v. oszlophoz tartozó bemeneti oszlopa pedig js−p+vdj s - p + v d. A paddingen kívülre eső indexekre a bemenet nulla, ahogyan azt a szimmetrikus zero padding definíciója előírja. Így a c.c. csatorna hozzájárulása a o.o. kimeneti csatornához

So,c[i,j]=∑u=0kh−1∑v=0kw−1X[n,c,is−p+ud,js−p+vd]K[o,c,u,v]. S_{o,c}[i,j] = \sum_{u=0}^{k_h-1} \sum_{v=0}^{k_w-1} X\!\left[n,c,\; i s - p + u d,\; j s - p + v d\right] \, K[o,c,u,v].

  1. A csatornák hozzájárulásának összegzése. A réteg definíciója szerint a o.o. kimeneti csatorna neuronja minden bemeneti csatorna minden kernelpozíciójából érkező szorzatot összegzi. Ezért a kimeneti csatorna előtti teljes lineáris kombináció

∑c=0Cin−1So,c[i,j]=∑c=0Cin−1∑u=0kh−1∑v=0kw−1X[n,c,is−p+ud,js−p+vd]K[o,c,u,v]. \sum_{c=0}^{C_{in}-1} S_{o,c}[i,j] = \sum_{c=0}^{C_{in}-1} \sum_{u=0}^{k_h-1} \sum_{v=0}^{k_w-1} X\!\left[n,c,\; i s - p + u d,\; j s - p + v d\right] \, K[o,c,u,v].

A három összegzés sorrendje tetszőleges, mert véges sok tagot adunk össze; a fenti csoportosítás csak a levezetés átláthatóságát szolgálja.

  1. A torzítás hozzáadása. A b[o]b[o] skalár nem függ sem a cc csatornaindextől, sem a u,vu,v kernelpozíciótól, sem a térbeli i,ji,j kimeneti indextől. Ezért a bias hozzáadása az előbbi összeghez a kimeneti csatornánkénti broadcast definíciója.

A négy lépés együtt éppen a lemma képletét adja. ▫\square

Alakellenőrzés. A jobb oldal minden rögzített (n,o,i,j)(n,o,i,j) indexre egyetlen valós skalár. A K[o]K[o] bank alakja Cin×kh×kwC_{in}\times k_h\times k_w, tehát a c,u,vc,u,v szerinti hármas összegzés pontosan a bank dimenzióit járja be. A b[o]b[o] indexelése konzisztens a KK első tengelyével, vagyis CoutC_{out} darab skalár van. A kimenet első tengelye NN, így a batch dimenzió az egyenletben csak passzív: ugyanaz a KK és bb alkalmazódik minden nn megfigyelésre.

Megjegyzés a keresztkorrelációról. A fenti képletben a kernel indexe u,vu,v növekvő sorrendben halad, és a bemeneti index is u,vu,v-vel nő. Ez a programozói főút keresztkorrelációja. Ha valaki matematikai konvolúciót akar, a 13.2. Konvolúció és cross-correlation szerint a kernelt 180 fokkal el kell forgatni; tanult súlyoknál a két konvenció ekvivalens paraméterezést ad, de kézzel megadott szűrőknél numerikusan különbözik. A D10 dokumentáció a Conv2d műveletét keresztkorrelációként specifikálja; ezt a laborban is követjük.

14.2.4 Kézi kis példa

Legyen N=1N=1, Cin=2C_{in}=2, Hin=Win=3H_{in}=W_{in}=3, Cout=2C_{out}=2, kh=kw=2k_h=k_w=2, stride 11, padding 00, dilation 11. Ekkor Hout=Wout=3−2+1=2H_{out}=W_{out}=3-2+1=2.

A bemenet két csatornája:

X[0,0]=(123456789),X[0,1]=(987654321). X[0,0] = \begin{pmatrix} 1 & 2 & 3\\ 4 & 5 & 6\\ 7 & 8 & 9 \end{pmatrix}, \qquad X[0,1] = \begin{pmatrix} 9 & 8 & 7\\ 6 & 5 & 4\\ 3 & 2 & 1 \end{pmatrix}.

A két kernel bank:

K[0,0]=(100−1),K[0,1]=(01−10), K[0,0]=\begin{pmatrix}1 & 0\\0 & -1\end{pmatrix}, \quad K[0,1]=\begin{pmatrix}0 & 1\\-1 & 0\end{pmatrix},

K[1,0]=(0110),K[1,1]=(−1001). K[1,0]=\begin{pmatrix}0 & 1\\1 & 0\end{pmatrix}, \quad K[1,1]=\begin{pmatrix}-1 & 0\\0 & 1\end{pmatrix}.

A torzítás b=(1,−1)b=(1,-1). Számítsuk ki a o=0o=0, i=0i=0, j=0j=0 kimeneti elemet. A c=0c=0 csatorna bal felső 2×22\times 2-es ablaka (1245)\begin{pmatrix}1&2\\4&5\end{pmatrix}, így

1⋅1+2⋅0+4⋅0+5⋅(−1)=−4. 1\cdot 1+2\cdot 0+4\cdot 0+5\cdot(-1)=-4.

A c=1c=1 csatorna ablaka (9865)\begin{pmatrix}9&8\\6&5\end{pmatrix}, így

9⋅0+8⋅1+6⋅(−1)+5⋅0=2. 9\cdot 0+8\cdot 1+6\cdot(-1)+5\cdot 0=2.

A csatornaösszeg −4+2=−2-4+2=-2, a bias hozzáadása után Y[0,0,0,0]=−1Y[0,0,0,0]=-1. A többi pozíciót ugyanígy kiszámolva:

Y[0,0]=(−1−1−1−1),Y[0,1]=(1379). Y[0,0] = \begin{pmatrix} -1 & -1\\ -1 & -1 \end{pmatrix}, \qquad Y[0,1] = \begin{pmatrix} 1 & 3\\ 7 & 9 \end{pmatrix}.

Figyeljük meg, hogy a két kimeneti csatorna teljesen más térképet ad, noha ugyanazt a bemenetet látja: a különbséget a bankok és a biasok okozzák. Az alábbi kód ezt a példát is ellenőrzi.

14.2.5 Labor: NumPy referencia és PyTorch Conv2d, két bemeneti csatornával

A labor a saját NumPy referenciát veti össze a PyTorch conv2d függvényével float64 pontosságban. A referencia a 14.1. Stride, padding, dilation és outputméret alfejezetben bevezetett indexezést használja, de itt teljes egészében megadjuk, hogy a labor önállóan futtatható legyen.

# munkakönyvtár: lab/n054
# fájl: n054_lab.py
import numpy as np
import torch
import torch.nn.functional as F

def conv2d_numpy(x, weight, bias, stride=1, padding=0, dilation=1):
    """NCHW keresztkorreláció, groups=1, szimmetrikus zero padding."""
    N, C_in, H, W = x.shape
    C_out, C_in_w, kH, kW = weight.shape
    if C_in_w != C_in:
        raise ValueError("A suly C_in tengelye nem egyezik a bemenettel")
    H_out = (H + 2 * padding - dilation * (kH - 1) - 1) // stride + 1
    W_out = (W + 2 * padding - dilation * (kW - 1) - 1) // stride + 1
    y = np.zeros((N, C_out, H_out, W_out), dtype=x.dtype)
    for n in range(N):
        for o in range(C_out):
            for i in range(H_out):
                for j in range(W_out):
                    total = 0.0
                    for c in range(C_in):
                        for u in range(kH):
                            for v in range(kW):
                                xi = i * stride - padding + u * dilation
                                xj = j * stride - padding + v * dilation
                                if 0 <= xi < H and 0 <= xj < W:
                                    total += x[n, c, xi, xj] * weight[o, c, u, v]
                    y[n, o, i, j] = total + bias[o]
    return y

# 1) A kezi peld a ellenorzese
x_manual = np.array([[[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
                      [[9, 8, 7], [6, 5, 4], [3, 2, 1]]]], dtype=np.float64)
w_manual = np.array([[[[1, 0], [0, -1]], [[0, 1], [-1, 0]]],
                     [[[0, 1], [1, 0]], [[-1, 0], [0, 1]]]], dtype=np.float64)
b_manual = np.array([1.0, -1.0], dtype=np.float64)

y_manual = conv2d_numpy(x_manual, w_manual, b_manual)
print("Kezi pelda, o=0:\n", y_manual[0, 0])
print("Kezi pelda, o=1:\n", y_manual[0, 1])

x_t = torch.from_numpy(x_manual)
w_t = torch.from_numpy(w_manual)
b_t = torch.from_numpy(b_manual)
y_t = F.conv2d(x_t, w_t, b_t, stride=1, padding=0)
print("PyTorch elteres a kezi peldan:", torch.max(torch.abs(y_t - torch.from_numpy(y_manual))).item())

# 2) Veletlen f64 osszevetes ket bemeneti csatornaval
rng = np.random.default_rng(20261020)
N, C_in, H, W = 2, 2, 5, 5
C_out, kH, kW = 3, 3, 3
stride, padding, dilation = 2, 1, 1

x = rng.standard_normal((N, C_in, H, W)).astype(np.float64)
w = rng.standard_normal((C_out, C_in, kH, kW)).astype(np.float64)
b = rng.standard_normal((C_out,)).astype(np.float64)

y_ref = conv2d_numpy(x, w, b, stride, padding, dilation)
y_torch = F.conv2d(torch.from_numpy(x),
                   torch.from_numpy(w),
                   torch.from_numpy(b),
                   stride=stride, padding=padding, dilation=dilation)

err = np.max(np.abs(y_ref - y_torch.numpy()))
print("Alakok: x", x.shape, "w", w.shape, "b", b.shape,
      "-> y", y_ref.shape)
print("max abszolut elteres:", f"{err:.3e}")

Futtatási parancs:

python n054_lab.py

A kimenet jelölt szemléltetés, mert ezt a kérést író környezet nem futtatott Python-kódot; az értékek a fenti determinisztikus kódból következnek, és a hallgatói futtatás során ellenőrizendők:

Kezi pelda, o=0:
 [[-1. -1.]
  [-1. -1.]]
Kezi pelda, o=1:
 [[1. 3.]
  [7. 9.]]
PyTorch elteres a kezi peldan: 0.0
Alakok: x (2, 2, 5, 5) w (3, 2, 3, 3) b (3,) -> y (2, 3, 2, 2)
max abszolut elteres: 0.000e+00

Az első két blokk igazolja a kézi számpéldát. A véletlen esetben a PyTorch és a NumPy referencia közötti maximális abszolút eltérés 00 vagy gépi epszilon nagyságrendű, mert mindkét oldal float64-ben számol, és a referencia ugyanazt az összegzési sorrendet követi, mint a levezetés. Ha float32-re váltanánk, a 2.2. Lebegőpont és stabil elemi számítás szerint nagyobb, de még mindig kicsi eltérésre számíthatnánk; a pontos egyezés nem várható el automatikusan. A csatornák számának növelése nem változtatja meg a kimeneti térbeli alakot, csak a bank méretét: Cin=4C_{in}=4 esetén a súly alakja (Cout,4,kh,kw)(C_{out},4,k_h,k_w) lenne.

14.2.6 Feladatok

F1 (fogalmi, nehézség 1). Definiálja a kernel bank fogalmát, és adja meg az alakját Cin=3C_{in}=3, kh=5k_h=5, kw=5k_w=5 esetén. Miért van CoutC_{out} darab bank, és mi a különbség egy bank és egyetlen térbeli kernel között?

F2 (levezetési, nehézség 1). Legyen kh=kw=1k_h=k_w=1, stride 11, padding 00, dilation 11, és hanyagoljuk el a biast. Vezesse le a Lemma képletéből, hogy ekkor

Y[n,o,i,j]=∑c=0Cin−1X[n,c,i,j]K[o,c,0,0]. Y[n,o,i,j] = \sum_{c=0}^{C_{in}-1} X[n,c,i,j]\,K[o,c,0,0].

Magyarázza el, miért nevezzük ezt pontonkénti csatornakeverésnek, és ellenőrizze a kimeneti alakot.

F3 (implementációs, nehézség 2). Írjon conv2d_numpy_s1p0 nevű függvényt, amely stride 11, padding 00 esetén valósítja meg a többcsatornás forwardot, és tesztelje a fenti kézi példán. Adja meg a függvény teljes kódját importokkal, a bemeneti tömböket, a várt kimenetet és a toleranciát.

F4 (hibakeresési, nehézség 2). Az alábbi kódrészlet a csatornaösszegzést próbálja NumPy-val elvégezni, de hibás:

def hibas_forward(x, w, b):
    N, C, H, W = x.shape
    Cout, _, kh, kw = w.shape
    Hout = H - kh + 1
    Wout = W - kw + 1
    y = np.zeros((Cout, Hout, Wout), dtype=x.dtype)
    for o in range(Cout):
        for i in range(Hout):
            for j in range(Wout):
                # HIBAS: a 0. tengely a batch, nem a csatorna
                y[o, i, j] = np.sum(
                    x[:, :, i:i+kh, j:j+kw] * w[o, :, :, :],
                    axis=(0, 2, 3)
                ) + b[o]
    return y

Lokalizálja a hibát, magyarázza el a következményét, és adja meg a javított sort. Milyen bemeneti alak esetén tűnne a kimenet véletlenül helyesnek?

F5 (kísérleti tervezés, nehézség 3). Tervezzen egy kis kísérletet annak eldöntésére, hogy a bias valóban kimeneti csatornánként egyetlen skalár-e, vagy térbeli pozíciónként változik. Fagyassza a súlyokat nullára, adjon meg ismert biasvektort, és írja le, milyen kimenetet vár a két hipotézis alatt, milyen statisztikával döntene, és milyen korlátai vannak a következtetésnek.

14.2.7 Megoldások és indoklás

F1 megoldás. A kernel bank a KK súlytenzor rögzített oo kimeneti csatornához tartozó szelete: K[o]∈ℝCin×kh×kwK[o]\in\mathbb{R}^{C_{in}\times k_h\times k_w}. A feladat adataival az alak 3×5×53\times5\times5. Azért van CoutC_{out} darab bank, mert minden kimeneti jellemzőtérkép saját szűrőkészletet kap. Egy bank nem egyetlen térbeli kernel, hanem CinC_{in} darab kernel együttese: minden bemeneti csatornához külön kh×kwk_h\times k_w szűrő tartozik, és ezek kimeneteit összegezzük. Az egycsatornás eset akkor áll elő, ha Cin=1C_{in}=1; ekkor a bank egyetlen térbeli kernellé redukálódik.

F2 megoldás. A Lemma képletébe kh=kw=1k_h=k_w=1, s=1s=1, p=0p=0, d=1d=1 helyettesítve u=v=0u=v=0 az egyetlen kernelindex, és a bemeneti index i⋅1−0+0⋅1=ii\cdot1-0+0\cdot1=i, illetve jj hasonlóan. Így

Y[n,o,i,j]=∑c=0Cin−1X[n,c,i,j]K[o,c,0,0]. Y[n,o,i,j] = \sum_{c=0}^{C_{in}-1} X[n,c,i,j]\,K[o,c,0,0].

Nincs térbeli szomszédosság, ezért a művelet minden (i,j)(i,j) pozícióban külön-külön, a csatornák mentén lineárisan kombinál. A kimeneti alak a 14.1. Stride, padding, dilation és outputméret formulával Hout=Hin−1+1=HinH_{out}=H_{in}-1+1=H_{in}, Wout=WinW_{out}=W_{in}; a kimenet N×Cout×Hin×WinN\times C_{out}\times H_{in}\times W_{in}. A tanulható skalárok száma CinCoutC_{in}C_{out}.

F3 megoldás. A függvény a conv2d_numpy speciális esete:

# fajl: n054_f3.py
import numpy as np

def conv2d_numpy_s1p0(x, weight, bias):
    N, C_in, H, W = x.shape
    C_out, C_in_w, kH, kW = weight.shape
    assert C_in_w == C_in
    H_out = H - kH + 1
    W_out = W - kW + 1
    y = np.zeros((N, C_out, H_out, W_out), dtype=x.dtype)
    for n in range(N):
        for o in range(C_out):
            for i in range(H_out):
                for j in range(W_out):
                    total = 0.0
                    for c in range(C_in):
                        total += np.sum(
                            x[n, c, i:i+kH, j:j+kW] * weight[o, c, :, :]
                        )
                    y[n, o, i, j] = total + bias[o]
    return y

x = np.array([[[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
               [[9, 8, 7], [6, 5, 4], [3, 2, 1]]]], dtype=np.float64)
w = np.array([[[[1, 0], [0, -1]], [[0, 1], [-1, 0]]],
              [[[0, 1], [1, 0]], [[-1, 0], [0, 1]]]], dtype=np.float64)
b = np.array([1.0, -1.0], dtype=np.float64)
y = conv2d_numpy_s1p0(x, w, b)
print(y[0, 0])
print(y[0, 1])

A várt kimenet a fenti kézi példa eredménye, az ellenőrzés egész számokon történik, ezért a tolerancia 00; lebegőpontos bemenetnél np.allclose(y, y_expected, atol=1e-12, rtol=1e-12) indokolt.

F4 megoldás. A hiba az axis=(0,2,3) kifejezésben van: a 0.0. tengely a batch, nem a bemeneti csatorna. A kód így a batch elemeit is összegzi, ezért a kimenet (C_out, H_out, W_out) alakú lesz az elvárt (N, C_out, H_out, W_out) helyett, és különböző megfigyelések keverednek. A javítás: axis=(1,2,3), azaz a csatorna- és kerneltengelyeket összegezni. A hiba akkor tűnne véletlenül helyesnek, ha N=1N=1, mert ekkor a batch szerinti összegzés egyetlen elemet ad, és az alak is egybeeshet, ha a többi dimenzió stimmel; de N>1N>1 esetén azonnal alak- és értékhibát kapunk.

F5 megoldás. Egy lehetséges terv: rögzítünk N=2N=2, Cin=2C_{in}=2, Hin=Win=4H_{in}=W_{in}=4, Cout=3C_{out}=3, kernel 2×22\times2, stride 11, padding 00. A súlyokat nullára állítjuk, a bias pedig legyen b=(1,2,3)b=(1,2,3). Ekkor a Lemma szerint Y[n,o,i,j]=b[o]Y[n,o,i,j]=b[o], tehát a kimenet minden n,i,jn,i,j indexre ugyanaz az o.o. csatornában. A hipotézisek: ha a bias per output channel, akkor a kimeneti térkép minden pozíciója konstans; ha térbeli bias lenne, akkor a bb hossza Cout⋅Hout⋅WoutC_{out}\cdot H_{out}\cdot W_{out} kellene, és a térkép pozíciónként változna. Döntési statisztika: minden kimeneti csatornára számítsuk ki a pozíciók közötti szórást; per-channel bias esetén ez 00, térbeli bias esetén általában pozitív. A korlát: ez a kísérlet csak a bias szerkezetét azonosítja, a súlyok csatornaösszegzését nem; továbbá a nulla súly melletti eredmény nem igazolja, hogy a bias a tanítás során is ezt a szerepet tartja meg.

14.2.8 Gyakori tévedés és ellenpélda

A leggyakoribb tévedés szerint a batch és a csatorna tengely felcserélhető, mert mindkettő az első két NCHW-dimenzió, és a reshape vagy transpose azonos elemszámot ad. Ez hamis. Legyen X∈ℝ2×3×4×4X\in\mathbb{R}^{2\times3\times4\times4} és X′=transpose(X,(1,0,2,3))∈ℝ3×2×4×4X'=\mathrm{transpose}(X,(1,0,2,3))\in\mathbb{R}^{3\times2\times4\times4}. Ugyanahhoz a K∈ℝCout×3×kh×kwK\in\mathbb{R}^{C_{out}\times3\times k_h\times k_w} súlyhoz XX-en két megfigyelést, X′X'-n három megfigyelést dolgoz fel a réteg: a X′X' első batch eleme az eredeti első csatorna két batch elemét tartalmazza, így a kernel azokat a megfigyeléseket csatornaként összegzi, amelyeknek függetleneknek kellene maradniuk. Konkrét ellenpélda: ha az eredeti XX első batch elemének első csatornája csupa 11, második csatornája csupa 22, második batch elemének első csatornája csupa 33, második csatornája csupa 44, akkor az átrendezett X′X' első batch elemének két csatornája 11-ek és 33-ak keveréke; ugyanaz a kernel más kimenetet ad, mint bármely eredeti megfigyelésre. A megfigyelési egység így sérül, ami az 1.3. Adatfelosztás és a megfigyelési egység szerint a split és a kiértékelés érvényességét is tönkreteszi.

14.2.9 Önellenőrzés és visszahívás

Ellenőrizze magát: (1) Mi a KK alakja Cout=4C_{out}=4, Cin=2C_{in}=2, kh=3k_h=3, kw=3k_w=3 mellett? (Válasz: (4,2,3,3)(4,2,3,3).) (2) Hány skalár bias tartozik ehhez a réteghez? (Válasz: 44.) (3) Ha Cin=1C_{in}=1, mire redukálódik a Lemma képlete? (Egyetlen csatorna összegzésére, azaz a 13.2. Konvolúció és cross-correlation alfejezetbeli egycsatornás kereszttkorrelációra.) (4) Miért nem szabad a csatornaösszegzést a batch tengelyen elvégezni? (Mert a megfigyelések keverednének.)

Visszahívás: a 13.2. Konvolúció és cross-correlation egység definiálta a keresztkorrelációt és a bias fogalmát egy csatornára; az itteni Lemma ezt terjeszti ki a csatornaösszegzéssel. A 10.2. Affín réteg és aktiváció backward affin rétegénél láttuk, hogy a batch dimenzió passzív: az XW⊤+bXW^\top+b minden sorra ugyanazt a leképezést alkalmazza. Ugyanez a tulajdonság jelenik meg itt is a KK és bb batchfüggetlenségében. Végül a 2.2. Lebegőpont és stabil elemi számítás egység figyelmeztet, hogy a referencia és a PyTorch közötti numerikus egyezést toleranciával, float64 mellett érdemes ellenőrizni.

14.2.10 Rövid összefoglaló

A többcsatornás konvolúciós réteg minden kimeneti csatornához egy kernel bankot rendel, amely CinC_{in} darab kh×kwk_h\times k_w-s kernelből áll. A forward a bemeneti csatornák szűrt térképeit elemenként összegzi, majd hozzáadja a kimeneti csatornánkénti skalár biast. A Lemma ezt a teljes összeget adja meg indexekkel, NCHW/OIHW alakokkal; a bizonyítás a csatornánkénti keresztkorrelációból és a véges összegek felcserélhetőségéből áll. A labor f64-ben igazolja, hogy a NumPy referencia és a PyTorch F.conv2d azonos eredményt ad két bemeneti csatornával. A batch és a csatorna tengely nem cserélhető fel azonos elemszám ellenére sem.

14.2.11 Következő alkalmazás

A 14.3. Paraméterszám, MAC és aktivációmemória egység a paraméterszámot, a MAC-műveletek számát és az aktivációmemóriát számítja ki ezekből az alakokból. A kernel bank fogalma ott válik költségmodellé: a Cout×Cin×kh×kwC_{out}\times C_{in}\times k_h\times k_w szorzat a súlyok számát, a kimeneti térbeli mérettel szorozva pedig a számítási igényt adja.

14.2.12 Források