A 13.3. Lokális szűrők és tanult reprezentáció egységben a cross-correlation réteget úgy definiáltuk, hogy a kernel egy lokális ablakban mozog a bemeneten. Egy hosszúságú véges 1D jelre és hosszúságú kernelre azonban az ablak a széleken kilógna a definíciós tartományból. Ebben az egységben azt vizsgáljuk, hogyan definiáljuk a hiányzó pozíciókat; ez a határfeltétel (boundary condition) megválasztása.
Előismeret: a 13.3. Lokális szűrők és tanult reprezentáció cross-correlation jelölése; a függvény és linearitás fogalma a 4.1.–4.3. egységekből. A frekvenciaintuícióhoz a 13.4. Határkezelés és frekvenciaintuíció alfejezetben bevezetett DFT-t használjuk, de ez a laborban opcionális mélyítés.
Induló kérdés. Egy hosszú jelet hosszú átlagoló kernelrel szűrünk. Hányféle, egymástól lényegesen eltérő módon definiálhatjuk a szélső pozíciókat, és a periodikus jel esetén melyik a „természetes” választás? A becslés: legalább három alapvető mód van — valid, zero padding és körkörös (circular) —, és periodikus jelnél a körkörös kiterjesztés őrzi meg a periodicitást; ezt a 52.3 szakaszban egzakt módon is megvizsgáljuk.
Legyen a bemeneti 1D jel , a kernel pedig , ahol páratlan egész, az egyszerűség kedvéért. A 13.3. Lokális szűrők és tanult reprezentáció cross-correlation definíciója szerint a kimenet egy pozícióban
ahol a paddelt (kiegészített) tömb, a szimmetrikus padding mérete, és a kernel indexe között fut, megfordítás nélkül. A kernel középpontja a pozíció, ezért esetén a kimenet hossza , azonos a bemenettel; ezt same kimenetnek hívjuk. Ha , akkor a kernel soha nem lóg ki a jelből, és a kimenet hossza ; ez a valid eset. A padding nélküli definícióban helyett közvetlenül -et használunk, és a index között fut.
Két objektumot különböztetünk meg. Az virtuális kiterjesztés minden egész indexre értéket ad, és a tartományon egyezik a jellel: . A paddelt tömb ennek egy véges, nullától indexelt kivágata:
A tömbindex és az jelindex tehát -vel el van tolva: , . A határfeltétel az értékét rögzíti a tartományon kívül:
Például , mellett a zero paddinges tömb , a circular paddinges .
A PyTorch Conv2d dokumentációja szerint a natív
konvolúciós réteg szimmetrikus zero paddinget használ, és a
padding_mode paraméter 'zeros',
'reflect', 'replicate' vagy
'circular' értéket vehet fel [D10]. A mi laborunkban a zero
és circular módot hasonlítjuk össze kézi implementációval.
2D-ben a jelölés analóg. Legyen , kernel , padding . A virtuális kiterjesztés , a paddelt tömb , , és a kimenet
A zero padding esetén a tartományon kívüli indexekre , a circular esetén . Batch és csatornadimenziók esetén a padding csak a térbeli dimenziókat érinti, az NCHW alakban és változatlan marad.
Intuíció. A valid mód a legóvatosabb: csak olyan kimenetet ad, ahol minden bemeneti érték valódi. A zero padding a hiányzó értéket nulla intenzitásúnak tekinti, ami a kép fekete szegélyének felel meg. A circular padding a jelet gyűrűként zárja: a bal szélen a jobb szélső értékek jelennek meg. Ez akkor természetes, ha a jel valóban periodikus, például egy körvonal mentén mintavételezett intenzitás; általános képen azonban mesterséges ugrást hoz létre a szemközti szélek között.
A levezetési kötelezettségünk a következő hamis állítás cáfolata: a különböző határkezelések ugyanazt a globális lineáris operátort adják ugyanazon a véges jelen. Az alábbi lemma pontos ellenpéldát mutat.
Lemma (zero és circular padding különböző operátort ad). Legyen , , , , és mindkét paddingnél. Ekkor a zero paddinges kimenet , a circular paddinges kimenet , tehát .
Bizonyítás (ellenpélda). Zero padding esetén , mert a és a jelindex a tartományon kívül esik. A definíció szerint:
Circular padding esetén a modulo szabály szerint és , tehát . Így
A két vektor különböző, így a két határkezeléshez tartozó lineáris leképezés nem azonos.
Következmény. A valid mód eleve más dimenziójú kimenetet ad (), így az sem lehet azonos a fenti kettővel. A határfeltétel tehát a véges jelű cross-correlation definíciós része, nem pusztán implementációs részlet.
Az ellenpélda egyben azt is mutatja, hogy az átlagoló kernel a circular paddingnél a teljes jelen konstans értéket ad, mert a gyűrűn minden pozíció ugyanazt a három értéket látja; a zero padding a széleken csökkenti az összeget.
A fenti kézi példa egyben a numerikus kontroll is. Az alábbi kód
ugyanezt a számítást végzi el NumPy-ban, kiegészítve a labor két
jelével. A dtype float64, mert a cél a pontos
egyezés, nem a tanítási sebesség.
# lab/n052_boundary_check.py
import numpy as np
DTYPE = np.float64
SEED = 0 # nincs véletlen a példában; a jegyzőkönyvhöz rögzítjük
rng = np.random.default_rng(SEED)
def pad_1d(x, p, mode):
"""Szimmetrikus padding 1D jelekre."""
x = np.asarray(x, dtype=DTYPE)
if mode == "zeros":
return np.pad(x, (p, p), mode="constant", constant_values=0.0)
if mode == "circular":
return np.pad(x, (p, p), mode="wrap")
raise ValueError(f"ismeretlen mode: {mode}")
def corr_1d(x, w, boundary):
"""Cross-correlation, K páratlan, same kimenet zero/circular esetén."""
H = x.shape[0]
K = w.shape[0]
assert K % 2 == 1, "csak páratlan kernel támogatott"
p = K // 2
if boundary == "valid":
out = np.empty(H - K + 1, dtype=DTYPE)
for t in range(out.shape[0]):
out[t] = np.sum(x[t:t+K] * w)
return out
xp = pad_1d(x, p, boundary)
out = np.empty(H, dtype=DTYPE)
for t in range(H):
out[t] = np.sum(xp[t:t+K] * w)
return out
x_demo = np.array([1.0, 2.0, 3.0])
w_mean = np.array([1.0, 1.0, 1.0])
print("zero :", corr_1d(x_demo, w_mean, "zeros"))
print("circular:", corr_1d(x_demo, w_mean, "circular"))Várható kimenet (számított példa, nem futtatott):
zero : [3. 6. 5.]
circular: [6. 6. 6.]
A tolerancia itt , mert a műveletek egész értékű összegek.
Kérdés. Hogyan befolyásolja a határfeltétel a szűrő kimenetét periodikus és éles szélű jelen?
Input, import és adatút. A labor két
determinisztikus 1D jelet generál a lab/n052_lab.py
fájlban, külső adatfájl nélkül:
Kernel:
(aluláteresztő), és
(élkiemelő, felüláteresztő). Mindkét kernelt zero és circular paddinggel
futtatjuk,
same kimenettel. A dtype float64, a
device CPU, a seed rögzített, bár a jelek
determinisztikusak.
# lab/n052_lab.py
import numpy as np
from n052_boundary_check import corr_1d
DTYPE = np.float64
SEED = 0
t = np.arange(32, dtype=DTYPE)
x_per = np.sin(2.0 * np.pi * t / 16.0)
x_edge = np.ones(32, dtype=DTYPE)
x_edge[16:] = 0.0
w_mean = np.ones(3, dtype=DTYPE) / 3.0
w_diff = np.array([-1.0, 0.0, 1.0], dtype=DTYPE)
for name, x in [("periodikus", x_per), ("el", x_edge)]:
for b in ["zeros", "circular"]:
y_mean = corr_1d(x, w_mean, b)
y_diff = corr_1d(x, w_diff, b)
print(f"{name:10s} {b:9s} mean szél: {y_mean[0]:+.4f} {y_mean[-1]:+.4f} "
f"diff szél: {y_diff[0]:+.4f} {y_diff[-1]:+.4f}")Futtatás és ellenőrzés. A parancs a
lab/ munkakönyvtárból:
cd lab && python n052_lab.pyEllenőrzés: a corr_1d függvény az alfejezet
demonstrációs példáján a fenti várható értékeket adja; a laborban a
periodikus jel átlagoló kernelre a belső pontokban mindkét padding
azonos értéket ad, de a széleken a zero padding kis eltérést mutat. Az
éljel differenciáló kernelre a circular padding a jel két, egymástól
távoli szélét kapcsolja össze, ami az egyik oldalon mesterséges ugrást
okoz.
Szemléltető, számított kimenet (nem mérés, a képletekből számolt példa):
| Jel | Padding | mean | mean | diff | diff |
|---|---|---|---|---|---|
| periodikus | zeros | ||||
| periodikus | circular | ||||
| él | zeros | ||||
| él | circular |
A periodikus szinusz 32 mintás ablakába pontosan két periódus fér; ezért a circular padding a jel saját folytatásához illeszkedik, a zero padding viszont a bal szélen módosítja az eredményt. A differenciáló szűrő körkörös módban sem ad zérus szélső értéket: a helyes érték a lokális jelváltozásból következik. Az éljelnél a circular padding a távoli végeket is összekapcsolja, és az utolsó pixel gradiensét megváltoztatja.
Opcionális DFT-mélyítés. A körkörös határfeltételhez tartozó szűrés felírható a diszkrét Fourier-transzformációval. Legyen a DFT
A circular paddinges cross-correlation esetén a kernel megfordítottjának DFT-jével való szorzás adja a kimenet DFT-jét; ez a körkonvolúciós tétel standard alakja. A tétel bizonyítását itt elhagyjuk; a feltételeket és a pontos állítást a [B01] Fourier-transzformációról szóló fejezete tárgyalja (a hivatkozott szakasz részletes bibliográfiai ellenőrzése még szükséges). Ha a DFT-szorzást a zero paddinges lineáris szűrésre akarjuk használni, a jelet és a kernelt nullákkal legalább hosszra kell kiegészíteni; különben a körkörös átfolyás a kívánt lineáris konvolúció helyett körkonvolúciós eredményt ad. Mindkét művelet lineáris operátor, a különbség a határkezelésben van.
F1 (fogalmi, nehézség 1). Definiálja szavakban a valid, zero padding és circular padding határfeltételt egy hosszú 1D jelre. Adjon meg egy és értéket, amelyre a valid kimenet hossza nem egyezhet a same kimenet hosszával. Átadandó: a három definíció saját szavakkal, a választott és a kimeneti hosszak képlete.
F2 (levezetési, nehézség 2). Legyen , , kernel páratlan hosszúságú, . Számítsa ki a zero és circular paddingű same kimenet minden elemét, és ellenőrizze, hogy a kimenet hossza . Átadandó: a paddelt jelek és a számítás lépései.
F3 (implementációs, nehézség 2). Írjon NumPy
függvényt corr_2d(X, W, boundary) néven, amely 2D zero és
circular paddingű same cross-correlationt valósít meg
és páratlan
kernelre. Tesztelje az
,
példán. Átadandó: a kód, a futtatás és a kimeneti mátrix.
F4 (hibakeresési, nehézség 3). Az alábbi kód a lineáris (zero paddinges) szűrést DFT-szorzással akarja kiszámítani, de az eredmény hibás:
import numpy as np
x = np.array([1.0, 2.0, 0.0, 1.0])
w = np.array([1.0, 1.0, 1.0])
xpad = np.pad(x, (0, 1)) # 5 hossz
wpad = np.pad(w, (0, 2)) # 5 hossz
y_fft = np.fft.ifft(np.fft.fft(xpad) * np.fft.fft(wpad)).real
print(y_fft) # hibás? hasonlítsa a zero paddinges corr_1d-hezLokalizálja a hibát, magyarázza el a körkörös átfolyást, és adja meg a javítást a minimális szükséges beágyazási hosszal. Átadandó: a hiba oka, a helyes beágyazás és az összehasonlító futás.
F5 (kísérleti/transzfer, nehézség 3). Tervezzen kísérletet a GlyphLab 24×24-es képein annak mérésére, hogy a zero padding és a circular padding hogyan változtatja meg egy fix 3×3-as élkiemelő kernel kimeneti statisztikáját a kép szélén. Adja meg a megfigyelési egységet, a splitet, a metrikát és az érvényességi korlátot. Átadandó: a protokoll szöveges terve, a scene_id csoportosítás indoklásával és az összehasonlítás korlátjával.
F1. A valid mód nem definiál külső pozíciót, ezért a kernel csak a jel belsejében dolgozhat, és a kimenet hossza . A zero padding a hiányzó pozíciókat -nak veszi, a circular padding pedig az indexet modulo tekinti, mintha a jel periódussal ismétlődne. Például , esetén a valid kimenet hossza , a same () kimenet hossza , tehát a kettő nem egyezhet.
F2. , , . Zero padding: . ; ; ; . Tehát .
Circular padding: az indexek modulo 4, tehát (a jelindex , a jelindex ). ; ; ; . Tehát . Mindkét kimenet hossza .
F3. Példakód:
def corr_2d(X, W, boundary):
X = np.asarray(X, dtype=np.float64)
W = np.asarray(W, dtype=np.float64)
H, Wd = X.shape
Kh, Kw = W.shape
assert Kh % 2 == 1 and Kw % 2 == 1
ph, pw = Kh // 2, Kw // 2
if boundary == "zeros":
mode, cval = "constant", 0.0
elif boundary == "circular":
mode, cval = "wrap", 0.0
else:
raise ValueError(boundary)
Xp = np.pad(X, ((ph, ph), (pw, pw)), mode=mode, constant_values=cval) \
if boundary == "zeros" else \
np.pad(X, ((ph, ph), (pw, pw)), mode=mode)
Y = np.empty_like(X)
for i in range(H):
for j in range(Wd):
Y[i, j] = np.sum(Xp[i:i+Kh, j:j+Kw] * W)
return Y
X = np.arange(1, 10, dtype=np.float64).reshape(3, 3)
W = np.array([[0,1,0],[1,0,1],[0,1,0]], dtype=np.float64)
print(corr_2d(X, W, "zeros"))
# [[ 6. 9. 8.]
# [13. 20. 17.]
# [12. 21. 14.]]
print(corr_2d(X, W, "circular"))
# [[16. 17. 18.]
# [19. 20. 21.]
# [22. 23. 24.]]Ellenőrzés. A kereszt alakú kernel a középpont és az átlók helyett csak a négy oldalszomszédot adja össze. Például zero padding mellett a bal felső pixel eredménye , a belső pixelé . Circular padding mellett a bal felső pixel . A két, kóddal is ellenőrzött eredménymátrix:
Mindkét kimenet alakja ; a belső pixel mindkét határkezelésben azonos, míg a szélpixelek eltérhetnek.
F4. A közvetlen, 5 hosszú FFT-szorzás körkonvolúciót
számít, emiatt az utolsó és az első minta összekeveredhet. Két,
és
hosszú sorozat lineáris konvolúciójához legalább
hosszú beágyazás szükséges. A corr_1d keresztkorrelációt
használ, ezért a konvolúciós szorzás előtt a kernelt meg kell fordítani.
Az
odd-
same kimenet a teljes, 6 hosszú konvolúció
pozíciójától kezdődő 4 elem:
x = np.array([1.0, 2.0, 0.0, 1.0])
w = np.ones(3, dtype=np.float64)
fft_size = len(x) + len(w) - 1 # 6
u = np.pad(x, (0, fft_size-len(x)))
v = np.pad(w[::-1], (0, fft_size-len(w)))
full = np.fft.ifft(np.fft.fft(u) * np.fft.fft(v)).real
same = full[1:1+len(x)]
np.testing.assert_allclose(same, corr_1d(x, w, "zeros"), atol=1e-12)
print(same) # [3. 3. 3. 1.]Az eredeti ötelemes beágyazás nem alkalmas az általános lineáris konvolúció visszaállítására. Ebben a speciális, szimmetrikus kerneles példában az első néhány érték esetleg véletlenül egyezhet, de a helyes összehasonlításnak a megfelelően eltolt same-szeletet kell figyelembe vennie.
F5. A kísérleti terv: a GlyphLab 360 képéből a train split 240 képe fix 3×3-as élkiemelő kernel (pl. Sobel-x) zero és circular paddinggel. Megfigyelési egység a scene_id, mert a két nézet ugyanahhoz a jelenethez tartozik, és a split is scene-szintű. Metrika: a kép szélső 2 pixelgyűrűjén számított átlagos abszolút kimeneti érték különbsége a két padding között, képenként, majd scene-szintű átlag és bootstrap CI a scene_id-kből. A protokoll rögzíti a kernel pontos értékeit, a padding méretét () és a dtype-ot. Érvényességi korlát: a GlyphLab mesterséges alakzatképein a szélek gyakran nullák, ezért a zero padding hatása kisebb lehet, mint természetes képeken; az eredmény nem általánosítható más adatra. A korrupt teszt nem használható a paddinghatás mérésére, mert az a választott modell értékelésére van fenntartva; ez a kísérlet nem tanít modellt, ezért nem sérti a tesztválasztási szabályt, de a protokollt a naplóban külön kell rögzíteni.
Tipikus tévedés. Az FFT-s körkonvolúció és a véges lineáris művelet csak megfelelő beágyazással felel meg egymásnak. Ha valaki egy hosszú jelet és hosszú kernelt változatlan hosszon FFT-szoroz, a művelet a hosszú körkonvolúciót adja, nem a zero paddinges lineáris eredményt. Ellenpélda: , ; a zero paddinges same kimenet , a 4 hosszú FFT-szorzás viszont konstans -at ad (a körkörös átfolyás miatt az első és utolsó elem is 3). A javítás a legalább hosszú nulla beágyazás, ahogy az F4 feladat mutatja.
Önellenőrzés. Ellenőrizze, hogy a same kimenet hossza , a valid kimeneté , és hogy a paddelt jel hossza . Számolja ki a szélső pozíciót kézzel, majd a kóddal; ha a zero és circular kimenet egyenlő, akkor vagy a jel periodikus a kernel ablakára nézve, vagy a kernel csak a belső tartományt látja (pl. nagy, ).
Visszahívás. A 13.3. Lokális
szűrők és tanult reprezentáció cross-correlation definícióját
használtuk megfordítás nélkül; a kernel indexelése és a padding mérete
összhangban van a 14.1. Stride, padding, dilation és
outputméret általános kimeneti képletével. A seed és a
determinisztikus jegyzőkönyv kezelése a 12.4. III.
ellenőrző pont: önállóan tanuló kis háló gyakorlatát követi; a
laborban nincs véletlen, de a SEED rögzítése a
reprodukálhatósági szokás része.
A véges jelen a kernel széleken kilóg a tartományból, ezért a határfeltétel a cross-correlation definíciós része. A valid mód csak teljes ablakot enged, a zero padding nullával, a circular padding modulo indexeléssel pótol. Az ellenpélda megmutatta, hogy a zero és circular padding különböző lineáris operátort ad: periodikus jelnél a circular konzisztens, éles szélnél mesterséges ugrást okoz. A DFT-szorzás körkörös határfeltételt valósít meg; lineáris szűréshez a jelet és a kernelt legalább hosszra kell nullázni. A labor két jelen és két kernelnél számszerűsíti a különbséget.
A 14.1. Stride, padding, dilation és outputméret egység a stride, padding és dilation általános kimeneti méretképletét vezeti le, és ezzel a CNN-réteg alakjainak teljes kontrollját adja meg.