39.1 Feladat- és annotációs döntés

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

A Projekt III-ban a CNN-t új céllal használjuk: a GlyphLab-kép osztálycímkéje helyett az alakzat térbeli kiterjedését kell megmondani. A tanulási cél most nem a három orientációs osztály, hanem vagy az előtér pixelcímkéje, vagy az azt befoglaló téglalap. A döntés első lépése nem architekturális, hanem annotációs: mit tekintünk a modell kimenetének, mi a hozzá tartozó ground truth, és milyen metrika méri a teljesítményt. A GlyphLab két annotációt tartalmaz: a masks.npy bináris előtér/háttér maszkot, a metadata.csv pedig az előtér legkisebb befoglaló téglalapját (bbox). Ebben az egységben e két cél közötti választást alapozzuk meg, rögzítjük a target alakját, a csoportos splitet és az üres eset konvencióját.

Előismeret: a 38.1. Adat, split és előzetes protokoll rögzítette a jelenetszintű splittel és a befagyasztott tesztprotokollt; a 27.1. Pixelszintű klasszifikáció és FCN bevezette a pixelszintű osztályozást és a per-pixel loss nevezőjét; a 26.4. Detektálási pipeline és hibaelemzés a detektálási pipeline-ban a doboztranszformációt; a 27.4. Instance mask és összetett feladat a semantic és instance maszk megkülönböztetését; a 35.1. Saliency és Grad-CAM a Grad-CAM-alapú lokalizációs vizualizációt. A Grad-CAM heurisztikus lokalizációt ad, de nem helyettesíti a tényleges maszk- vagy dobozcélú tanítást.

Induló kérdés. A GlyphLab-képeken az alakzat vékony vonal vagy rúd; a maszk gyakran csak néhány pixel széles. Elég-e a befoglaló téglalap ahhoz, hogy a „hol van az objektum” alkalmazási kérdést megválaszoljuk? A becslésünk: ha a felhasználás pixelmaszkot igényel, akkor nem, mert a bbox nem őrzi meg a maszk alakját, ürességét és több komponensét.

39.1.2 2. Definíciók és annotációs konvenció

Legyen egy GlyphLab-kép

I∈[0,1]1×H×W,H=W=24, I\in[0,1]^{1\times H\times W},\qquad H=W=24,

float32, NCHW tárolásban. A képhez két annotáció tartozik.

Semantic mask. A bináris szemantikus maszk minden pixelhez a háttér/előtér osztályt rendeli:

M∈{0,1}1×H×W, M\in\{0,1\}^{1\times H\times W},

ahol M0,i,j=1M_{0,i,j}=1 jelenti az előtér-, M0,i,j=0M_{0,i,j}=0 a háttérpixelt. A sorindex ii a yy, az oszlopindex jj az xx koordináta; mindkettő nullától indexelt. A GlyphLab masks.npy uint8 típusú, de a tanítási target float32-re konvertálható, mert a bináris BCEWithLogitsLoss float targetet vár. A batch alak

Y∈{0,1}N×1×H×W. Y\in\{0,1\}^{N\times1\times H\times W}.

Többosztályos általánosításban Y∈{0,…,K−1}N×H×WY\in\{0,\ldots,K-1\}^{N\times H\times W} int64 target lenne, a modell pedig Z∈ℝN×K×H×WZ\in\mathbb R^{N\times K\times H\times W} logitsot adna; a GlyphLab bináris előtere miatt itt a K=2K=2 eset egyetlen logit-csatornával, BCEWithLogitsLoss-szal is leírható.

Bbox. A befoglaló téglalap négy egész koordináta:

b=(xmin,ymin,xmax,ymax)∈ℤ4, b=(x_{\min},y_{\min},x_{\max},y_{\max})\in\mathbb Z^4,

a következő fél-nyílt intervallum-konvencióval:

pixel(i,j)∈b⇔ymin≤i<ymaxésxmin≤j<xmax. \text{pixel}(i,j)\in b\iff y_{\min}\le i<y_{\max}\ \text{és}\ x_{\min}\le j<x_{\max}.

A GlyphLab-szerződés ezt rögzíti: a doboz zérus alapú, fél-nyílt xyxy, és a bbox a maszk nemnulla pixeleinek legkisebb befoglaló téglalapja. Érvényes nemüres dobozra 0≤xmin<xmax≤W0\le x_{\min}<x_{\max}\le W és 0≤ymin<ymax≤H0\le y_{\min}<y_{\max}\le H. A cél batch alakja lokalizációs feladatban B∈ℝN×4B\in\mathbb R^{N\times4}; koordinátái pixel egységben értendők. A lokalizációs fej emellett általában egy objektum-jelenlét logitot is ad, hogy az üres képeket kezelni tudja.

Üres eset. Ha egy képen nincs egyetlen előtérpixel sem, a maszkcél a teljes nulla tömb M=0M=0, a dobozcél pedig a szentinel

b∅=(−1,−1,−1,−1). b_{\emptyset}=(-1,-1,-1,-1).

A szentinel helyi, explicit sémadöntés, nem matematikai szükségszerűség. Indoka kettős: (a) a negatív koordináta egyetlen érvényes dobozban sem fordulhat elő, ezért az üres eset egyetlen összehasonlítással felismerhető, és nem keverhető össze semmilyen valódi dobozzal; (b) a dobozregressziós veszteségből az ilyen célokat kizárjuk, így az érvénytelen állapot nem kerül a loss számlálójába. A (0,0,0,0)(0,0,0,0) a fél-nyílt konvencióban nulla területű, érvénytelen doboz — nem egy pixelt fed le (a (0,0)(0,0) pixel doboza (0,0,1,1)(0,0,1,1)) —, tehát elvben szintén jelölhetne ürességet; a negatív szentinelt a fenti két gyakorlati ok miatt választjuk. A 39.3. Geometriai kontroll és metrika validate_box függvénye és a 39.4. Alkalmazási riport és átadás exportja ugyanezt a szentinelt fogadja el, és üres predikált maszkra ezt írja.

Csoportos split. A maszk és a bbox a képhez tartozó annotáció, ezért a splitet nem szabad képszinten újra véletlenezni. A megfigyelési egység a scene_id: egy jelenet mindkét view-ja és minden annotációja ugyanabba a train/val/test részbe kerül. A 38.1. Adat, split és előzetes protokoll már levezette, hogy a jelenetszintű split a megnevezett használati helyzetben torzítatlan tesztbecslőt ad, míg a képszintű véletlen split a két view közötti hasonlóság miatt szivárgást okozna. A Project III ezt a splitet nem változtatja meg: 120/30/30 jelenet, osztályrétegzetten generálva.

Annotation convention. A kéziratban végig a nulla alapú, sor-major pixelindexelést és a fél-nyílt [min,max) intervallumot használjuk. A kép bal felső pixele a (0,0)(0,0) cella; a jj oszlop a jj-edik egységcellát fedi le, ezért a jobb szélső oszlopindex W−1W-1. A bbox a cellákat teljes egészében tartalmazza, nem a cellák közötti élvonalat adja meg.

39.1.3 3. Konzisztens koordináta- és üreseset-definíció

A két annotáció közötti átjárást két leképezéssel rögzítjük. Legyen F={(i,j):M0,i,j=1}F=\{(i,j):M_{0,i,j}=1\} az előtérpixelek halmaza.

Definíció (maszkból bbox, β\beta). Ha F≠∅F\ne\emptyset, akkor

xmin=min(i,j)∈Fj,xmax=1+max(i,j)∈Fj,ymin=min(i,j)∈Fi,ymax=1+max(i,j)∈Fi. \begin{aligned} x_{\min}&=\min_{(i,j)\in F}j,\quad &x_{\max}&=1+\max_{(i,j)\in F}j,\\ y_{\min}&=\min_{(i,j)\in F}i,\quad &y_{\max}&=1+\max_{(i,j)\in F}i. \end{aligned}

Ha F=∅F=\emptyset, akkor β(M)=(−1,−1,−1,−1)\beta(M)=(-1,-1,-1,-1).

Definíció (bboxból maszk, γ\gamma). Érvényes b=(xmin,ymin,xmax,ymax)b=(x_{\min},y_{\min},x_{\max},y_{\max}) esetén

γ(b)0,i,j={1,ha ymin≤i<ymaxésxmin≤j<xmax,0,különben. \gamma(b)_{0,i,j}= \begin{cases} 1,& \text{ha } y_{\min}\le i<y_{\max}\ \text{és}\ x_{\min}\le j<x_{\max},\\ 0,& \text{különben}. \end{cases}

A szentinel esetre γ((−1,−1,−1,−1))=0\gamma((-1,-1,-1,-1))=0.

Állítás (befoglaló téglalap konzisztenciája). A fenti definíciókkal:

  1. Ha F≠∅F\ne\emptyset, akkor β(M)\beta(M) érvényes bbox, és minden (i,j)∈F(i,j)\in F pixel benne van a β(M)\beta(M)-beli fél-nyílt téglalapban.
  2. γ(β(M))≥M\gamma(\beta(M))\ge M pontonként; egyenlőség pontosan akkor, ha MM a saját befoglaló téglalapjának minden celláján 11.
  3. Minden érvényes bb bboxra β(γ(b))=b\beta(\gamma(b))=b.
  4. Ha F=∅F=\emptyset, akkor γ(β(M))=0=M\gamma(\beta(M))=0=M.

Bizonyítás. (1) Véges nemüres halmazon a min\min és max\max létezik. A definícióból xmin≤xmax−1x_{\min}\le x_{\max}-1, tehát xmin<xmaxx_{\min}<x_{\max}; hasonlóan ymin<ymaxy_{\min}<y_{\max}. A min/max tulajdonsága miatt tetszőleges (i,j)∈F(i,j)\in F-re xmin≤j≤xmax−1x_{\min}\le j\le x_{\max}-1, azaz j∈[xmin,xmax)j\in[x_{\min},x_{\max}), és ymin≤i≤ymax−1y_{\min}\le i\le y_{\max}-1, azaz i∈[ymin,ymax)i\in[y_{\min},y_{\max}).

  1. Az (1) állítás szerint minden FF-beli cellában γ(β(M))=1=M\gamma(\beta(M))=1=M, a többi cellában γ(β(M))≥0=M\gamma(\beta(M))\ge0=M. Egyenlőség pontosan akkor áll, ha a befoglaló téglalap minden cellája FF-ben van, tehát nincs benne háttérpixel.

  2. Legyen bb érvényes. Ekkor Fγ={(i,j):ymin≤i<ymax,xmin≤j<xmax}F_\gamma=\{(i,j):y_{\min}\le i<y_{\max}, x_{\min}\le j<x_{\max}\} nemüres, mert mindkét intervallum hossza legalább 11. A sor-minimum yminy_{\min}, a sor-maximum ymax−1y_{\max}-1; a +1+1 szabály így ymaxy_{\max}-ot ad vissza. Az oszlopokra analóg módon xmaxx_{\max}-ot kapunk.

  3. A szentinel definíciójából közvetlenül adódik. ▫\square

A levezetés mutatja, hogy a fél-nyílt konvenció és a +1+1-es szabály együtt konzisztens: a bboxból visszaállított maszk befoglaló téglalapja pontosan az eredeti bbox, míg a maszkból származtatott bboxból visszaállított maszk felső becslése az eredeti maszknak. A kettő pontosan akkor esik egybe, ha a maszk maga is téglalap alakú. Ez a geometriai tény a célválasztás központi érve: a bbox cél csak téglalap-őrző, a maszk cél alak-őrző.

A választott fő annotáció. A Project III alapváltozata a GlyphLab bináris foreground maszkját választja szemantikus szegmentálási célként, mert (a) a masks.npy teljes pixelcímkét ad, (b) a bbox abból egyértelműen származtatható, fordítva nem, és (c) a maszkmetrika a vékony vonalak tényleges alakját méri. A projekt végig ezen a főágon marad. A 39.3. Geometriai kontroll és metrika és a 39.4. Alkalmazási riport és átadás dobozai nem egy külön tanított lokalizációs fej kimenetei, hanem a predikált maszkból a fenti β\beta leképezéssel származtatott xyxy dobozok: a maszkot a σ(z)>0,5\sigma(z)>0{,}5 küszöb adja, üres predikált maszkra a doboz a (−1,−1,−1,−1)(-1,-1,-1,-1) szentinel, és az exportot a 39.4. Alkalmazási riport és átadás export_delivery.py szkriptje végzi. A maszkon mért IoU neve mindenhol mask_iou, a dobozon mérté bbox_iou. Önálló dobozregressziós fej tanítása a projektnek nem része; legfeljebb transzferfeladatként szerepel.

39.1.4 4. Eredeti kézi kis példa

Tekintsük az 5×55\times5-ös maszkot:

0 0 0 0 0
0 1 1 0 0
0 1 0 1 0
0 0 1 0 0
0 0 0 0 0

Az előtérpixelek halmaza:

F={(1,1),(1,2),(2,1),(2,3),(3,2)}. F=\{(1,1),(1,2),(2,1),(2,3),(3,2)\}.

A definíció szerint

xmin=1,xmax=xmax index+1=3+1=4,ymin=1,ymax=3+1=4, x_{\min}=1,\quad x_{\max}=x_{\max\text{ index}}+1=3+1=4,\qquad y_{\min}=1,\quad y_{\max}=3+1=4,

tehát a bbox (1,1,4,4)(1,1,4,4). A befoglaló téglalap területe (4−1)(4−1)=9(4-1)(4-1)=9, míg a maszk területe |F|=5|F|=5. A bboxból visszaállított maszk:

0 0 0 0 0
0 1 1 1 0
0 1 1 1 0
0 1 1 1 0
0 0 0 0 0

A két maszk metszete 55 pixel; a maszk-Dice:

Dice=2⋅55+9=1014≈0.7143, \text{Dice}=\frac{2\cdot5}{5+9}=\frac{10}{14}\approx0.7143,

a maszk-IoU pedig 59≈0.5556\frac{5}{9}\approx0.5556. Ha viszont a célt bboxban mérnénk, a predikált befoglaló téglalap pontosan megegyezik a ground truth bboxszal, tehát a bbox-IoU 11 lenne. Ugyanaz a predikció a bbox-metrikában tökéletes, a maszk-metrikában gyenge; ez a későbbi tipikus tévedés alapja.

Most üres eset: legyen M=0M=0. Ekkor β(M)=(−1,−1,−1,−1)\beta(M)=(-1,-1,-1,-1), és γ(β(M))=0\gamma(\beta(M))=0, tehát a körbejárás az üres maszkot adja vissza. A maszk-Dice-t az üres–üres esetre 11-nek definiáljuk; ha csak az egyik oldal üres, a Dice 00, mert a metszet nulla, a nevező pedig pozitív.

39.1.5 5. Konkrét labor: annotációs specifikáció és baseline

A labor egy specifikációs és konzisztencia-ellenőrző szkriptet készít. Nem tanít modellt: az a 39.2. Encoder–decoder vagy lokalizációs fej feladata. A labor célja, hogy a Project III target-szerződése géppel ellenőrzött legyen, és a későbbi tanítás ne indulhasson el ellentmondásos annotációval.

Munkakönyvtár és parancs. Hozzuk létre a lab/project3 könyvtárat, mentsük a szkriptet annotation_spec.py néven, majd futtassuk:

mkdir -p lab/project3
# a fenti kód lab/project3/annotation_spec.py néven mentendő
cd lab/project3
python annotation_spec.py --data ../data

A GlyphLab adatfájlok a lab/data könyvtárban vannak. A környezet Python 3.10+, NumPy, nincs GPU, nincs külső súly. A seed itt nem releváns, mert a szkript determinisztikus ellenőrzést végez; az adatgenerátor seedjét a manifest.json rögzíti.

#!/usr/bin/env python3
# lab/project3/annotation_spec.py
"""Project III annotation specification and consistency check."""
import argparse
import csv
import json
from collections import defaultdict
from pathlib import Path

import numpy as np

H, W = 24, 24
EMPTY_BBOX = (-1, -1, -1, -1)


def mask_to_bbox(mask: np.ndarray) -> tuple[int, int, int, int]:
    """Binary mask (H, W) -> half-open xyxy bbox or EMPTY_BBOX."""
    if not np.all(np.isin(mask, [0, 1])):
        raise ValueError("mask values must be 0/1")
    ys, xs = np.nonzero(mask)
    if len(xs) == 0:
        return EMPTY_BBOX
    return int(xs.min()), int(ys.min()), int(xs.max()) + 1, int(ys.max()) + 1


def dice(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """Dice for two binary masks. Empty-empty convention: 1."""
    yt = y_true.astype(np.float64)
    yp = y_pred.astype(np.float64)
    inter = float(np.sum(yt * yp))
    denom = float(np.sum(yt) + np.sum(yp))
    if denom == 0.0:
        return 1.0
    return 2.0 * inter / denom


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--data", type=Path, default=Path("../data"))
    parser.add_argument("--out", type=Path, default=Path("spec.json"))
    args = parser.parse_args()

    images = np.load(args.data / "images.npy")
    masks = np.load(args.data / "masks.npy")
    with open(args.data / "metadata.csv", newline="", encoding="utf-8") as f:
        rows = list(csv.DictReader(f))

    assert images.shape == (360, 1, H, W), images.shape
    assert masks.shape == (360, 1, H, W), masks.shape
    assert masks.dtype == np.uint8, masks.dtype
    assert np.all(np.isin(masks, [0, 1])), "non-binary mask value"

    mismatches = []
    for r in rows:
        i = int(r["index"])
        b_file = (int(r["xmin"]), int(r["ymin"]),
                  int(r["xmax"]), int(r["ymax"]))
        b_mask = mask_to_bbox(masks[i, 0])
        if b_mask != b_file:
            mismatches.append((i, b_file, b_mask))

    by_scene = defaultdict(set)
    for r in rows:
        by_scene[r["scene_id"]].add(r["split"])
    scene_conflicts = [s for s, sp in by_scene.items() if len(sp) > 1]

    empty_count = sum(1 for i in range(len(masks)) if np.sum(masks[i]) == 0)

    val_idx = [int(r["index"]) for r in rows if r["split"] == "val"]
    all_foreground = np.ones((H, W), dtype=np.float64)
    empty_pred = np.zeros((H, W), dtype=np.float64)
    d_empty = [dice(masks[i, 0], empty_pred) for i in val_idx]
    d_all = [dice(masks[i, 0], all_foreground) for i in val_idx]

    spec = {
        "target": "semantic_mask",
        "input": {"shape": [1, H, W], "dtype": "float32", "range": [0.0, 1.0]},
        "target_shape": [1, H, W],
        "target_dtype": "float32",
        "target_values": [0, 1],
        "coordinate_convention":
            "zero-indexed; y=row, x=column; bbox half-open [xmin,xmax) x [ymin,ymax)",
        "empty_case": {
            "mask": "all-zero array",
            "bbox_sentinel": list(EMPTY_BBOX),
            "dice_empty_empty": 1.0,
            "dice_one_empty": 0.0,
        },
        "split": {
            "unit": "scene_id",
            "train_scenes": 120,
            "val_scenes": 30,
            "test_scenes": 30,
        },
        "baselines": {
            "empty_mask": {"mean_val_dice": float(np.mean(d_empty))},
            "all_foreground": {"mean_val_dice": float(np.mean(d_all))},
        },
        "consistency": {
            "metadata_bbox_matches_mask": len(mismatches) == 0,
            "mismatch_count": len(mismatches),
            "empty_mask_count": empty_count,
            "scene_split_conflicts": len(scene_conflicts),
        },
    }
    with open(args.out, "w", encoding="utf-8") as f:
        json.dump(spec, f, indent=2, ensure_ascii=False)

    ok = len(mismatches) == 0 and len(scene_conflicts) == 0
    print("CONSISTENCY_OK" if ok else "CONSISTENCY_FAIL")
    print(json.dumps(spec, indent=2, ensure_ascii=False))


if __name__ == "__main__":
    main()

Átadandó eredmény. A labor kimenete a lab/project3/spec.json, amely rögzíti a semantic_mask targetet, a target shape-et, a koordináta- és üreseset-konvenciót, a jelenetszintű splitet és a két baseline-t. A konzol CONSISTENCY_OK sorának meg kell jelennie; ez azt ellenőrzi, hogy a metadata.csv-beli bbox minden képre pontosan megegyezik a maszkból származtatott befoglaló téglalappal, és egyetlen jelenet sem került több splitbe. A baselines.empty_mask.mean_val_dice várhatóan 00, ha minden val maszk nemüres; az all_foreground érték a val maszkok átlagos kitöltöttségét tükrözi, és a későbbi modellnek ezt kell jelentősen meghaladnia. Ezt az értéket a szkript a tényleges val maszkokból számolja. Futási jegyzőkönyv (eredeti GlyphLab-adat, NumPy 2.3.5): a konzol CONSISTENCY_OK-t írt, a mismatch_count, az empty_mask_count és a scene_split_conflicts egyaránt 00, az empty_mask baseline val-Dice értéke 00, az all_foreground baseline-é 0,04120{,}0412; ez utóbbi a kis előtérarányt tükrözi.

A baseline a val spliten mérhető, mert a val a modellválasztási halmaz; a végső, befagyasztott modell tesztkiértékelése a 39.4. Alkalmazási riport és átadás alfejezetben történik majd egyszer, a clean test maszkjain.

39.1.6 6. Feladatok

F1 (fogalmi, nehézség 1). Magyarázza el a szemantikus maszk, a bbox és az instance maszk különbségét. Adjon egy kis, 3×33\times3-as bináris maszkpéldát, amelyben ugyanaz a szemantikus maszk két különböző instance-annotációt enged meg. Átadandó: legfeljebb féloldalas saját magyarázat és a konkrét példa.

F2 (levezetési, nehézség 2). Adott a 4×44\times4-es maszk:

1 1 0 0
1 0 0 0
0 0 1 0
0 0 0 0

Számolja ki a β(M)\beta(M) bboxot és a γ(β(M))\gamma(\beta(M)) kitöltött maszkot. Számolja ki a két maszk közötti Dice-t és IoU-t. Bizonyítsa a 3. szakasz állításának 2. pontját: γ(β(M))≥M\gamma(\beta(M))\ge M pontonként, és adja meg, mikor áll egyenlőség. Átadandó: a bbox, a kitöltött maszk, a két metrikaérték és a bizonyítás lépései.

F3 (implementációs, nehézség 2). Írjon mask_to_bbox és dice függvényt a fenti konvencióval. Tesztelje három esetre: az 5. szakasz 5×55\times5-ös példájára, az üres maszkra és az egyetlen (i,j)=(2,3)(i,j)=(2,3) pixelt tartalmazó maszkra. Átadandó: futtatható kód és a három kimenet.

F4 (hibakeresési, nehézség 2). Valaki a következő hibás függvényt írta:

def wrong_bbox(mask):
    ys, xs = np.nonzero(mask)
    return (xs.min(), ys.min(), xs.max(), ys.max())

Magyarázza meg a hiba okát, mutassa meg egy olyan 2×22\times2-es bemeneten, hogy a visszaadott bbox a fél-nyílt konvencióban nem tartalmazza az összes előtérpixelt, és adja meg a javítást. Mi a következménye a területre és az IoU-ra? Átadandó: hibalokalizáció, minimális reprodukció, javított kód és ellenőrzés.

F5 (kísérlettervezési transzfer, nehézség 3). Tervezzen egy GlyphLab-kísérletet, amely eldönti, hogy a választott alkalmazási kérdésre a maszkcél vagy a bboxcél illik jobban. Nevezze meg a kérdést, a baseline-t, a splitet, a metrikát és az érvényességi korlátot. Az előre megadott győztes nem kötelező; a negatív eredmény is elfogadható. Átadandó: egyoldalas protokollterv.

39.1.7 7. Megoldások és indoklás

F1. A szemantikus maszk minden pixelhez osztálycímkét rendel; a bbox csak négy koordinátát ad; az instance maszk ezen felül az azonos osztályú objektumokat is elkülönülő példányokra bontja. Példa: a 3×33\times3-as maszk

1 0 1
0 0 0
1 0 1

szemantikusan négy előtérpixelt tartalmaz, de instance-szinten lehet két függőleges, két vízszintes, vagy négy különálló folt felosztása; a szemantikus maszkból az instance-annotáció nem vezethető le. Ez a 27.4. Instance mask és összetett feladat tanulsága.

F2. A 4×44\times4-es maszk előtere F={(0,0),(0,1),(1,0),(2,2)}F=\{(0,0),(0,1),(1,0),(2,2)\}. A min/max:

xmin=0,xmax=2+1=3,ymin=0,ymax=2+1=3, x_{\min}=0,\ x_{\max}=2+1=3,\qquad y_{\min}=0,\ y_{\max}=2+1=3,

tehát b=(0,0,3,3)b=(0,0,3,3). A γ(b)\gamma(b) kitöltött maszk:

1 1 1 0
1 1 1 0
1 1 1 0
0 0 0 0

A metszet 44, a két terület 44 és 99, tehát

Dice=2⋅44+9=813≈0.6154,IoU=49≈0.4444. \text{Dice}=\frac{2\cdot4}{4+9}=\frac{8}{13}\approx0.6154,\qquad \text{IoU}=\frac{4}{9}\approx0.4444.

A bizonyítás: a β\beta definíciójából minden FF-beli pixel koordinátái a [xmin,xmax)[x_{\min},x_{\max}) és [ymin,ymax)[y_{\min},y_{\max}) intervallumba esnek, ezért γ(β(M))\gamma(\beta(M)) ott 11 értéket vesz fel. A bboxon kívüli pixeleken M=0M=0, és γ∈{0,1}\gamma\in\{0,1\}, tehát γ(β(M))≥M\gamma(\beta(M))\ge M minden cellában. Egyenlőség pontosan akkor, ha a bboxon belül minden cella MM szerint 11; ez az az eset, amikor a maszk téglalapot tölt ki.

F3. A függvények a labor szkriptből vehetők. A tesztek:

import numpy as np

EMPTY = (-1, -1, -1, -1)

def mask_to_bbox(mask):
    ys, xs = np.nonzero(mask)
    if len(xs) == 0:
        return EMPTY
    return int(xs.min()), int(ys.min()), int(xs.max()) + 1, int(ys.max()) + 1

def dice(y_true, y_pred):
    yt = y_true.astype(np.float64); yp = y_pred.astype(np.float64)
    inter = float(np.sum(yt * yp)); denom = float(np.sum(yt) + np.sum(yp))
    return 1.0 if denom == 0.0 else 2.0 * inter / denom

m1 = np.array([
    [0,0,0,0,0],
    [0,1,1,0,0],
    [0,1,0,1,0],
    [0,0,1,0,0],
    [0,0,0,0,0]])
m2 = np.zeros((5,5), dtype=np.uint8)
m3 = np.zeros((5,5), dtype=np.uint8); m3[2,3] = 1

print(mask_to_bbox(m1), dice(m1, np.zeros_like(m1)))
print(mask_to_bbox(m2), dice(m2, np.zeros_like(m2)))
print(mask_to_bbox(m3), mask_to_bbox(np.zeros_like(m3)))

Várt kimenet: (1,1,4,4)(1,1,4,4), Dice a nulla predikcióval 00; (−1,−1,−1,−1)( -1,-1,-1,-1 ), Dice 11; (3,2,4,3)(3,2,4,3), üresre (−1,−1,−1,−1)(-1,-1,-1,-1).

F4. A hiba oka: a max koordinátához nem ad hozzá 11-et, így a fél-nyílt intervallum felső határa kihagyja a legnagyobb indexű előtérpixelt. Példa: M=(1000)M=\begin{pmatrix}1&0\\0&0\end{pmatrix} esetén a hibás függvény (0,0,0,0)(0,0,0,0)-t ad, amely érvénytelen és nulla területű; a helyes eredmény (0,0,1,1)(0,0,1,1). Ha a hibás dobozból kitöltő maszkot készítünk, az nem tartalmazza a j=0j=0 vagy i=0i=0 szélső pixelt, a metszet és az IoU torzul. A javítás:

def xyxy_bounds(xs, ys):
    return (int(xs.min()), int(ys.min()),
            int(xs.max()) + 1, int(ys.max()) + 1)

és az üres eset kezelése EMPTY_BBOX visszaadással. Az ellenőrzés a F3 három tesztjével történik.

F5. Egy lehetséges terv. Kérdés: „A GlyphLab vékony vonalainak térbeli azonosításához a pixelszintű maszk vagy a befoglaló bbox ad-e a felhasználási szerződésben jobb döntéshozatalt?” Baseline: üres maszk és teljes kép mint all-foreground; lokalizációs ágon a teljes képnyi fix bbox. Split: a 38.1. Adat, split és előzetes protokoll jelenetszintű, osztályrétegzett 120/30/30; modellválasztás csak clean val-on, teszt egyszer, befagyasztva. Metrika: a maszkágra kép-szintű binary Dice, üres–üres konvencióval; a bboxágra bbox-IoU. A két ág összehasonlításakor a bboxági predikciót γ\gamma-val maszkká alakítva is kiértékeljük Dice-on, de közöljük, hogy ez a kitöltési torzítás miatt konzervatív alsó becslés a bboxágra nézve. Érvényességi korlát: a GlyphLab szintetikus, egyobjektumos, zajos grayscale adat; az eredmény nem általánosítható többobjektumos vagy természetes képekre. Az intervallumokat jelenetszintű bootstrap adhatja [S02] szerint; a döntéshez a felhasználási következményt is rögzíteni kell, nem csak a pontszámot.

39.1.8 8. Tipikus tévedés, önellenőrzés és visszahívás

Tipikus tévedés. „A bboxra váltva a régi maszkmetrika automatikusan megfelelő marad.” Ez hamis. Legyen a ground truth maszk L-alak: 4×44\times4-es kép, a felső sor és a bal oszlop 11, a többi 00; területe 77. A ground truth bbox (0,0,4,4)(0,0,4,4). Ha a modell a teljes befoglaló téglalapot adja, a bbox-IoU 11, de a maszk-Dice a valós L-maszk és a kitöltött téglalap között 2⋅77+16=1423≈0.609\frac{2\cdot7}{7+16}=\frac{14}{23}\approx0.609. A két target tehát más hibát definiál: ugyanaz az output az egyik metrikában tökéletes, a másikban gyenge. A metrikát a targettel együtt kell rögzíteni, nem a régi target után húzni.

Önellenőrzés. (1) Miért nem elég a bbox, ha a kérdés a pixelmaszk? (2) Hogyan definiáltuk az üres bboxot és miért nem (0,0,0,0)(0,0,0,0)? (3) Mit jelent a fél-nyílt [xmin,xmax)[x_{\min},x_{\max}) a pixelcella-koordinátákban? (4) Miért scene_id a split egysége? (5) Mikor egyenlő γ(β(M))\gamma(\beta(M)) és MM?

Rövid válaszok. (1) Mert a bbox elveszti a maszk alakját, lyukjait és komponenseit. (2) A szentinel (−1,−1,−1,−1)(-1,-1,-1,-1); helyi sémadöntés, mert a negatív koordináta egyetlen érvényes dobozban sem fordul elő (könnyen felismerhető), és az ilyen célokat a dobozregressziós lossból kizárjuk. A (0,0,0,0)(0,0,0,0) nulla területű, érvénytelen doboz, nem egy pixelé. (3) A cellát indexeljük, és a felső határ nyílt, így a jobb alsó cella W−1W-1, H−1H-1 indexig tart. (4) A két view közös jelenete függő megfigyelés; a képszintű split szivárgást okozna. (5) Pontosan akkor, ha a maszk téglalapot tölt ki.

Visszahívás. A 38.1. Adat, split és előzetes protokoll rögzítette a jelenetszintű osztályrétegzett splitet és a teszt egyszeri, befagyasztott használatát; a 27.1. Pixelszintű klasszifikáció és FCN a pixel-CE loss nevezőjét az érvényes pixelekre vezette le; a 35.1. Saliency és Grad-CAM Grad-CAM-vizualizációja heurisztikus lokalizáció, amely nem pótolja a tanított maszkcélt. A maszk itt rögzített bináris célja a 39.2. Encoder–decoder vagy lokalizációs fej alfejezetben a modellkimenet és a loss szerződésévé válik.

39.1.9 Rövid összefoglaló

A Project III annotációs döntése a kérdéshez köti a célt. A szemantikus maszk minden pixelhez bináris előtér/háttér címkét rendel: batch alakban Y∈{0,1}N×1×24×24Y\in\{0,1\}^{N\times1\times24\times24}. A bbox négy fél-nyílt xyxy koordináta, a maszk legkisebb befoglaló téglalapja; üres célja (−1,−1,−1,−1)(-1,-1,-1,-1), míg a maszk üres célja a nulla tömb. A lemma belátja, hogy a bboxból visszaállított maszk felső becslése az eredeti maszknak, és pontosan téglalapmaszkra egyeznek meg. A labor a metadata-bbox és a maszk konzisztenciáját ellenőrzi, baseline-ként üres és teljes előtér predikciót ad. A metrika target-specifikus: bbox-IoU és maszk-Dice nem felcserélhető.

39.1.10 Következő alkalmazás

A 39.2. Encoder–decoder vagy lokalizációs fej az itt rögzített semantic_mask targetre építi és tanítja az encoder–decoder szegmentáló hálót. A target shape, az üreseset-konvenció és a jelenetszintű split ott válik a loss, a modellkimenet és a tanítási ciklus szerződésévé.

39.1.11 Felhasznált források