27.1 Pixelszintű klasszifikáció és FCN

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

Ez az alfejezet a képosztályozástól a sűrű, térbeli előrejelzés felé lép: minden pixelhez külön osztálycímkét rendelünk. A cél annak pontos rögzítése, hogyan kell a logit-tenzort, a céltenzort és a veszteséget úgy párosítani, hogy az osztálytengely, az érvénytelen pixel és a felbontásváltozás ne okozzon rejtett hibát. Előismeret: a 15.1. Súly- és biasgradiens alfejezetből a konvolúciós réteg gradiense, a 15.4. Független gradiensellenőrzési protokoll alfejezetből a gradiensellenőrzés protokollja, a 16.4. IV. ellenőrző pont: a CNN-operátor teljes megértése alfejezetből a CNN-operátor specifikációja, a 22.4. Augmentációs kísérlet és domainváltozás alfejezetből a clean/corrupt szétválasztás, a 23.4. Hibaszeletek és alkalmazási határ alfejezetből a hibaszeletek, a 26.1. Dobozkoordináta, IoU és lokalizációs loss alfejezetből a doboz és az IoU, a 26.4. Detektálási pipeline és hibaelemzés alfejezetből a képpel szinkronban transzformált annotáció. A korábbi 7.2. Softmax és többosztályos cross-entropy alfejezetben levezettük a softmax–cross-entropy gradiensét; itt csak a pixelszintű általánosítást és a maszkolt átlagot vezetjük le.

Induló kérdés: egy 24×2424\times24-es, egycsatornás GlyphLab-képen három osztályú szegmentálást készítünk. Hány logitot állít elő egy egyképnyi batch, és hány független osztályozási döntést reprezentál? Becslését ellenőrizzük a következő szakasz alakdefiníciója után.

27.1.2 2. Dense prediction, FCN és a tenzoralak-szerződés

Definíció (dense prediction). Legyen X∈ℝN×Cin×H×WX\in\mathbb R^{N\times C_{in}\times H\times W} képbatch. Egy ff modell dense prediction kimenetet ad, ha minden nn mintához egy KK csatornás, Hout×WoutH_{out}\times W_{out} térbeli rácson értelmezett logit-tenzort számol:

Z=f(X)∈ℝN×K×Hout×Wout.Z=f(X)\in\mathbb R^{N\times K\times H_{out}\times W_{out}}.

Minden (n,i,j)(n,i,j) pozícióhoz egy KK-dimenziós logitvektor tartozik. A hagyományos képosztályozó ezzel szemben Z∈ℝN×KZ\in\mathbb R^{N\times K} alakú, tehát a térbeli kiterjedést globálisan eliminálja. A dense prediction megtartja a térbeli szerkezetet, ezért a kimenet közvetlenül összehasonlítható egy pixelcímke-ráccsal.

Definíció (fully convolutional network, FCN). FCN-nek nevezzük azt a hálózatot, amelynek számítási gráfja konvolúciós, pooling- és elemenkénti műveletekből, valamint felbontást növelő interpolációból vagy transposed convolutionből áll, és amely nem tartalmaz teljesen összekötött, az összes térbeli pozíciót egyetlen vektorba keverő réteget. A fogalmat itt architekturális szerződésként használjuk: az NN mintadimenziótól eltekintve minden művelet megőrzi vagy szabályosan változtatja a térbeli rácsot.

A céltenzor alakja:

Y∈{0,1,…,K−1}N×Hout×Wout,Y\in\{0,1,\ldots,K-1\}^{N\times H_{out}\times W_{out}},

ahol Yn,i,jY_{n,i,j} az nn-edik kép (i,j)(i,j) pixelének osztályindexe. Az osztálytengely a logitban a második dimenzió, NCHW konvencióban az 11 indexű tengely; a céltenzornak nincs osztálytengelye, mert indexeket tárol. A betanított modell az

Ŷn,i,j=argmaxc∈{0,…,K−1}Zn,c,i,j\hat Y_{n,i,j}=\arg\max_{c\in\{0,\ldots,K-1\}} Z_{n,c,i,j}

predikciót adja. A softmax a class axis mentén normalizál pixelről pixelre:

Pn,c,i,j=exp(Zn,c,i,j)∑c′=0K−1exp(Zn,c′,i,j),∑c=0K−1Pn,c,i,j=1.P_{n,c,i,j}=\frac{\exp(Z_{n,c,i,j})}{\sum_{c'=0}^{K-1}\exp(Z_{n,c',i,j})},\qquad \sum_{c=0}^{K-1}P_{n,c,i,j}=1.

Ezzel az induló kérdésre a válasz: N=1,K=3,Hout=Wout=24N=1,K=3,H_{out}=W_{out}=24 esetén 1⋅3⋅24⋅24=17281\cdot3\cdot24\cdot24=1728 logit készül, ami 24⋅24=57624\cdot24=576 darab, egymástól függetlenül normalizált háromosztályú döntés.

Az FCN-ek közül a szegmentáló encoder–decoder szerkezet klasszikus példája az U-Net [P14]; az ellenőrzött absztrakt szerint egy kontraháló, kontextust gyűjtő útból és egy szimmetrikus expandáló útból áll, amely a pontos lokalizációt támogatja. A példányszintű szegmentálás felé a Mask R-CNN [P15] absztraktja egy objektumdetektorra épülő, szegmentációs maszkot előrejelző ágat mutat be; az utóbbi a detektálás feltételéhez kötött, ezért nem azonos az itteni, teljes képre kiterjedő pixelszintű FCN-szerződéssel.

27.1.3 3. Per-pixel cross-entropy és maszkolt átlagolás

Egy pixel vesztesége a többosztályos cross-entropy logitformája:

ℓn,i,j=−logPn,Yn,i,j,i,j=−logexp(Zn,Yn,i,j,i,j)∑c=0K−1exp(Zn,c,i,j).\ell_{n,i,j}=-\log P_{n,Y_{n,i,j},i,j} =-\log\frac{\exp(Z_{n,Y_{n,i,j},i,j})}{\sum_{c=0}^{K-1}\exp(Z_{n,c,i,j})}.

A pixelszintű célfüggvény nem a batchméret, hanem az érvényes pixelpozíciók szerinti átlag. Ehhez legyen

M∈{0,1}N×Hout×WoutM\in\{0,1\}^{N\times H_{out}\times W_{out}}

ismert érvényességmaszk, ahol Mn,i,j=1M_{n,i,j}=1 jelenti, hogy az (n,i,j)(n,i,j) célcímke felhasználható. Az érvényes halmaz

𝒱={(n,i,j):Mn,i,j=1},S=|𝒱|.\mathcal V=\{(n,i,j):M_{n,i,j}=1\},\qquad S=|\mathcal V|.

Lemma (maszkolt átlagos pixel-loss nevezője és gradiense). Legyen minden pixelre ℓn,i,j\ell_{n,i,j} differenciálható függvénye a Zn,:,i,jZ_{n,:,i,j} logitvektornak, és legyen MM a logitoktól független, előre rögzített maszk. Ha S>0S>0, a maszkolt átlagos pixel-loss

ℒM=1S∑(n,i,j)∈𝒱ℓn,i,j=∑n=0N−1∑i=0Hout−1∑j=0Wout−1Mn,i,jℓn,i,j∑n=0N−1∑i=0Hout−1∑j=0Wout−1Mn,i,j\mathcal L_M=\frac{1}{S}\sum_{(n,i,j)\in\mathcal V}\ell_{n,i,j} =\frac{\sum_{n=0}^{N-1}\sum_{i=0}^{H_{out}-1}\sum_{j=0}^{W_{out}-1}M_{n,i,j}\ell_{n,i,j}}{\sum_{n=0}^{N-1}\sum_{i=0}^{H_{out}-1}\sum_{j=0}^{W_{out}-1}M_{n,i,j}}

nevezője az érvényes pixelek SS száma, nem a teljes NHoutWoutNH_{out}W_{out} elemszám. A gradiens

∇Zn,c,i,jℒM=Mn,i,jS∇Zn,c,i,jℓn,i,j.\nabla_{Z_{n,c,i,j}}\mathcal L_M =\frac{M_{n,i,j}}{S}\,\nabla_{Z_{n,c,i,j}}\ell_{n,i,j}.

Ha S=0S=0, a 0/00/0 esetet kizárjuk, és a konvenció ℒM=0\mathcal L_M=0.

Bizonyítás. A maszk elemei rögzített skalárok, ezért ℒM\mathcal L_M a ℓn,i,j\ell_{n,i,j} tagok lineáris kombinációja: ℒM=∑n,i,jαn,i,jℓn,i,j\mathcal L_M=\sum_{n,i,j}\alpha_{n,i,j}\ell_{n,i,j}, ahol αn,i,j=Mn,i,j/S\alpha_{n,i,j}=M_{n,i,j}/S. A parciális deriválás lineáris művelet, tehát a láncszabály nélkül is:

∂ℒM∂Zn,c,i,j=∑n′,i′,j′αn′,i′,j′∂ℓn′,i′,j′∂Zn,c,i,j=αn,i,j∂ℓn,i,j∂Zn,c,i,j, \frac{\partial\mathcal L_M}{\partial Z_{n,c,i,j}} =\sum_{n',i',j'}\alpha_{n',i',j'} \frac{\partial\ell_{n',i',j'}}{\partial Z_{n,c,i,j}} =\alpha_{n,i,j}\frac{\partial\ell_{n,i,j}}{\partial Z_{n,c,i,j}},

mert a ℓn′,i′,j′\ell_{n',i',j'} pixelveszteség csak a saját Zn′,:,i′,j′Z_{n',:,i',j'} logitjaitól függ, a (c,i,j)(c,i,j) szerinti parciális derivált pedig csak az azonos (n,i,j)(n,i,j) indexű tagban nem nulla. Ezzel a gradiensformula adódik. ∎

A softmax–cross-entropy gradiensét a 7.2. Softmax és többosztályos cross-entropy alfejezetben vezettük le; pixelszinten ugyanaz a formula érvényes minden (n,i,j)(n,i,j)-re:

∇Zn,c,i,jℓn,i,j=Pn,c,i,j−𝟏[Yn,i,j=c]. \nabla_{Z_{n,c,i,j}}\ell_{n,i,j} =P_{n,c,i,j}-\mathbf 1[Y_{n,i,j}=c].

A lemmával összetéve:

∇Zn,c,i,jℒM=Mn,i,jS(Pn,c,i,j−𝟏[Yn,i,j=c]). \boxed{ \nabla_{Z_{n,c,i,j}}\mathcal L_M =\frac{M_{n,i,j}}{S}\bigl(P_{n,c,i,j}-\mathbf 1[Y_{n,i,j}=c]\bigr). }

Az érvénytelen pixeleken a gradiens nulla, az érvényeseken a szokásos softmax-hiba osztva az érvényes pixelszámmal. Ha minden pixel érvényes, M≡1M\equiv1 és S=NHoutWoutS=NH_{out}W_{out}, tehát a maszkmentes átlagot kapjuk vissza. A sum-redukciós változat, ℒsum=∑Mℓ\mathcal L_{\mathrm{sum}}=\sum M\ell, ugyanennek SS-szerese; az optimalizáló lépésnagyság szempontjából ez nem közömbös, ezért a kísérleti naplóban mindig közölni kell a választott redukciót.

A gyakorlatban az érvénytelenséget gyakran célcímke-szimbólummal jelölik, nem külön maszkkal. Legyen gg a ignore index; ekkor az Yn,i,j=gY_{n,i,j}=g értékű pixelek nem vesznek részt az átlagban. Ez ekvivalens a maszkkal:

Mn,i,j=𝟏[Yn,i,j≠g]. M_{n,i,j}=\mathbf 1[Y_{n,i,j}\neq g].

A lemmánk feltétele teljesül: MM a rögzített célból számolt, nem a modell kimenetéből. A keretrendszer pontos reduction-viselkedését a telepített verzió dokumentációjában kell ellenőrizni; a fenti levezetés az általunk elvárt numerikus konvenciót rögzíti. A mean az érvényes elemek átlaga, a sum az érvényes elemek összege, a none pedig megőrzi a N×Hout×WoutN\times H_{out}\times W_{out} alakú, S-nel osztatlan pixelveszteség-rácsot.

Resize mode és class axis. Ha a hálózat kimeneti rácsa kisebb a célrácsnál, például stride-ok miatt, két út közül választhatunk. A helyes irány az, hogy a logit-tenzort interpoláljuk a cél felbontására a térbeli dimenziók mentén, az osztálytengelyt csatornának kezelve. A diszkrét célcímkéket ezzel szemben nem interpoláljuk. A logitinterpoláció módját explicit módon rögzíteni kell; a leggyakrabban bilineáris interpolációt használunk a KK csatorna mindegyikére. Az interpoláció után a softmax és a veszteség a már definiált képletekkel számolható. A célrácshoz tartozó maszkot a logitokkal együtt sem interpoláljuk; ha a megfigyelés a kimeneti rács pillanatából származik, a maszkot abban a felbontásban kell tárolni.

27.1.4 4. Kézi számpélda

Legyen N=1N=1, K=3K=3, Hout=Wout=2H_{out}=W_{out}=2. A logitok:

Z=[Z00Z01Z10Z11],Z00=[2−10.2],Z01=[−0.510],Z10=[003],Z11=[12−2]. Z=\begin{bmatrix} Z_{00} & Z_{01}\\ Z_{10} & Z_{11} \end{bmatrix},\quad Z_{00}=\begin{bmatrix}2\\-1\\0.2\end{bmatrix},\ Z_{01}=\begin{bmatrix}-0.5\\1\\0\end{bmatrix},\ Z_{10}=\begin{bmatrix}0\\0\\3\end{bmatrix},\ Z_{11}=\begin{bmatrix}1\\2\\-2\end{bmatrix}.

A célrács $Y=\begin{bsmallmatrix}0 & 1\\2 & 0\end{bsmallmatrix}$, a maszk $M=\begin{bsmallmatrix}1 & 0\\1 & 1\end{bsmallmatrix}$. Az érvényes pixelszám S=3S=3.

A softmax a (0,0)(0,0) pixelen: exp\exp értékek 7.389,0.368,1.2217.389,0.368,1.221, összegük 8.9788.978, így P00≈(0.8230,0.0410,0.1360)P_{00}\approx(0.8230,0.0410,0.1360). A cél 00, tehát ℓ00=−log0.8230≈0.1948\ell_{00}=-\log0.8230\approx0.1948.

A (1,0)(1,0) pixelen: exp\exp értékek 1,1,20.0851,1,20.085, összegük 22.08522.085, P10≈(0.0453,0.0453,0.9094)P_{10}\approx(0.0453,0.0453,0.9094), cél 22, ℓ10≈0.0949\ell_{10}\approx0.0949.

A (1,1)(1,1) pixelen: exp\exp értékek 2.718,7.389,0.1352.718,7.389,0.135, összegük 10.24210.242, P11≈(0.2654,0.7214,0.0132)P_{11}\approx(0.2654,0.7214,0.0132), cél 00, ℓ11=−log0.2654≈1.3264\ell_{11}=-\log0.2654\approx1.3264.

A maszkolt átlag:

ℒM=0.1948+0.0949+1.32643≈0.5387. \mathcal L_M=\frac{0.1948+0.0949+1.3264}{3}\approx0.5387.

A gradiens a lemma szerint: a (0,0)(0,0) pixelen (P00−e0)/3=(−0.1770,0.0410,0.1360)/3(P_{00}-e_0)/3=(-0.1770,0.0410,0.1360)/3; a (0,1)(0,1) pixelen (0,0,0)(0,0,0), mert maszkolt; a (1,0)(1,0) pixelen (P10−e2)/3=(0.0453,0.0453,−0.0906)/3(P_{10}-e_2)/3=(0.0453,0.0453,-0.0906)/3; a (1,1)(1,1) pixelen (P11−e0)/3=(−0.7346,0.7214,0.0132)/3(P_{11}-e_0)/3=(-0.7346,0.7214,0.0132)/3. A három érvényes pixel hozzájárulásainak összege az osztálytengely mentén nullát ad, hiszen minden pixel egy-egy P−eyP-e_y vektort kap.

A következő ellenőrizhető munkamenet ugyanezt számítja ki. A kimenet itt számított példa, nem futtatott laborjegyzőkönyv.

# munkakönyvtár: lab/
import numpy as np
rng = np.random.default_rng(20261020)

Z = np.array([[[[2.0, -0.5], [0.0, 1.0]],
               [[-1.0, 1.0], [0.0, 2.0]],
               [[0.2, 0.0], [3.0, -2.0]]]], dtype=np.float64)  # (1,3,2,2)
Y = np.array([[[0, 1], [2, 0]]], dtype=np.int64)              # (1,2,2)
M = np.array([[[1, 0], [1, 1]]], dtype=np.float64)            # (1,2,2)

def softmax_pixelwise(Z):
    Z_shift = Z - Z.max(axis=1, keepdims=True)
    E = np.exp(Z_shift)
    return E / E.sum(axis=1, keepdims=True)

P = softmax_pixelwise(Z)
N, K, H, W = Z.shape
Y_flat = Y.reshape(N, 1, H, W)
I_y = np.eye(K)[Y.reshape(N, H, W)]                 # one-hot: (N,H,W,K)
log_p_y = np.log(P[Y_flat.astype(bool)].reshape(N, H, W))  # hibakerülő, lásd alább
# Megbízhatóbb alakkezelés:
P_ch = np.transpose(P, (0, 2, 3, 1))                # (N,H,W,K)
log_p_y = np.log(np.take_along_axis(P_ch, Y[..., None], axis=3)[..., 0])
ell = -log_p_y
L_masked = np.sum(M * ell) / np.sum(M)
print(f"S={np.sum(M):.0f}, L_masked={L_masked:.6f}")  # S=3, L_masked=0.538681

A Z.max levonása csak numerikus stabilitást szolgál; a softmax értéke változatlan, a log–sum–exp túlcsordulását kerüli el. Az ellenőrző kézi értékkel való egyezés tűrése itt 10−510^{-5} relatív abszolút hiba float64 mellett.

27.1.5 5. Labor: GlyphLab maszk és küszöbbaseline

A labor célja, hogy pixelszintű céladatot töltsünk be, ellenőrizzük a szerződését, és egy triviális, erősítés nélküli baseline-t illesszünk rá. A bináris maszk itt nem a háromosztályú orientációcímke, hanem az előtér/háttér szegmentációs cél leegyszerűsített változata; a küszöbös előrejelzés tehát szegmentációs baseline, nem osztályozó.

# munkakönyvtár: lab/
# futtatás: python n105_baseline.py
import numpy as np
from pathlib import Path

DATA = Path("data")
images = np.load(DATA / "images.npy")      # (360,1,24,24), float32, [0,1]
masks  = np.load(DATA / "masks.npy")       # (360,1,24,24), uint8, {0,1}
labels = np.load(DATA / "labels.npy")      # (360,), int64
meta   = np.loadtxt(DATA / "metadata.csv", dtype=str, delimiter=",", skiprows=1)

print("images:", images.shape, images.dtype,
      "range", float(images.min()), float(images.max()))
print("masks :", masks.shape, masks.dtype,
      "values", np.unique(masks).tolist())

assert images.shape == (360, 1, 24, 24)
assert masks.shape == (360, 1, 24, 24)
assert masks.dtype == np.uint8
assert set(np.unique(masks)) <= {0, 1}

# Baseline: fix intenzitásküszöb az egyetlen csatornán; az 1-es csatorna
# dimenzió elhagyásával a predikció alakja (360,24,24).
threshold = 0.5
pred = (images[:, 0] > threshold).astype(np.uint8)  # (360,24,24)
target = masks[:, 0]                                # (360,24,24)

pixel_acc = float(np.mean(pred == target))
inter = np.logical_and(pred == 1, target == 1).sum()
union = np.logical_or(pred == 1, target == 1).sum()
iou = float(inter / union) if union > 0 else float("nan")
print(f"threshold={threshold}, pixel_acc={pixel_acc:.6f}, IoU={iou:.6f}")

A kód minden képre és minden pixelre egyetlen átfogó metrikát számol. A várt kimenet itt jelölt szemléltetés, nem a környezet által futtatott eredmény: a pixel-accuracy minden bizonnyal magas, mert a háttér dominálhat, míg az IoU lényegesen alacsonyabb lehet, ha a vékony előtérvonalak egy részét a küszöb levágja. Ez mutatja, hogy a 23.4. Hibaszeletek és alkalmazási határ alfejezetben tárgyalt összesített pontszám miért nem elég: bináris szegmentálásban az osztályarány miatt a pixel-accuracy és az IoU nem cserélhető fel. A küszöböt kizárólag train/val felosztáson szabad hangolni; a test pixelarányos metrikája csak a befagyasztott küszöb mellett értelmezhető.

27.1.6 6. Feladatok

F1 (fogalmi, nehézség 1). Magyarázza el, miben különbözik a képosztályozó logit-tenzor és a dense prediction logit-tenzor alakja, és adja meg NCHW konvencióban a class axis indexét. Miért nincs osztálytengelye a céltenzornak? Átadandó: saját szavas definíció és egy N=2,K=4,H=5,W=6N=2,K=4,H=5,W=6 numerikus alakpélda mindkét tenzorra.

F2 (levezetési, nehézség 2). Legyen N=1,K=2,H=2,W=1N=1,K=2,H=2,W=1, logitok Z0,:,0,0=(1,−1)⊤Z_{0,:,0,0}=(1,-1)^\top, Z0,:,1,0=(0,2)⊤Z_{0,:,1,0}=(0,2)^\top, cél $Y=\begin{bsmallmatrix}0\\0\end{bsmallmatrix}$, maszk $M=\begin{bsmallmatrix}1\\0\end{bsmallmatrix}$. Számítsa ki ℒM\mathcal L_M-et és a logitokra vonatkozó teljes gradientsűrűséget a lemmából; ellenőrizze, hogy az osztálytengely mentén minden pixel gradiensösszege nulla.

F3 (implementációs, nehézség 2). Írjon NumPy-függvényt masked_cross_entropy_mean(logits, targets, ignore_index) aláírással, ahol logits alakja (N,K,H,W)(N,K,H,W) float64, targets alakja (N,H,W)(N,H,W) int64. A függvény az ignore_index értékű célpontokat kihagyó átlagot adja vissza. A függvény stabil softmaxot használjon, és S=0 esetén 0.0-t adjon. Ellenőrizze a fenti kézi példa logitjain, az ignore cél (0,1)(0,1)-gyel.

F4 (hibakeresési, nehézség 2). Az alábbi kódrészlet egy segmentációs loss kiszámítására készült:

loss = ((mask * pixel_loss).sum()
        / mask.numel())          # hiba: teljes elemszámmal oszt

Mi a hiba oka, numerikus következménye, és hogyan kell javítani? Adjon egy konkrét hárompixeles számpéldát, amelyben a hibás és a javított loss értéke eltér, és mutassa meg a javított gradiens arányát.

F5 (kísérleti/transzfer, nehézség 3). Tervezzen egy bináris szegmentálási kísérletet a GlyphLab maszkon a küszöbbaseline-hoz: figyeljen a scene-alapú splitre, a küszöbválasztás train/val protokolljára, az IoU és a pixel-accuracy együttes jelentésére, valamint a per-scene bootstrap érvényességére. Átadandó: protokollszöveg, baseline, metrikák és a teszt használatára vonatkozó korlátozás.

27.1.7 7. Megoldások és indoklás

F1 megoldás. A képosztályozó kimenete Z∈ℝN×KZ\in\mathbb R^{N\times K}: minden képhez egyetlen logitvektor tartozik, a térbeli dimenzió eltűnik. A dense prediction kimenete Z∈ℝN×K×Hout×WoutZ\in\mathbb R^{N\times K\times H_{out}\times W_{out}}: az osztálydöntés rögzített térbeli rácson ismétlődik. NCHW esetén a class axis a 00-tól számolt 11 indexű tengely, azaz minden (n,i,j)(n,i,j)-re a Zn,:,i,jZ_{n,:,i,j} szelet az egy pixel osztálylogitja. A céltenzor Y∈{0,…,K−1}N×Hout×WoutY\in\{0,\ldots,K-1\}^{N\times H_{out}\times W_{out}} alakú, mert minden pozícióhoz egyetlen osztályindexet tárol; az osztályt egy egész érték, nem one-hot vektor jelöli. A megadott alakpélda: ZZ alakja (2,4,5,6)(2,4,5,6), YY alakja (2,5,6)(2,5,6).

F2 megoldás. Az első pixel (i=0)(i=0): Z=(1,−1)⊤Z=(1,-1)^\top. exp(Z)=(2.7183,0.3679)\exp(Z)=(2.7183,0.3679), összeg 3.08623.0862, P=(0.8808,0.1192)P=(0.8808,0.1192). A cél 00, ezért ℓ=−log0.8808=0.1269\ell=-\log0.8808=0.1269; a gradiens a lemma nélküli kézzel a logitokra nézve itt P−e0=(−0.1192,0.1192)P-e_0=(-0.1192,0.1192), osztva S=1S=1-gyel, azaz (−0.1192,0.1192)(-0.1192,0.1192). A második pixel maszkja 00, ezért nem számít, és gradiense (0,0)(0,0). Tehát ℒM=0.1269\mathcal L_M=0.1269, a teljes logitgradiens a (0,0)(0,0) pixelen (−0.1192,0.1192)(-0.1192,0.1192), minden más helyen 00. Mind a két sorban a két-komponensű gradiensösszeg 00, mert PP valószínűségeloszlás és e0e_0 összege 11.

F3 megoldás.

import numpy as np

def masked_cross_entropy_mean(logits, targets, ignore_index):
    N, K, H, W = logits.shape
    assert targets.shape == (N, H, W)
    logits_shift = logits - np.max(logits, axis=1, keepdims=True)
    exp_z = np.exp(logits_shift)
    denom = exp_z.sum(axis=1, keepdims=True)          # (N,1,H,W)
    log_p_y = logits_shift - np.log(denom)            # (N,K,H,W)
    # kiválasztás osztálytengely mentén:
    log_p_y_ch = np.transpose(log_p_y, (0, 2, 3, 1))  # (N,H,W,K)
    y_idx = targets[..., None]                        # (N,H,W,1)
    log_p_target = np.take_along_axis(log_p_y_ch, y_idx, axis=-1)[..., 0]  # (N,H,W)
    loss_per_pixel = -log_p_target
    mask = (targets != ignore_index).astype(np.float64)
    S = mask.sum()
    if S == 0:
        return 0.0
    return float(np.sum(mask * loss_per_pixel) / S)

# teszt a kézi példával:
Z = np.array([[[[2.0, -0.5], [0.0, 1.0]],
               [[-1.0, 1.0], [0.0, 2.0]],
               [[0.2, 0.0], [3.0, -2.0]]]], dtype=np.float64)
Y = np.array([[[0, 1], [2, 0]]], dtype=np.int64)
print(masked_cross_entropy_mean(Z, Y, ignore_index=1))  # ~0.5387

Az implementáció nem számol külön M maszkot paraméterként, hanem az ignore indexből állítja elő. Az S=0S=0 ág a lemma konvenciója. A stabil log–sum–exp a logits_shift - log(denom) alakban marad, mert x−log∑exp(x)x-\log\sum\exp(x) numerikusan biztonságosabb, mint log(P)\log(P) külön számítása.

F4 megoldás. A hiba oka, hogy a nevező az összes pixel mask.numel()-e, nem az érvényes mask.sum()-ja. Következménye, hogy ha a maszk ritka, az átlag lefelé torzul, és a gradiens is a teljes rács, nem az érvényes halmaz nagyságával skálázódik. Konkrét példa: három pixel, ℓ=(0.6,0.9,0.3)\ell=(0.6,0.9,0.3), M=(1,1,0)M=(1,1,0); a hibás érték (0.6+0.9+0)/3=0.5(0.6+0.9+0)/3=0.5, a helyes (0.6+0.9)/2=0.75(0.6+0.9)/2=0.75. A javított kód mask.sum()-mal oszt, és a lemmából a gradiens érvényes pontokon 1/S=1/21/S=1/2, míg a hibásnál 1/31/3 lenne. Visszaellenőrzés: mindhárom pixel gradientjét az analitikus MS(P−ey)\frac{M}{S}(P-e_y) képlettel összevetve a hiba megszűnik.

F5 megoldás. Egy lehetséges terv: a megfigyelési egység a scene_id, ezért a küszöböt a train/val scene-ekre, nem a testre hangoljuk. A baseline a képintenzitás fix küszöbös binarizálása. A search-tér t∈{0.05,0.10,…,0.95}t\in\{0.05,0.10,\ldots,0.95\}; a valasztáshoz a val halmazon számolt IoU-t használjuk, mert a pixel-accuracy a háttérdominancia miatt gyenge diszkriminátor. A kiválasztott t*t^*-ot a befagyasztott bináris döntési szabállyal egyszer értékeljük a testen: per-image IoU átlag, illetve az összes pixelre számolt globális IoU és pixel-accuracy, mert ezek aggregálása eltérő. A bizonytalansághoz scene-rétegzett bootstrapet használunk, ahol minden resample egység egy scene, és a két view-t mindig egy blokkban tartjuk. A teszt eredményéből nem módosítjuk a küszöböt; a korrupciótesztet a 22.4. Augmentációs kísérlet és domainváltozás alfejezethez hasonlóan csak a befagyasztott szabályon, páros scene-szerkezettel értékeljük.

27.1.8 8. Tipikus tévedés, önellenőrzés

Tipikus tévedés. A diszkrét célcímke felbontásváltoztatását bilineáris interpolációval oldják meg. Ellenpélda: legyen egy kétszomszédos célpixel értéke 00 és 11. Bilineáris interpolációval köztes értékek, például 0.250.25 és 0.750.75 keletkezhetnek, amelyek sem a 00, sem az 11 osztályt nem jelentik. A cél az osztályindexek halmazán kívüli értékeket kap, a loss vagy hibaüzenettel leáll, vagy némán hamis osztályként értelmezi őket. A javítás az, hogy a logitokat interpoláljuk tetszőleges sima móddal, a címketenzorra ellenben nearest-neighbor módot használunk, vagy a megfigyelést már a célrácsban tároljuk. A szabály a 26.4. Detektálási pipeline és hibaelemzés szinkrontranszformációs követelményének pontos megfelelője: a diszkrét céloknál a geometriai transzformáció nem keverhet osztályokat.

Önellenőrzés. (1) Adott ZZ alakja (2,5,16,16)(2,5,16,16). Hány független osztályozási pontot tartalmaz, és hol van a class axis? Válasz: 2⋅16⋅16=5122\cdot16\cdot16=512 pont, az 11 indexű tengelyen. (2) Ha S=0S=0, mit ad a lemmánk? 00-t, de ezt kísérleti naplóban külön jelezni kell, mert elrejtheti az üres batch hibáját. (3) Korábbi fogalom visszahívása: a 15.1. Súly- és biasgradiens alfejezetben levezetett biasgradiens a batch és térbeli indexek szerinti upstream-összeg volt. A pixelszintű lossban a pixel-gradiensek hasonlóan adódnak össze a konvolúciós backward során, de az átlagolás skálája előbb a maszkolt loss-szal, utóbb a minibatch átlagával kétszeres redukciót is jelenthet; ezért minden nem-skalár lépésnél ellenőrizni kell, hogy a nevező SS, a batchméret, vagy a teljes pixelrács-e.

Visszahívás (26.1. Dobozkoordináta, IoU és lokalizációs loss, 23.1. Confusion matrix és aggregálási konvenció). Az IoU a pixelszintű előrejelzésben is a metszet/unió hányados, a dobozos definícióval azonos arányfogalom; a fél-nyílt koordinátakonvenció viszont itt már nem kétdimenziós intervallumra, hanem a diszkrét pixelhalmaz elemszámára vonatkozik. A 23.1. Confusion matrix és aggregálási konvenció tanulsága alapján a pixel-accuracy globális átlag, az IoU osztályszerű halmazátfedés: kiegyensúlyozatlan foreground esetén a két metrika egymásnak ellentmondó következtetésre vezethet.

27.1.9 Rövid összefoglaló

A dense prediction modell N×K×Hout×WoutN\times K\times H_{out}\times W_{out} logitrácsot ad, ahol minden (n,i,j)(n,i,j) pixel külön osztályozási pont, a class axis NCHW-ban az 1. A pixel-cross-entropy az érvényes célpixelek átlaga: ℒM=1S∑Mℓ\mathcal L_M=\frac1S\sum M\ell, S=∑MS=\sum M. A lemma bizonyítja, hogy a nevező az érvényes pixelszám, a gradiens pedig MS(P−ey)\frac{M}{S}(P-e_y). Az ignore index egy adott célból származó maszkkal ekvivalens. A logitokat bilineárisan interpolálhatjuk a célrácsra; a diszkrét címkéket csak nearest módon szabad transzformálni. A labor a GlyphLab maszkon fix küszöbbaseline-t illeszt, és a pixel-accuracy mellé IoU-t tesz, mert a háttérdominancia félrevezető pontszámot adhat.

27.1.10 Felhasznált források

A masked mean lemma és a kézi példa saját levezetés; a labor futási kimenete a szövegben jelölt szemléltetés, nem mért eredmény.