Ebben az egységben egyetlen célt járunk körbe pontosan: adott bemeneti képméret, kernel, padding, stride és dilation mellett mekkora lesz a CNN-réteg kimeneti térbeli mérete, és miért éppen ez a méret. A 13.4. Határkezelés és frekvenciaintuíció egységben már láttuk, hogy a határfeltétel, például a zero padding, a cross-correlation definíciójának része; most ezt a gondolatot általánosítjuk a CNN-réteg összes geometriai paraméterére. A 13.1. Kép mint diszkrét többcsatornás jel egységből visszahozzuk a mintavételi rácsot, a 2.1. Python, NumPy és tenzoralak egységből pedig a tenzoralakok és a broadcasting ellenőrzésének szokását.
Induló becslésként gondoljunk egy -es képre és egy -as kernelre, stride mellett, padding nélkül. Hány pozícióba tudjuk a kernelt teljes egészében rátenni? A mag bal felső sarka a oszlopokban lehet, ezért a kimenet -nek ígérkezik. Kérdés: hogyan változik ez, ha lépésközt, kitöltést vagy tágítást kapcsolunk be, és mi a közös, bizonyítható szabály?
A bemenet egy NCHW tensor, ahol a magasság, a szélesség. A groups esetére szorítkozunk, tehát a konvolúciós mag
A térbeli paraméterek:
| Jel | Tartomány | Jelentés |
|---|---|---|
| pozitív egész | kernel magassága és szélessége | |
| nemnegatív egész | szimmetrikus zero padding a két dimenzióban | |
| pozitív egész | stride, azaz lépésköz | |
| pozitív egész | dilation, azaz tágítási távolság |
A dilation azt mondja meg, hogy a kernel két szomszédos együtthatója hány bemeneti rácspontra kerül egymástól. A kernel ténylegesen lefedett, effektív kiterjedése (kernel extent) ezért nem , hanem
Például esetén a három kernelpozíció a bemeneti indexeket fedi le, tehát a kiterjedés , nem .
A kimenet térbeli alakja
.
A [D10] dokumentációban használt programozói konvenció
szerint a Conv2d keresztkorrelációt
számol, azaz a kernel súlyait nem forgatja meg. Egy
kimeneti indexhez a kernel első
együtthatójának horgonypozíciója a magasság mentén
az -edik kernelpozíció pedig a
bemeneti indexet mintavételezi. A zero padding miatt az eredeti tartományon kívüli, de a kiterjesztett tartományon belüli indexeket nulla helyettesíti; az ezen kívül eső horgonypozíciókhoz már nincs érvényes kimeneti ablak.
A következő állítást az index-tartományból teljes egészében levezetjük; ez saját levezetés, amely a [D10] dokumentált indexezését használja.
Tétel (szimmetrikus paddinggel). Legyen pozitív egész és nemnegatív egész. Egy térbeli dimenzió mentén a keresztkorreláció érvényes kimeneti indexeinek halmaza
Ha nem üres, akkor , ahol
A pontosan akkor üres, ha ; ezt a beállítást érvénytelen kimenetnek nevezzük, és a könyvtári forward ilyenkor alakhibát ad.
Bizonyítás. Mivel , , és , a bal oldali egyenlőtlenség
mindig teljesül. A jobb oldali feltétel minden indexre akkor és csak akkor áll fenn, ha a legnagyobb mellett sem sérül:
Átrendezve:
A legnagyobb megengedett egész ezért
A feltétel bal oldala -ben monoton növekvő, ezért ha , akkor minden index teljesíti az összes egyenlőtlenséget, tehát az érvényes indexek halmaza egy kezdőszelet. Így
Végül akkor üres, ha , azaz a számláló kisebb, mint . Ez éppen a
feltétellel ekvivalens.
A szélességre a teljesen azonos levezetés adódik, helyett szereplőkkel:
A magasságra és szélességre vonatkozó feltételek egymástól függetlenek. A réteg akkor ad érvényes kimenetet, ha és egyszerre teljesül.
Két fontos megjegyzés tartozik a tétel hatásköréhez. Először: a
formula
-t
tartalmaz, mert a paddinget a térbeli dimenzió mindkét
oldalán azonos méretűnek tételeztük fel. Ha egy API vagy egy
F.pad hívás aszimmetrikus paddinget használ,
bal és
jobb kitöltéssel, akkor a tétel bizonyításában
helyére
kerül, mert a jobb széli feltétel csak a teljes padding mennyiségét
érzékeli. Másodszor: a padding='valid' szöveges opció nem
azonos a „van legalább egy kimeneti ablak” matematikai értelemben vett
érvényességgel; a 'valid' egyszerűen a
választás rövid neve.
Legyen a bemenet , tehát . Válasszunk , , és paramétereket. A mag effektív kiterjedése mindkét dimenzióban .
Magasságra:
Szélességre:
A kimenet térbeli alakja tehát . Ellenőrizzük a magassági indexeket: -nál a horgonypozíció , a lefedett indexek , a a bal oldali paddingból érkezik. -nél a lefedett indexek , ez még érvényes, mert a jobb széli paddinget is tartalmazza. -nál a lefedés lenne, de már túlmegy a kiterjesztett tartományon, ezért nem érvényes. A számított kimeneti index tehát pontosan fedi a lehetséges pozíciókat.
Egy gyors, önállóan futtatható kontroll ugyanerre:
import torch
H, W, k, p, s, d = 6, 5, 3, 1, 2, 1
formula = ((H + 2*p - d*(k-1) - 1)//s + 1,
(W + 2*p - d*(k-1) - 1)//s + 1)
conv = torch.nn.Conv2d(2, 4, k, stride=s, padding=p, dilation=d,
bias=False, dtype=torch.float32, device="cpu")
y = conv(torch.zeros(1, 2, H, W, dtype=torch.float32, device="cpu"))
print("formula:", formula, "torch:", tuple(y.shape[2:]))A képletből számított, nem futtatott várt kimenet:
formula: (3, 2) torch: (3, 2). A
választás azt is ellenőrzi, hogy a csatornák száma nem befolyásolja a
térbeli alakot.
A labor célja, hogy ne egyetlen példán higgyünk a formulának, hanem
egymástól eltérő beállításokon, nemnégyzetes bemenet és kernel mellett
is összevessük a saját számítást a Conv2d tényleges
kimeneti alakjával. A teszt determinisztikus, ezért az alakegyezéshez
semmilyen lebegőpontos tolerancia nem kell; a seed csak a szokásos
reprodukálhatósági kontrollt rögzíti.
Munkakönyvtár: n053_lab/. Futtatás:
python shape_test.py.
# n053_lab/shape_test.py
import torch
def out1d(length, kernel, pad, stride, dilation):
"""Egy térbeli dimenzió kimeneti mérete szimmetrikus paddinggel."""
assert length >= 1 and kernel >= 1 and stride >= 1 and dilation >= 1
assert pad >= 0
return (length + 2 * pad - dilation * (kernel - 1) - 1) // stride + 1
def expected_shape(H, W, kh, kw, ph, pw, sh, sw, dh, dw):
return (out1d(H, kh, ph, sh, dh), out1d(W, kw, pw, sw, dw))
def main():
torch.manual_seed(20261020)
dtype = torch.float32
device = torch.device("cpu")
# (H, W, kh, kw, ph, pw, sh, sw, dh, dw, név)
cases = [
(5, 5, 3, 3, 0, 0, 1, 1, 1, 1, "valid, négyzetes"),
(7, 5, 3, 3, 1, 1, 1, 1, 1, 1, "nemnégyzetes, padding"),
(8, 10, 3, 5, 0, 0, 1, 1, 1, 1, "nemnégyzetes input és kernel"),
(7, 6, 3, 3, 0, 0, 2, 2, 1, 1, "stride 2, padding nélkül"),
(9, 8, 3, 3, 0, 0, 1, 1, 2, 2, "dilation 2"),
(5, 4, 4, 4, 1, 1, 2, 2, 1, 1, "padding+stride"),
(13, 7, 5, 5, 0, 0, 2, 2, 1, 1, "nagyobb nemnégyzetes"),
(6, 6, 6, 6, 0, 0, 1, 1, 1, 1, "éppen 1x1 kimenet"),
]
for (H, W, kh, kw, ph, pw, sh, sw, dh, dw, name) in cases:
exp = expected_shape(H, W, kh, kw, ph, pw, sh, sw, dh, dw)
x = torch.zeros(1, 2, H, W, dtype=dtype, device=device)
conv = torch.nn.Conv2d(2, 3, (kh, kw),
stride=(sh, sw),
padding=(ph, pw),
dilation=(dh, dw),
bias=False,
dtype=dtype, device=device)
got = tuple(conv(x).shape[2:])
assert got == exp, (name, got, exp)
print(f"{name:30s} formula={exp} torch={got}")
# Érvénytelen beállítás: a formulának <= 0 méretet kell adnia.
# A Conv2d forward ilyenkor RuntimeError-t ad, ezért itt csak a formulát teszteljük.
invalid = [(3, 4, 0, 1, 1), (4, 5, 0, 1, 1)]
for L, k, p, s, d in invalid:
value = out1d(L, k, p, s, d)
assert value <= 0, (L, k, p, s, d, value)
print(f"érvénytelen L={L},k={k},p={p},s={s},d={d}: formula={value}")
print("Minden alaktartalom-ellenőrzés rendben.")
if __name__ == "__main__":
main()A fenti kódot az írás pillanatában nem futtattuk; az alábbi sorok a képletből közvetlenül számított, jelölt várt kimenet, nem mért futás:
valid, négyzetes formula=(3, 3) torch=(3, 3)
nemnégyzetes, padding formula=(7, 5) torch=(7, 5)
nemnégyzetes input és kernel formula=(6, 6) torch=(6, 6)
stride 2, padding nélkül formula=(3, 2) torch=(3, 2)
dilation 2 formula=(5, 4) torch=(5, 4)
padding+stride formula=(2, 2) torch=(2, 2)
nagyobb nemnégyzetes formula=(5, 2) torch=(5, 2)
éppen 1x1 kimenet formula=(1, 1) torch=(1, 1)
érvénytelen L=3,k=4,p=0,s=1,d=1: formula=0
érvénytelen L=4,k=5,p=0,s=1,d=1: formula=0
A teszt fontos tanulsága, hogy a validitási feltétel külön vizsgálandó: a értékek nem negatív előjellel, hanem nullánál kisebb vagy azzal egyenlő értékkel jelzik, hogy a réteg geometriailag nem kivitelezhető.
same padding nem mindig szimmetrikusGyakori félreértés, hogy a same padding pontosan
ugyanaz, mint egyetlen szimmetrikus
megadása. A fenti tétel csak szimmetrikus paddingre érvényes, ezért
érdemes ellenpéldát számolni.
Legyen
,
,
,
.
Ha egy same típusú szabály a
kimeneti méretet célozza, akkor a kívánt teljes padding
Három padding pixel kellene összesen, ami két egész oldalkitöltéssel csak aszimmetrikusan, például bal , jobb elosztásban valósítható meg. A szimmetrikus ehhez kevés:
a szimmetrikus
pedig ugyan
kimenetet ad, de
padding pixelt használ, tehát nem ugyanaz az operátor, mint a minimális
pixeles same változat:
A tanulság nem az, hogy a same opció „rossz”, hanem hogy
a mögöttes padding-elosztás, a támogatott stride-ok és az egész kimeneti
méretre kerekítés szabálya könyvtáronként és verzióként eltérhet. A
hordozható kódban érdemes explicit numerikus paddinget számolni, és a
jelen formulát használni; a padding='same' viselkedését
pedig külön, a saját környezetben ellenőrizni.
Ellenőrizd a réteget a következő sorrendben: először az effektív kiterjedést, másodszor a validitási feltételt, harmadszor a floor formulát, végül mindkét térbeli dimenziót külön-külön. A 13.4. Határkezelés és frekvenciaintuíció tanulsága szerint a padding a lineáris operátor definíciójának része, ezért két eltérő padding-beállítás azonos kimeneti méret mellett is különböző számítást jelenthet. A 13.1. Kép mint diszkrét többcsatornás jel rácsindexelése nélkül a horgonypozíció feltételei nem írhatók fel. A későbbi 10.2. Affín réteg és aktiváció backward jellegű backward-levezetésekben a kimeneti indexhalmaz itteni lezártsága fogja garantálni, hogy a gradiensek hozzájárulásai egy jól meghatározott, véges ablakhalmazon összegződnek.
F1 fogalmi, nehézség 1. Definiáld az effektív kernel-kiterjedést. Adj két különböző párt, amelyeknek azonos az effektív kiterjedése, és mutasd meg egy példán, hogy a kimeneti méretük azonos. Magyarázd el, miért nem jelenti ez, hogy a két operátor azonos. Átadandó: definíció, két paraméterpár, kimeneti méret és egy indoklás.
F2 levezetési, nehézség 2. Számítsd ki a kimeneti méretet , , , , esetén. Ellenőrizd az indexfeltételből, hogy a magasság mentén érvényes, de nem; a szélesség mentén érvényes, de nem. Átadandó: mindkét dimenzió levezetése, a kiszámított pár és a négy index feltételvizsgálata.
F3 implementációs, nehézség 2. Írj
out1d és expected_shape függvényeket a tétel
alapján, és teszteld legalább hat különböző beállításon, köztük
nemnégyzetes inputtal. A valid eseteket hasonlítsd össze
torch.nn.Conv2d kimeneti alakjával; egy érvénytelen
beállításnál ellenőrizd, hogy a formula nullánál nem nagyobb értéket ad.
Átadandó: teljes futtatható kód és az output másolata.
F4 hibakeresési, nehézség 2. Az alábbi függvény hibás:
def out_bad(L, k, p, s, d):
return (L + 2*p - (k - 1) - 1)//s + 1Lokalizáld a hibát, mutass ellenpéldát bemenettel, írd le a hiba következményét egy többrétegű hálóra, majd add meg a javított függvényt és a visszaellenőrzést. Átadandó: hiba oka, a hibás és helyes kimenet, javított kód.
F5 kísérleti/transzfer, nehézség 3. Tervezz rövid,
helyi protokollt annak eldöntésére, hogy a telepített PyTorch-verzió
padding='same' üzemmódja hogyan viselkedik a
beállításra. Az összehasonlítás alapja legyen a szükséges teljes padding
paritása: ha páratlan, akkor minimális same padding nem
lehet szimmetrikus. Rögzítsd a verziót, a stride-ot, a mért vagy hiányzó
kimeneti méretet és a következtetést a hordozhatóságról. Átadandó:
protokoll, eredménytáblázat és értékelési kritérium.
F1 megoldás. Az effektív kiterjedés . A pár kiterjedése ; a pár kiterjedése . mellett mindkettőre
A kimeneti méret azonos, de a kernel a bemeneti indexeket pozíciókban mintavételezi, a kernel pedig pozíciókban, ezért a súlyok más lineáris kombinációt képeznek.
F2 megoldás. Magasság: , a validitás teljesül.
A index lefedése , a maximum ; -ra a maximum . Szélesség: , ,
-nál a lefedés maximuma , tehát érvényes; -nél . A kimenet .
F3 megoldás. A labor shape_test.py
kódja teljes megoldás. Egy tömör, önállóan futtatható ellenőrzés:
import torch
def out1d(L, k, p, s, d):
return (L + 2*p - d*(k - 1) - 1)//s + 1
cases = [
((5, 5), 3, 0, 1, 1),
((7, 5), 3, 1, 1, 1),
((7, 6), 3, 0, 2, 1),
((9, 8), 3, 0, 1, 2),
((5, 4), 4, 1, 2, 1),
((13, 7), 5, 0, 2, 1),
]
for (H, W), k, p, s, d in cases:
exp = (out1d(H, k, p, s, d), out1d(W, k, p, s, d))
x = torch.zeros(1, 1, H, W)
y = torch.nn.functional.conv2d(x, torch.zeros(1, 1, k, k),
stride=s, padding=p, dilation=d)
assert tuple(y.shape[2:]) == exp, (exp, y.shape)
print((H, W), "->", exp)
assert out1d(3, 4, 0, 1, 1) <= 0
print("érvénytelen eset ellenőrizve")Az alakegyezés egzakt egész összehasonlítás, ezért nincs szükség
toleranciára. A nem futtatott, számított várt output:
(5, 5) -> (3, 3), (7, 5) -> (7, 5),
(7, 6) -> (3, 2), (9, 8) -> (5, 4),
(5, 4) -> (2, 2), (13, 7) -> (5, 2).
F4 megoldás. A hiba a dilation hiánya: a függvény -et használ a tényleges kiterjedés helyett. A megadott példán a kernel effektív kiterjedése , ezért a helyes eredmény
a hibás függvény viszont
értéket ad. A következmény az, hogy a következő réteg vagy egy
későbbi view/Linear alakillesztése rossz
mérettel számol, és a forward során alakhiba keletkezik, pedig a
számítás geometriailag csak
kimeneti pozíciót enged meg. Javítás:
def out_fixed(L, k, p, s, d):
return (L + 2*p - d*(k - 1) - 1)//s + 1
assert out_fixed(9, 3, 0, 1, 2) == 5
assert out_bad(9, 3, 0, 1, 2) == 7F5 megoldásminta. A protokoll három esetet vizsgál,
mert a szükséges teljes padding paritása mondja meg, hogy a minimális
same padding lehet-e szimmetrikus. A kívánt kimeneti
méretet
-ként
rögzítjük, a szükséges teljes padding
Az esetben és
A
páratlan, tehát a minimális same padding csak
aszimmetrikusan valósítható meg; ha a telepített API ilyenkor hibát ad,
vagy egész szimmetrikus paddingre kényszeríti a hívást, akkor a
viselkedés nem írható le a jelen fejezet egyetlen
paraméterével. A jegyzőkönyvben rögzíteni kell a
torch.__version__ értéket, a padding='same'
hívás kimeneti alakját vagy a RuntimeError szövegét, és
azt, hogy a kapott alak megegyezik-e a szimmetrikus
vagy
változat valamelyikével. A következtetés csak a saját verzióra és erre a
három paraméterkombinációra érvényes; ez nem általános API-teszt.
A CNN-réteg térbeli kimeneti méretét a stride, a szimmetrikus
padding, a kernelméret és a dilation együtt határozza meg. A kernel
effektív kiterjedése
;
egy kimeneti index akkor érvényes, ha a horgonypozícióból indított
összes mintavételi pont a paddinggel kiterjesztett tartományba esik. Az
indexfeltételből levezettük a
formulát, és megadtuk a legalább egy kimeneti ablakot garantáló
feltételt. A labor nyolc beállításon, nemnégyzetes inputtal és kernelmel
ellenőrzi a formulát. A same padding nem azonos
automatikusan a szimmetrikus paddinggel: páratlan szükséges teljes
padding esetén a minimális megoldás aszimmetrikus lehet.
A 14.2. Több bemeneti és kimeneti csatorna egység a több bemeneti és kimeneti csatorna összegzési szerkezetét vizsgálja; az itteni térbeli alakzatok adják a csatornánkénti kimeneti mátrixok méretét és a paraméterszám számításának geometriai alapját.