22.4 Augmentációs kísérlet és domainváltozás

22.4.1 1. Cél, előismeret és induló becslés

A 22.3. Zajos és hiányos címke egységben a címkére ható zajt vizsgáltuk. Itt a bemenetre ható két műveletet választjuk szét: az augmentation policy a tanítás idején szándékosan bővíti az eloszlást, a corruption pedig a kiértékelés idején szándékosan eltolja azt. A kettő összekeverése az egyik leggyakoribb kísérleti hiba: a tanítási transformáció növelheti az invarianciát, a tesztkorruptálás viszont egy másik alkalmazási tartományt szimulál.

Előismeret: a 22.2. Címkemegőrző transzformációk címkemegőrző transzformációi, a 22.3. Zajos és hiányos címke covariate-zaj fogalma, a 21.4. Osztályarány, súlyozás és céleloszlás súlyozott empirikus kockázata, a 18.4. Reziduális blokk ablatív vizsgálata matched recipe elve és a 10.4. Tanítási lépés és tanuló MLP holdout-protokollja.

Induló kérdés. Egy modellt véletlen fényesség-augmentációval tanítunk. A validációs pontosság 0.85, a sötétebb, zajosabb corrupt teszten 0.61. Miért nem mondhatjuk egyszerűen, hogy „a modell 0.24 pontot romlott”? Mert a 0.85 és a 0.61 két különböző eloszláson mért mennyiség; a különbség csak akkor értelmezhető, ha előbb pontosan megmondjuk, melyik halmaz melyik eloszlást reprezentálja. Ezt a levezetési kötelezettséget a 3. szakasz teljesíti.

22.4.2 2. Definíciók, jelölések és alakok

Augmentation policy. Legyen 𝒯\mathcal{T} a bemeneti tér címkemegőrző transzformációinak egy halmaza, μ\mu pedig rajtuk egy valószínűségi eloszlás. A (𝒯,μ)(\mathcal{T}, \mu) pár az augmentation policy. Egy tanítási lépésben minden egyes XX bemenetre függetlenül húzunk egy t∼μt \sim \mu transzformációt, és a modell a t(X)t(X) képet látja; a YY célpont változatlan marad. A policy által definiált célfüggvény a következő módosított empirikus kockázat:

R̂aug(f)=1N∑i=1N𝔼t∼μ[ℓ(f(t(Xi)),Yi)],\hat R_{\mathrm{aug}}(f)=\frac{1}{N}\sum_{i=1}^{N}\mathbb E_{t\sim\mu}\bigl[\ell(f(t(X_i)),Y_i)\bigr],

ahol Xi∈[0,1]1×24×24X_i\in[0,1]^{1\times 24\times 24} a GlyphLab NCHW-képe, Yi∈{0,1,2}Y_i\in\{0,1,2\} az int64 osztályindex, ℓ\ell pedig a veszteség. A minibatch-training ennek Monte-Carlo-közelítését minimalizálja: egy epochon belül egy mintaelem tipikusan csak egy tt-realizációt lát.

A GlyphLab-szerződés fontos korlátja, hogy a 90°-os forgatás nem címkemegőrző: a vertical osztályt horizontalra képezi, és a diagonal osztályban csak az egyik ferde irány szerepel. Megengedhető viszont a multiplikatív fényesség-skálázás, az additív zaj, a kis eltolás és a kontrasztmódosítás, mert ezek nem változtatják meg az orientáció szemantikáját.

Seed. Az a véletlenállapot, amely a policy-transzformációk húzását és az esetleges sztochasztikus korruptálást vezérli. Az 5.4. Függő képek, seedek és resampling szerint a reprodukálhatósághoz seed, algoritmus, könyvtárverzió és adatbetöltési sorrend együttesen szükséges; egyetlen seed nem hordozhatósági ígéret.

Corruption. Kizárólag a tesztbemenetekre alkalmazott, rögzített vagy seeddel rögzített degradáló leképezés. A GlyphLab corrupted_test.npy fájlja a 60 test képet zajjal és fényességgradienssel módosítja, ugyanabban a scene/view sorrendben, mint a clean test. A corruption nem része a tanítási policynek.

Domain shift. Az a helyzet, amikor a tanítási és az alkalmazási bemeneti eloszlás különbözik: Ptrain(X)≠Pdeploy(X)P_{\mathrm{train}}(X)\neq P_{\mathrm{deploy}}(X). A corrupt teszt egy szintetikus covariate shift: a P(Y∣X)P(Y\mid X) feltételes osztályszerkezetet megtartjuk, de az XX peremeloszlást a cc leképezéssel eltoljuk.

Frozen holdout. Olyan teszt-/kiértékelési halmaz, amelyet pontosan egyszer, az összes modell- és hiperparaméter-döntés után értékelünk ki, további frissítés vagy választás nélkül. A rajta mért metrika annak az eloszlásnak a kockázatát becsli, amelyet a halmaz reprezentál.

22.4.3 3. Állítás: a clean és a corrupt teszt által reprezentált eloszlás

Legyen 𝒳⊂ℝ1×24×24\mathcal X\subset\mathbb R^{1\times 24\times 24} a képtér, 𝒴={0,1,2}\mathcal Y=\{0,1,2\} a célhalmaz. A GlyphLab generátor definiál egy 𝒟\mathcal D együttes eloszlást 𝒳×𝒴\mathcal X\times\mathcal Y fölött: osztályfeltételes jelenetparaméterekből és nézetfüggő tiszta zajból. A clean test 30 független testjelenetből áll, jelenetenként két nézettel; az egyes képek marginális törvénye így 𝒟\mathcal D. Legyen

c:𝒳→𝒳c:\mathcal X\to\mathcal X

az a rögzített, mérhető leképezés, amely a corrupted_test.npy-ben tárolt zaj- és fényességgradiens-műveletet írja le; a fájlban rögzített realizáció miatt cc determinisztikus függvénynek tekinthető. A corrupt teszt elemei X̃i=c(Xi)\tilde X_i=c(X_i), a hozzájuk tartozó célpont továbbra is YiY_i.

Állítás 3.1 (reprezentált eloszlás és torzítatlanság). Tegyük fel, hogy

Ekkor a clean teszt a 𝒟\mathcal D eloszlást, a corrupt teszt pedig a

𝒟c(A×{k})=𝒟({(x,y):c(x)∈A,y=k})\mathcal D_c(A\times\{k\})=\mathcal D\bigl(\{(x,y):c(x)\in A,\ y=k\}\bigr)

pushforward eloszlást reprezentálja. Továbbá a 0/1 hibára (vagy bármely más korlátos ℓ\ell veszteségre)

𝔼[160∑i=160ℓ(f(X̃i),Yi)]=∫𝒳×𝒴ℓ(f(c(x)),y)d𝒟(x,y).\mathbb E\left[\frac{1}{60}\sum_{i=1}^{60}\ell(f(\tilde X_i),Y_i)\right]=\int_{\mathcal X\times\mathcal Y}\ell(f(c(x)),y)\,d\mathcal D(x,y).

Bizonyítás. A cc leképezés pontonként hat az XiX_i képre, és YiY_i-t nem módosítja, ezért minden (X̃i,Yi)(\tilde X_i,Y_i) pár marginális törvénye azonos a (c(X),Y)(c(X),Y) pár törvényével, ahol (X,Y)∼𝒟(X,Y)\sim\mathcal D. A jeleneten belüli nézetfüggés a marginális törvényt nem érinti, csak az együttes eloszlás korrelációs szerkezetét. Rögzített ff mellett az empirikus átlag lineáris funkcionál, így a várható értéke az egyedi várható értékek átlaga. A törvényazonosság miatt minden tag a jobb oldali integrállal egyenlő, tehát az állítás teljesül. ▫\square

Ha a corruptálás zajkomponense nem rögzített, hanem ϵ∼ν\epsilon\sim\nu véletlen húzás, akkor a fájl egyetlen realizáció; ekkor a fenti torzítatlanság az adott ϵ\epsilon realizációra feltételes állítás. Az átlagos corrupt-tartomány fölötti kockázathoz több zajrealizációra lenne szükség, ezért a GlyphLab corrupt tesztje egyetlen kiválasztott domainpontot mér, nem pedig egy corrupt-eloszlás fölötti integrált.

A frozen holdout megsértésének formális következménye a következő kiválasztási torzítás.

Állítás 3.2 (kiválasztási torzítás, ellenpélda az érintetlen teszthez). Legyen MM jelölt modell f1,…,fMf_1,\dots,f_M, és legyen R̂m\hat R_m a mm-edik modell empirikus hibája a corrupt teszten. Minden rögzített mm-re teljesüljön 𝔼[R̂m]=Rm\mathbb E[\hat R_m]=R_m, ahol RmR_m a valódi hiba. Ha a kiválasztás m̂=argminmR̂m\hat m=\arg\min_m\hat R_m, akkor

𝔼[R̂m̂]≤minmRm.\mathbb E[\hat R_{\hat m}]\le\min_m R_m.

Bizonyítás. Minden rögzített kk-ra minmR̂m≤R̂k\min_m\hat R_m\le\hat R_k. Várható értéket véve 𝔼[minmR̂m]≤𝔼[R̂k]=Rk\mathbb E[\min_m\hat R_m]\le\mathbb E[\hat R_k]=R_k. Mivel ez minden kk-ra igaz, 𝔼[minmR̂m]≤minkRk\mathbb E[\min_m\hat R_m]\le\min_k R_k. A kiválasztott modell jelentett hibája éppen R̂m̂=minmR̂m\hat R_{\hat m}=\min_m\hat R_m. ▫\square

A pontossággal való maximalizáló kiválasztásra az analóg egyenlőtlenség 𝔼[maxmÂm]≥maxmAm\mathbb E[\max_m\hat A_m]\ge\max_m A_m: a jelentett maximum felfelé torzított a legjobb valódi kockázathoz képest is. A tétel azt mondja ki, hogy a corrupt teszten végzett adaptív választás után a jelentett szám már nem a kiválasztott modell érintetlen becslője, hanem szélsőérték-statisztika.

22.4.4 4. Kézi kis példa és ellenőrző kód

Tekintsünk egy 3×33\times 3-as, egycsatornás vertical képet:

X=[010010010],Y=0.X=\begin{bmatrix}0&1&0\\0&1&0\\0&1&0\end{bmatrix},\qquad Y=0.

A policy legyen: 12\tfrac12 valószínűséggel 0.80.8-cal szorzunk, különben azonosság. Három húzás szemléltetése:

Húzás Transzformáció Kapott osztálycímke Átlagintenzitás
1. seed fényesség ×0.8\times 0.8 0 (vertical) 3⋅0.89=0.267\frac{3\cdot 0.8}{9}=0.267
2. seed azonosság 0 (vertical) 39=0.333\frac{3}{9}=0.333
90°-os forgatás (tiltott) 000111000\begin{smallmatrix}0&0&0\\1&1&1\\0&0&0\end{smallmatrix} 1 (horizontal) 0.3330.333

A brightness-transzformáció megőrzi az orientációt, ezért a policy része lehet; a 90°-os forgatás a GlyphLab-szerződés szerint nem. A corruptáló leképezésre alkalmazzuk a [−0.2,0,0.2][-0.2,0,0.2] oszlopgradienst, zaj nélkül:

c(X)=[010.2010.2010.2],c(X)=\begin{bmatrix}0&1&0.2\\0&1&0.2\\0&1&0.2\end{bmatrix},

a negatív oszlopot a [0,1][0,1] tartomány nullára vágja. A címke még mindig 0, de a jobb oldali intenzitás megváltozott: ez a covariate shift egy apró, kézzel ellenőrizhető esete.

Minimális NumPy-ellenőrzés:

import numpy as np

def brightness_policy(x: np.ndarray, rng: np.random.Generator) -> np.ndarray:
    # x: (C,H,W) float32; csak fényesség, eltolás nélkül
    x = x.astype(np.float32)
    a = np.float32(0.8) if rng.random() < 0.5 else np.float32(1.0)
    return np.clip(a * x, 0.0, 1.0).astype(np.float32)

rng = np.random.default_rng(1)
img = np.zeros((1, 3, 3), dtype=np.float32)
img[0, :, 1] = 1.0                     # vertical bar
for step in range(5):
    out = brightness_policy(img, rng)
    col_sums = out[0].sum(axis=0)      # (3,) oszlopösszegek
    assert int(col_sums.argmax()) == 1  # az orientáció megmaradt
    print(step, out[0].tolist())

A kimenet az rng-állapottól függ; az ellenőrzött invariáns az, hogy az oszlopösszegek maximuma minden húzás után a középső oszlopban marad, tehát a vertical címke nem változik. A kódot a tanuló a saját környezetében futtathatja.

22.4.5 5. Labor: befagyasztott corrupt kiértékelés

A labor egyetlen kötelező szabálya: a corrupted_test.npy csak a befagyasztott modellen értékelhető, utólagos ráhangolás nélkül. A protokoll:

  1. Kérdés. Csökkenti-e a brightness+eltolás augmentáció a clean→corrupt pontosságcsökkenést?
  2. Adat. GlyphLab: train 240, val 60, clean test 60, corrupt test 60 kép, a corrupt sorrend a clean test sorrendjével páros.
  3. Megfigyelési egység. scene_id; a bootstrap és a páros összehasonlítás jelenetszintű.
  4. Baseline. Identity policy, minden más döntés azonos (matched recipe, 18.4. Reziduális blokk ablatív vizsgálata).
  5. Változó tényező. A policy: identity versus brightness+shift.
  6. Metrika. Pontosság, valamint jelenetszintű páros clean−corrupt különbség.
  7. Választás. Best checkpoint kizárólag a clean val loss alapján; a tesztet ezután egyszer értékeljük.

Munkakönyvtár: a lab/ könyvtárból indulva. Először az adat integritását a kezdő labor quickcheck.py ellenőrzi. A főút CPU, float32, seed 20261020:

python quickcheck.py
python augment_experiment.py --seed 20261020 --epochs 15

Az augment_experiment.py minimális, önállóan futtatható váza:

# lab/augment_experiment.py
import csv, json
from pathlib import Path
import numpy as np
import torch
import torch.nn.functional as F
from torch import nn
from torch.utils.data import DataLoader, TensorDataset

data = Path("data")
manifest = json.loads((data / "manifest.json").read_text())
assert manifest["version"] == "1.0"
X = torch.from_numpy(np.load(data / "images.npy"))            # (360,1,24,24)
y = torch.from_numpy(np.load(data / "labels.npy"))            # (360,)
X_c = torch.from_numpy(np.load(data / "corrupted_test.npy"))  # (60,1,24,24)
with open(data / "metadata.csv", newline="") as f:
    rows = list(csv.DictReader(f))
TRAIN_SLICE = slice(0, 240)
VAL_SLICE = slice(240, 300)
TEST_SLICE = slice(300, 360)

def augment(batch: torch.Tensor, rng: np.random.Generator) -> torch.Tensor:
    # batch: (N,1,H,W) float32; brightness + kis wrap-eltolás
    N = batch.shape[0]
    a = torch.from_numpy(rng.uniform(0.8, 1.2, size=(N, 1, 1, 1))
                         .astype(np.float32))
    out = batch * a
    if rng.random() < 0.5:
        dx = int(rng.integers(-1, 2))
        out = torch.roll(out, dx, dims=-1)
    return torch.clamp(out, 0.0, 1.0)

class SmallCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(1, 8, 3, padding=1), nn.ReLU(),
            nn.Conv2d(8, 16, 3, padding=1), nn.ReLU(),
            nn.AdaptiveAvgPool2d(1))
        self.head = nn.Linear(16, 3)
    def forward(self, x):
        return self.head(self.features(x).flatten(1))

def evaluate(model, Xq, yq):
    model.eval()
    with torch.no_grad():
        logits = model(Xq)
        loss = F.cross_entropy(logits, yq, reduction="mean").item()
        acc = (logits.argmax(1) == yq).float().mean().item()
    return loss, acc, logits.argmax(1)

def main(seed=20261020, epochs=15):
    torch.manual_seed(seed)
    rng = np.random.default_rng(seed)
    train_ds = TensorDataset(X[TRAIN_SLICE], y[TRAIN_SLICE])
    train_loader = DataLoader(train_ds, batch_size=32, shuffle=True)
    model = SmallCNN()
    opt = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-4)
    best_val = float("inf")
    Path("runs").mkdir(exist_ok=True)
    for epoch in range(1, epochs + 1):
        model.train()
        for xb, yb in train_loader:
            opt.zero_grad()
            loss = F.cross_entropy(model(augment(xb, rng)), yb)
            loss.backward()
            opt.step()
        val_loss, val_acc, _ = evaluate(model, X[VAL_SLICE], y[VAL_SLICE])
        if val_loss < best_val:
            best_val = val_loss
            torch.save(model.state_dict(), f"runs/best_seed{seed}.pt")
        print(f"{epoch:02d} val_loss={val_loss:.4f} val_acc={val_acc:.4f}")
    # Választás CSAK clean val alapján történt; most jön a befagyasztott értékelés.
    model.load_state_dict(torch.load(f"runs/best_seed{seed}.pt"))
    _, clean_acc, clean_pred = evaluate(model, X[TEST_SLICE], y[TEST_SLICE])
    _, corrupt_acc, corr_pred = evaluate(model, X_c, y[TEST_SLICE])
    test_scenes = [r["scene_id"] for r in rows[300:360]]
    scene_clean, scene_corr = {}, {}
    for idx, sid in enumerate(test_scenes):
        scene_clean.setdefault(sid, []).append(
            (clean_pred[idx] == y[300 + idx]).item())
        scene_corr.setdefault(sid, []).append(
            (corr_pred[idx] == y[300 + idx]).item())
    drops = [np.mean(scene_corr[sid]) - np.mean(scene_clean[sid])
             for sid in scene_clean]
    print(f"clean_test_acc={clean_acc:.4f} corrupt_test_acc={corrupt_acc:.4f}")
    print(f"median_scene_drop={np.median(drops):.4f}")

if __name__ == "__main__":
    main()

A kézirat írása nem futtatta ezt a kódot, ezért konkrét numerikus kimenetet nem állítunk mért eredményként. Lehetséges számított szemléltetés, amelyet a hallgató a saját futásával hasonlíthat össze:

01 val_loss=0.42 val_acc=0.80
...
clean_test_acc=0.83 corrupt_test_acc=0.61 median_scene_drop=-0.17
számított szemléltetés, nem mért futás

Értelmezés: a clean pontosság a 𝒟\mathcal D, a corrupt pontosság a 𝒟c\mathcal D_c kockázatának becslője. A páros jelenetszintű drop azt méri, hogy ugyanazon jeleneteken mekkora romlást okoz a corruptálás; a két view átlagolása jelenetre csökkenti a nézetfüggő zaj hatását.

22.4.6 6. Feladatok

F1 (fogalmi, nehézség 1). Definiálja az augmentation policy-t és a corruption-t, és magyarázza el, miért nem címkemegőrző a 90°-os forgatás a GlyphLabban. Adjon meg egy megengedett és egy tiltott transzformációt, a bemeneti/kimeneti alakokkal együtt. Átadandó: saját megfogalmazású definíció, egy táblázat és egy mondat arról, hogy a policy miért csak a tanítási bemenetre hat.

F2 (levezetési, nehézség 2). Tegyük fel, hogy egy befagyasztott modell corrupt tesztbeli pontossága 60 független Bernoulli-megfigyelés összege, p=0.7p=0.7 siker-valószínűséggel. Vezesse le az empirikus pontosság várható értékét és szórásnégyzetét. Ezután mondja ki és bizonyítsa be a 3.1. Állítás torzítatlansági részét determinisztikus cc mellett. Átadandó: képletek, a p=0.7p=0.7, N=60N=60 számértékek, valamint annak indoklása, hogy a jeleneten belüli pozitív korreláció miért növelheti a szórást az i.i.d. képlethez képest.

F3 (implementációs, nehézség 2). Írjon apply_augmentation(X, Y, seed, use_shift=True) függvényt, ahol X alakja (N,1,H,W) float32, Y alakja (N,) int64. A függvény brightness-skálázást és opcionális {−1,0,1}\{-1,0,1\} eltolást alkalmaz, Y-t változatlanul adja vissza, és kizárja a forgatást. Ellenőrizze, hogy két azonos seedű hívás bitre azonos tömböt ad, az alakok megmaradnak, és az értékek [0,1][0,1]-ben vannak. Átadandó: teljes kód, seed-ellenőrzés, shape-assert és egy rövid magyarázat a copy szükségességéről.

F4 (hibakeresési, nehézség 2). Az alábbi vázlat öt modellt tanít, a corrupt teszten választja ki a legjobbat, majd ugyanezt a pontosságot jelenti végső eredményként:

for i in range(5):
    train_model(seed=i)
    acc[i] = evaluate(corrupt_test).accuracy
best = argmax(acc)
print("végső corrupt teszt pontosság:", acc[best])

Azonosítsa a hibát, adja meg a 3.2. Állításból következő torzítás irányát, és írja le a javított protokollt. Átadandó: hibalokalizálás, következmény, javítás lépései.

F5 (kísérlettervezési, nehézség 3). Tervezzen GlyphLab-kísérletet annak eldöntésére, hogy a brightness+shift augmentáció csökkenti-e a clean→corrupt jelenetszintű romlást az identity baseline-hoz képest. Adja meg a matched recipe minden rögzített döntését, a seedek számát, a metrikát, a jelenetszintű bootstrap egységet és a konfidenciaintervallum értelmezését [S02] alapján. Átadandó: protokoll, baseline, változó tényező, erőforrás-korlát és az érvényességi határ. Előre megadott győztes nem szükséges.

22.4.7 7. Megoldások és indoklás

F1 megoldás. A policy (𝒯,μ)(\mathcal T,\mu): tanítási időben minden X∈[0,1]1×24×24X\in[0,1]^{1\times 24\times 24} képre t∼μt\sim\mu húzás után t(X)t(X)-et adunk a hálónak, Y∈{0,1,2}Y\in\{0,1,2\} változatlan. A corruption a tesztképekre alkalmazott rögzített leképezés, amely a kiértékelési tartományt tolja el. A 90°-os forgatás a vertical-t horizontalra cseréli, a diagonal osztályban pedig a másik ferde irányt állítja elő, amely nincs a címkekészletben; ezért nem címkemegőrző. Megengedett: tα(X)=αXt_\alpha(X)=\alpha X, α∈[0.8,1.2]\alpha\in[0.8,1.2]. Tiltott: 90°-os forgatás. A policy csak a tanítási bemenetre hat, mert a tesztidőszaki eloszlás megváltoztatása nélkül akarunk invarianciát tanítani.

F2 megoldás. Legyen Zi∼Bernoulli(p)Z_i\sim\mathrm{Bernoulli}(p) független az i=1,…,60i=1,\dots,60 képre, p̂=160∑iZi\hat p=\frac{1}{60}\sum_i Z_i. Ekkor 𝔼[p̂]=160∑ip=p=0.7\mathbb E[\hat p]=\frac{1}{60}\sum_i p=p=0.7, és a függetlenség miatt

Var(p̂)=1602∑i=160p(1−p)=p(1−p)60=0.7⋅0.360=0.0035,\operatorname{Var}(\hat p)=\frac{1}{60^2}\sum_{i=1}^{60}p(1-p)=\frac{p(1-p)}{60}=\frac{0.7\cdot0.3}{60}=0.0035,

tehát a szórás 0.0035≈0.0592\sqrt{0.0035}\approx0.0592. A 3.1. Állítás bizonyítása: minden (X̃i,Yi)(\tilde X_i,Y_i) marginális törvénye (c(X),Y)(c(X),Y) törvénye (X,Y)∼𝒟(X,Y)\sim\mathcal D mellett, mert cc pontonként hat és YiY_i-t nem érinti; rögzített ff esetén az átlag lineáris, ezért a várható érték az egyedi várható értékek átlaga, ami a jobb oldali integrállal egyenlő. A jeleneten belüli két view pozitívan korrelált, így a független mintákra érvényes p(1−p)/60p(1-p)/60 szórásnégyzet alulbecsli a tényleges szórást; az effektív mintanagyság 30 jelenet és 60 kép között van, amint azt az 5.4. Függő képek, seedek és resampling is tárgyalja.

F3 megoldás.

import numpy as np

def apply_augmentation(X, Y, seed, use_shift=True):
    X = np.asarray(X, dtype=np.float32).copy()
    Y = np.asarray(Y, dtype=np.int64)
    rng = np.random.default_rng(seed)
    N = X.shape[0]
    a = rng.uniform(0.8, 1.2, size=(N, 1, 1, 1)).astype(np.float32)
    out = X * a
    if use_shift:
        dx = rng.integers(-1, 2, size=N)
        for i in range(N):
            out[i] = np.roll(out[i], int(dx[i]), axis=-1)
    out = np.clip(out, 0.0, 1.0).astype(np.float32)
    return out, Y.copy()

X0 = np.zeros((2, 1, 4, 4), dtype=np.float32)
X0[:, 0, :, 1] = 1.0
Y0 = np.array([0, 0], dtype=np.int64)
a1, y1 = apply_augmentation(X0, Y0, 7)
a2, y2 = apply_augmentation(X0, Y0, 7)
assert a1.shape == X0.shape and y1.shape == Y0.shape
assert np.array_equal(a1, a2) and np.array_equal(y1, y2)
assert np.array_equal(y1, Y0) and np.all((a1 >= 0.0) & (a1 <= 1.0))

A copy azért szükséges, mert a np.asarray bemenetként kapott tömbön végzett helyben módosítás felülírná a hívó eredeti adatát; a feladat szerződése szerint a hívónak meg kell őriznie az eredeti X-et és Y-t.

F4 megoldás. Hiba: a corrupt tesztet modellválasztásra használják, tehát a halmaz nem frozen holdout. A 3.2. Állítás pontosság-változata szerint 𝔼[maxmÂm]≥maxmAm\mathbb E[\max_m\hat A_m]\ge\max_m A_m: a jelentett maximum felfelé torzított a legjobb valódi pontossághoz képest is, és a kiválasztott modell saját valódi kockázatának torzítatlan becslője sem. Javítás: mind az öt seed modelljét tanítsa be, mindegyikhez a clean val loss alapján válasszon best checkpointot, fagyassza be, és csak ezután értékelje egyszer a clean és a corrupt teszten; hasonlítsa össze a jelenetszintű páros különbségeket.

F5 megoldás. Egy lehetséges protokoll. Kérdés: a brightness+shift policy csökkenti-e a corrupt romlást? Baseline: identity policy. Változó: policy. Rögzített: SmallCNN architektúra, AdamW, lr-ütemezés, weight decay, batch méret, epochszám, split a manifest szerint, seedhalmaz {20261020,20261021,20261022,20261023,20261024}\{20261020, 20261021, 20261022, 20261023, 20261024\}. Minden seedhez a best checkpointot a clean val loss választja; a corrupt tesztet csak a befagyasztott modellen értékeljük. Metrika: pontosság és jelenetszintű clean−corrupt drop. A bootstrap egység scene_id, osztályonkénti rétegzéssel, 2000 újramintavétellel; az intervallum értelmezése [S02] szerint feltételes, ismételt mintavételi állítás. Érvényességi határ: egyetlen corrupt-típus, 30 testjelenet, szintetikus GlyphLab, ezért az eredmény nem általánosítható természetes képi domain shiftre.

22.4.8 8. Tipikus tévedés ellenpéldával

A leggyakoribb hiba, hogy a corrupt tesztet a „tesztkorruptálási csomag” utáni hangolásra használják, majd ugyanazt a számot jelentik végső pontosságként. Formális ellenpélda a 3.2. Állítás: ha őt jelölt valódi corrupt hibája mind Rm=0.30R_m=0.30, de a 60 képes empirikus hibák véletlen ingadozása 0.25,0.32,0.28,0.35,0.220.25,0.32,0.28,0.35,0.22, a minimum alapján kiválasztott 0.220.22 könnyen kedvezőbbnek látszik a valódi 0.300.30-nál. A jelentett minimum nem a kiválasztott modell érintetlen becslője, hanem a minta szélsőértéke. A második gyakori hiba a tanítási policy tesztidőszaki alkalmazása: ha a kiértékeléskor is véletlen fényességet húzunk, a clean teszt pontossága az augmentációs seedtől függő zajt kap, és már nem a 𝒟\mathcal D-re vonatkozó reprodukálható becslő.

22.4.9 9. Önellenőrzés és visszahívás

  1. Melyik eloszlást reprezentálja a clean, és melyiket a corrupt teszt? — A clean a 𝒟\mathcal D-t, a corrupt a cc leképezés utáni 𝒟c=c#𝒟\mathcal D_c=c_\#\mathcal D-t.
  2. Miért torzít a corrupt teszten végzett kiválasztás? — Mert 𝔼[minmR̂m]≤minmRm\mathbb E[\min_m\hat R_m]\le\min_mR_m, tehát a jelentett minimum szisztematikusan legfeljebb a legjobb valódi hibával egyenlő; pontosságra a maximum analóg módon felfelé torzít.
  3. Mi a különbség az augmentation policy és a corruption között? — A policy tanítási eloszlásbővítés címkemegőrző transzformációkkal, a corruption rögzített tesztdegradáció, amely a kiértékelési tartományt tolja el.

Visszahívás: a 22.3. Zajos és hiányos címke megkülönböztette a label noise-t a covariate zajtól; itt a corruptálás tiszta covariate shift, mert P(Y∣X)P(Y\mid X)-et megtartjuk. A 21.4. Osztályarány, súlyozás és céleloszlás súlyozott empirikus kockázata mutatta, hogy a cél eloszlása megváltoztatható; itt a bemeneti eloszlást változtatjuk. A 18.4. Reziduális blokk ablatív vizsgálata matched recipe elve biztosítja, hogy a policy-hatást egyetlen tényező izolálja, a 10.4. Tanítási lépés és tanuló MLP pedig a holdout elkülönítését alapozta meg.

22.4.10 Rövid összefoglaló

Az augmentációs policy címkemegőrző transzformációk fölötti eloszlás, amely csak a tanítási bemenetre hat, és az empirikus kockázat módosított változatát definiálja. A clean teszt a 𝒟\mathcal D cél-eloszlást, a corrupt teszt a rögzített cc leképezés utáni 𝒟c\mathcal D_c pushforward eloszlást reprezentálja; befagyasztott modellnél a corrupt halmazon számolt empirikus hiba torzítatlan becslő. A corrupt teszten végzett modellválasztás kiválasztási torzítást okoz: a jelentett minimum legfeljebb a legjobb valódi hibával egyenlő várható értékű. A labor ezért kizárólag clean validáción választ best checkpointot, és a corrupt halmazt egyszer, a befagyasztott modellen értékeli, jelenetszintű páros különbséggel.

22.4.11 Következő alkalmazás

A 23.1. Confusion matrix és aggregálási konvenció a confusion matrix és az aggregálási konvenciók kérdését tárgyalja; az itt bevezetett clean–corrupt páros szerkezet ott teszi lehetővé a csoportszintű intervallum és a páros összehasonlítás helyes értelmezését.

22.4.12 Felhasznált források