23.3 Páros összehasonlítás és csoportszintű intervallum

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

A 23.2. Kalibráció és döntési küszöb egység a modell valószínűségi kimenetét és a döntési küszöböt választotta szét. Most a pontosság típusú mutatók bizonytalanságát vizsgáljuk, és ehhez a tesztminta szerkezetét tesszük explicitté. A GlyphLab test halmaza 60 képből áll, de a 60 kép csak 30 független jelenetből (scene) származik: minden jelenethez két zajos nézet (view) tartozik. Ha egy jelenet két nézete hasonlóan viselkedik, akkor a 60 kép nem hordoz 60 független bizonyítékot.

Előismeret: a konfidenciaintervallum 5.3. Mintavételi bizonytalanság és intervallum alfejezetbeli ismételt mintavételi értelmezése, a függő képek és a bootstrap egység megválasztása 5.4. Függő képek, seedek és resampling, a split scene-invariánsa 11.3. Dataset és DataLoader a split után, valamint az accuracy aggregálási konvenciói 23.1. Confusion matrix és aggregálási konvenció. Induló kérdés: ha a test accuracy 0,92, akkor a „±” értékét 60 kép vagy 30 jelenet alapján számoljuk? Durva előzetes becslésként egy binomiális jellegű standard hiba p=0,9p=0{,}9 mellett 60 független egységnél 0,9⋅0,1/60≈0,039\sqrt{0{,}9\cdot 0{,}1/60}\approx 0{,}039, 30 független egységnél 0,9⋅0,1/30≈0,055\sqrt{0{,}9\cdot 0{,}1/30}\approx 0{,}055; a tényleges eltérés attól függ, mennyire korrelálnak a nézetek a jeleneten belül.

23.3.2 2. Jelenetátlagos accuracy, páros hibák és a bizonytalanság három forrása

Legyen a tesztben NN kép, nsn_s jelenet és KK osztály. Az ii képre s(i)s(i) a jelenetazonosító, 𝒱s={i:s(i)=s}\mathcal V_s=\{i:s(i)=s\} a jelenet nézeteinek halmaza, vs=|𝒱s|v_s=|\mathcal V_s|. A GlyphLab testen N=60N=60, ns=30n_s=30, K=3K=3, vs=2v_s=2 minden ss-re, és minden osztályból ck=10c_k=10 jelenet van. A predikciók tömbje ŷ∈{0,…,K−1}N\hat y\in\{0,\ldots,K-1\}^N alakú int64, a cél yy ugyanilyen. A képszintű helyesség

Ai=𝟏{ŷi=yi}∈{0,1}. A_i=\mathbf 1\{\hat y_i=y_i\}\in\{0,1\}.

A jelenetátlagos accuracy (per-scene accuracy) a jeleneten belül átlagol, majd a jeleneteket egyenlő súllyal veszi:

As=1vs∑i∈𝒱sAi,A‾=1ns∑s=1nsAs. A_s=\frac{1}{v_s}\sum_{i\in\mathcal V_s}A_i,\qquad \bar A=\frac{1}{n_s}\sum_{s=1}^{n_s}A_s.

A kiegyensúlyozott GlyphLabon a képszintű accuracy és A‾\bar A numerikusan megegyezik, mert minden jelenetnek két képe van; a különbség nem a pontértékben, hanem abban van, hogy A‾\bar A definíciója explicitté teszi a mintavételi egységet, a jelenetet. A 23.1. Confusion matrix és aggregálási konvenció alfejezetbeli micro/macro megkülönböztetés mintájára ez jelenetszintű átlagolás, nem osztályon belüli macro-átlag.

A GlyphLab orientációs címkéje jelenettulajdonság, ezért minden jelenethez egyértelműen tartozik ysy_s osztály. Az osztály-rétegzett jelenetátlag

A‾=1K∑k=1KA‾(k),A‾(k)=1ck∑s∈𝒮kAs,𝒮k={s:ys=k} \bar A=\frac1K\sum_{k=1}^K\bar A_{(k)},\qquad \bar A_{(k)}=\frac1{c_k}\sum_{s\in\mathcal S_k}A_s,\qquad \mathcal S_k=\{s:y_s=k\}

a három osztály jelenetátlagát egyenlő súllyal veszi. Ez a generáló szerződés osztályarányát tükrözi, és nem függ attól, hogy egy véletlen bootstrap húzásban éppen hány jelenet kerül az egyes osztályokból.

Páros hibák (paired errors): ha két körülményt, például a clean és a corrupt tesztet, ugyanazon a ss jeleneten értékelünk, akkor nem két független accuracy átlagot hasonlítunk össze, hanem a jelenetenkénti különbségeket:

Δs=Ascl−Asco,Δ‾=1ns∑s=1nsΔs. \Delta_s=A_s^{\mathrm{cl}}-A_s^{\mathrm{co}}, \qquad \bar\Delta=\frac1{n_s}\sum_{s=1}^{n_s}\Delta_s.

Itt cl\mathrm{cl} a tiszta, co\mathrm{co} a korrupt teszt. Ha a jelenetek függetlenek és azonos eloszlásúak, akkor

Var(Δ‾)=1ns(σcl2+σco2−2Cov(Ascl,Asco)). \mathrm{Var}(\bar\Delta)=\frac1{n_s}\Big(\sigma_{\mathrm{cl}}^2+\sigma_{\mathrm{co}}^2-2\,\mathrm{Cov}(A_s^{\mathrm{cl}},A_s^{\mathrm{co}})\Big).

A közös jelenet nehézsége tipikusan pozitív kovarianciát ad, így a páros összehasonlítás varianciája kisebb, mint a független csoportoké. A párosítás a GlyphLab korrupt tesztjénél szerződéses: a corrupted_test.npy sorrendje a metadata test sorrendje, tehát ugyanaz a jelenet/nézet pár.

A bizonytalanságnak három, gyakran összemosott forrását különböztetjük meg:

  1. Tesztminta-mintavételi bizonytalanság: a 30 tesztjelenet csak egy minta a generáló eloszlásból; ezt célozza a jelenet-bootstrap.
  2. Tanítási seedek ingadozása: ugyanazon a rögzített teszten több training seeddel kapott accuracyk szórása. Jele A(r)A^{(r)}, r=1,…,Rr=1,\ldots,R, átlaga A‾(⋅)\bar A^{(\cdot)}, szórásnégyzete σ̂seed2=1R−1∑r(A(r)−A‾(⋅))2\hat\sigma_{\mathrm{seed}}^2=\frac1{R-1}\sum_r(A^{(r)}-\bar A^{(\cdot)})^2.
  3. Alkalmazási/domain kockázat: a modell más eloszlású bemeneten elkövetett hibája. Ezt sem a bootstrap, sem a seed-ismétlés önmagában nem becsli.

Konfidenciaintervallum alatt az [S02] szerinti ismételt mintavételi eljárást értjük: a 95%-os intervallum olyan eljárás, amely sok független tesztminta esetén körülbelül az esetek 95%-ában lefedi a rögzített paramétert. Ez nem annak a valószínűsége, hogy „a paraméter az adott intervallumban van”.

23.3.3 3. A class-stratified scene bootstrap mintavételi egysége és súlyai

A klaszter-bootstrap (cluster bootstrap) a jelenetet tekinti újramintavételi egységnek: ha egy ss jelenetet behúzunk, annak minden view-ját együtt vesszük. A rétegzett változat osztályonként külön húz, így minden replikátumban ckc_k jelenet van a kk osztályból. A következő lemma a replikátumok feltételes eloszlásának első két momentumát rögzíti. Szándékosan nem állítja a bootstrap intervallum lefedettségét; az utóbbi külön, itt nem bizonyított aszimptotikus kérdés.

Lemma (class-stratified scene bootstrap momentumai). Rögzítsük a tesztjeleneteket és a kk osztály AsA_s értékeit. A bb-edik replikátumban minden kk rétegben ckc_k darab független, egyenletes húzást végzünk a 𝒮k\mathcal S_k halmazból visszatevéssel. Legyen Nb,s(k)N_{b,s}^{(k)} az s∈𝒮ks\in\mathcal S_k jelenet húzási gyakorisága, tehát Nb(k)∼Multinomial(ck,1ck𝟏)N_b^{(k)}\sim\mathrm{Multinomial}(c_k,\tfrac1{c_k}\mathbf 1), és legyen a rétegen belüli bootstrap súly

wb,s(k)=Nb,s(k)ck. w_{b,s}^{(k)}=\frac{N_{b,s}^{(k)}}{c_k}.

A replikátum

A‾(k),b*=∑s∈𝒮kwb,s(k)As=1ck∑j=1ckAIb,j,A‾b*=1K∑k=1KA‾(k),b*. \bar A^*_{(k),b}=\sum_{s\in\mathcal S_k}w_{b,s}^{(k)}A_s =\frac1{c_k}\sum_{j=1}^{c_k}A_{I_{b,j}},\qquad \bar A^*_b=\frac1K\sum_{k=1}^K\bar A^*_{(k),b}.

Ekkor E*[A‾b*]=A‾E^*[\bar A^*_b]=\bar A, és

Var*(A‾b*)=1K2∑k=1K1ck2∑s∈𝒮k(As−A‾(k))2. \mathrm{Var}^*(\bar A^*_b) =\frac1{K^2}\sum_{k=1}^K\frac1{c_k^2}\sum_{s\in\mathcal S_k}\big(A_s-\bar A_{(k)}\big)^2.

A súlyok megőrzött tulajdonságai: wb,s(k)≥0w_{b,s}^{(k)}\ge 0, rétegenként ∑swb,s(k)=1\sum_s w_{b,s}^{(k)}=1, a rétegek összsúlya a teljes replikátumban rögzített 1/K1/K, és E*[wb,s(k)]=1/ckE^*[w_{b,s}^{(k)}]=1/c_k. A véletlen súlyok várható értéke tehát a rétegen belüli egyenletes jelenetsúly.

Bizonyítás. A *^* felső index a bootstrap húzás szerinti feltételes várható értéket/varianciát jelöli, a tesztadatok rögzítettek. A bb-edik replikátum Ib,1,…,Ib,ckI_{b,1},\ldots,I_{b,c_k} húzásai függetlenek, és mindegyik egyenletes eloszlású 𝒮k\mathcal S_k-n. Mivel A‾(k),b*\bar A^*_{(k),b} ezek átlaga,

E*[A‾(k),b*]=1ck∑j=1ckE*[AIb,j]=1ck∑j=1ck1ck∑s∈𝒮kAs=A‾(k). E^*[\bar A^*_{(k),b}] =\frac1{c_k}\sum_{j=1}^{c_k}E^*[A_{I_{b,j}}] =\frac1{c_k}\sum_{j=1}^{c_k}\frac1{c_k}\sum_{s\in\mathcal S_k}A_s =\bar A_{(k)}.

A varianciához először egyetlen húzás varianciáját számoljuk:

Var*(AIb,j)=E*[AIb,j2]−A‾(k)2=1ck∑s∈𝒮kAs2−A‾(k)2=1ck∑s∈𝒮k(As−A‾(k))2, \mathrm{Var}^*(A_{I_{b,j}}) =E^*[A_{I_{b,j}}^2]-\bar A_{(k)}^2 =\frac1{c_k}\sum_{s\in\mathcal S_k}A_s^2-\bar A_{(k)}^2 =\frac1{c_k}\sum_{s\in\mathcal S_k}\big(A_s-\bar A_{(k)}\big)^2,

ahol az utolsó egyenlőség a négyzetes eltérés szokásos kifejtése. A ckc_k darab húzás függetlensége miatt

Var*(A‾(k),b*)=1ck2∑j=1ckVar*(AIb,j)=1ck2∑s∈𝒮k(As−A‾(k))2. \mathrm{Var}^*(\bar A^*_{(k),b}) =\frac1{c_k^2}\sum_{j=1}^{c_k}\mathrm{Var}^*(A_{I_{b,j}}) =\frac1{c_k^2}\sum_{s\in\mathcal S_k}\big(A_s-\bar A_{(k)}\big)^2.

Végül a rétegenkénti húzások függetlenek, ezért a 1K\frac1K-szoros átlag varianciája a rétegvarianciák összegének 1/K21/K^2-szerese. Ezzel a lemmát beláttuk.

23.3.4 4. Kézi példa és minimális ellenőrző kód

Legyen K=2K=2 osztály, mindkettőben ck=3c_k=3 jelenet, az accuracy-értékek:

Osztály AsA_s értékek A‾(k)\bar A_{(k)}
0 1,0;1,0;0,51{,}0;\;1{,}0;\;0{,}5 5/65/6
1 0,5;0,5;0,00{,}5;\;0{,}5;\;0{,}0 1/31/3

Az osztály-rétegzett jelenetátlag A‾=(5/6+1/3)/2=7/12≈0,5833\bar A=(5/6+1/3)/2=7/12\approx 0{,}5833. A lemma varianciája: mindkét rétegben ∑s(As−A‾(k))2=1/6\sum_s(A_s-\bar A_{(k)})^2=1/6, tehát a rétegvariancia 1/(9⋅6)=1/541/(9\cdot 6)=1/54, a teljes variancia 1/(4)(1/54+1/54)=1/1081/(4)(1/54+1/54)=1/108, a szórás 1/108≈0,09621/\sqrt{108}\approx 0{,}0962.

Egy konkrét replikátum lehet: a 0. osztály húzásai (s1,s3,s2)(s_1,s_3,s_2) értékei (1,0;0,5;1,0)(1{,}0;0{,}5;1{,}0), átlaga 5/65/6; az 1. osztály húzásai (s2,s3,s1)(s_2,s_3,s_1) értékei (0,5;0,0;0,5)(0{,}5;0{,}0;0{,}5), átlaga 1/31/3; a replikátum 7/127/12. Egy másik replikátum, ahol az 1. osztályban kétszer a 0,00{,}0 értékű jelenet jön, A‾b*=(5/6+1/6)/2=1/2\bar A^*_b=(5/6+1/6)/2=1/2, mutatja a replikátumok ingadozását.

import numpy as np

rng = np.random.default_rng(20261020)
A = {0: np.array([1.0, 1.0, 0.5]),
     1: np.array([0.5, 0.5, 0.0])}
B = 2000
repl = np.empty(B, dtype=np.float64)
for b in range(B):
    total = 0.0
    for vals in A.values():
        c = vals.shape[0]
        idx = rng.integers(0, c, size=c)
        total += vals[idx].mean()
    repl[b] = total / len(A)

print(repl.mean(), repl.std(ddof=1),
      np.percentile(repl, [2.5, 97.5]))

A kézirat írásakor ezt a kódot nem futtattuk; a lemmából számított ellenőrző értékek a fentiek. A futtatáskor B=2000B=2000 mellett a replikátumátlag és -szórás az elméleti értékeket a véletlen hiba nagyságrendjén belül közelíti.

23.3.5 5. Labor: GlyphLab jelenetátlagos accuracy és intervallum

Bemenetek: lab/data/metadata.csv, a modell által előállított lab/test_preds.npy alakja (60,), int64, a metadata test sorrendjében; opcionálisan lab/test_preds_corrupt.npy ugyanebben a sorrendben. A predikciót a korábbi egységek szerint, a legjobb val-loss checkpointból állítjuk elő:

import numpy as np
import torch

model.eval()
with torch.inference_mode():
    logits = model(images_test)          # images_test: (60,1,24,24), float32, CPU
preds = logits.argmax(dim=1).cpu().numpy().astype(np.int64)
np.save("lab/test_preds.npy", preds)

A metrikaszámítás numpy-val CPU-n fut. A bootstrap seedje 20261020, a replikátumszám B=2000, a percentilis intervallum a [2.5, 97.5] percentilisekből áll. A futtatás a projekt gyökeréből:

python lab/scene_interval.py --preds lab/test_preds.npy \
  --corrupt lab/test_preds_corrupt.npy --out lab/scene_interval.json
# lab/scene_interval.py
import argparse
import json
import numpy as np
import pandas as pd

BASE = "lab"

def bootstrap_from_dict(vals_by_class, B, seed):
    rng = np.random.default_rng(seed)
    classes = list(vals_by_class)
    repl = np.empty(B, dtype=np.float64)
    for b in range(B):
        total = 0.0
        for k in classes:
            vals = np.asarray(vals_by_class[k], dtype=np.float64)
            c = vals.shape[0]
            total += vals[rng.integers(0, c, size=c)].mean()
        repl[b] = total / len(classes)
    return repl

def main():
    p = argparse.ArgumentParser()
    p.add_argument("--preds", required=True)
    p.add_argument("--corrupt", default=None)
    p.add_argument("--out", default=f"{BASE}/scene_interval.json")
    p.add_argument("--B", type=int, default=2000)
    p.add_argument("--seed", type=int, default=20261020)
    p.add_argument("--check", action="store_true")
    args = p.parse_args()

    if args.check:
        A = {0: np.array([1.0, 1.0, 0.5]),
             1: np.array([0.5, 0.5, 0.0])}
        repl = bootstrap_from_dict(A, args.B, args.seed)
        assert abs(repl.mean() - 7/12) < 0.01
        assert abs(repl.std(ddof=1) - np.sqrt(1/108)) < 0.02
        print("check ok", repl.mean(), repl.std(ddof=1))
        return

    meta = pd.read_csv(f"{BASE}/data/metadata.csv")
    test = meta[meta["split"] == "test"].sort_values("index").reset_index(drop=True)
    labels = test["label"].to_numpy(dtype=np.int64)
    preds = np.load(args.preds).astype(np.int64)
    assert preds.shape == (60,), preds.shape

    test["correct"] = (preds == labels).astype(np.float64)
    image_acc = float(test["correct"].mean())
    scene_acc = test.groupby("scene_id")["correct"].mean()
    scene_label = test.groupby("scene_id")["label"].first()
    assert np.array_equal(scene_acc.index.to_numpy(),
                          scene_label.index.to_numpy())
    scene_mean = float(scene_acc.mean())
    assert np.isclose(image_acc, scene_mean, rtol=0, atol=1e-12)

    vals = {int(k): scene_acc[scene_label == k].to_numpy(dtype=np.float64)
            for k in np.unique(scene_label)}
    repl = bootstrap_from_dict(vals, args.B, args.seed)
    lo, hi = np.percentile(repl, [2.5, 97.5])

    out = {
        "image_accuracy_mean": image_acc,
        "scene_accuracy_mean": scene_mean,
        "scene_ci_low": float(lo),
        "scene_ci_high": float(hi),
        "bootstrap_unit": "scene_id",
        "stratified_by": "label",
        "B": args.B,
        "seed": args.seed,
    }

    if args.corrupt:
        preds_c = np.load(args.corrupt).astype(np.int64)
        assert preds_c.shape == (60,), preds_c.shape
        test["correct_c"] = (preds_c == labels).astype(np.float64)
        scene_acc_c = test.groupby("scene_id")["correct_c"].mean()
        delta = scene_acc - scene_acc_c
        vals_d = {int(k): delta[scene_label == k].to_numpy(dtype=np.float64)
                  for k in np.unique(scene_label)}
        drep = bootstrap_from_dict(vals_d, args.B, args.seed)
        dlo, dhi = np.percentile(drep, [2.5, 97.5])
        out.update({
            "paired_mean_delta_clean_minus_corrupt": float(delta.mean()),
            "paired_ci_low": float(dlo),
            "paired_ci_high": float(dhi),
        })

    with open(args.out, "w", encoding="utf-8") as f:
        json.dump(out, f, indent=2)
    print(json.dumps(out, indent=2))

if __name__ == "__main__":
    main()

Ellenőrzés: a --check kapcsoló a 4. szakasz kézi példáján a lemmabeli momentumokat ellenőrzi. A fő futás a (60,) predikcióalakot és a képszintű/jelenetátlagos accuracy számszerű egyezését is teszteli. A jelen kézirat írásakor a GlyphLab-predikciós futás nem történt meg, ezért a fenti JSON-mezők jelölt szemléltetésnek minősülnek; a tényleges értékeket a hallgatói labor futása állítja elő.

Eredményértelmezés: a scene_ci_low/high a kiválasztott modell tesztminta-mintavételi bizonytalanságának közelítése a 30 jelenet mellett, a generáló GlyphLab eloszlásra vonatkoztatva. A páros paired_mean_delta_clean_minus_corrupt a corrupt bemenet hatását ugyanazon jelenet/nézet párokon méri; a hozzá tartozó intervallum a párosítás miatt nem a két accuracy-különbség két független mintából számolt varianciáját használja.

23.3.6 6. Feladatok

F1 (fogalmi, nehézség 1). Magyarázza el, miért nem mindegy, hogy a GlyphLab bootstrap egysége a kép vagy a jelenet. Adjon olyan mesterséges tesztmintát, ahol a képszintű bootstrap standard hibája 1/21/\sqrt 2-szerese a jelenet-bootstrapének. Átadandó: definíciók, feltétel és a számpélda.

F2 (levezetési, nehézség 2). A 4. szakasz kézi példáján vezesse le mindkét réteg replikátum-varianciáját, és számítsa ki a teljes varianciát 1/1081/108-ként. Átadandó: a lemma minden lépése és a numerikus ellenőrzés.

F3 (implementációs, nehézség 2). Írjon stratified_scene_bootstrap(A_by_class, B, seed) függvényt, amely a class -> float64 tömb leképezésből (B,) replikátumtömböt ad. Ellenőrizze B=2000B=2000, seed=20261020 mellett, hogy a kézi példán a replikátumátlag 7/12±0,017/12\pm 0{,}01 és a szórás 1/108±0,02\sqrt{1/108}\pm 0{,}02 tűrésen belül van. Átadandó: teljes kód, shape- és szélsőeset-kontroll.

F4 (hibakeresési, nehézség 2). Adott a következő replikátumszámítás: idx = rng.integers(0, len(scene_acc), size=len(scene_acc)); repl_b = scene_acc[idx].mean(). Nevezze meg a hibát a GlyphLab class-stratified bootstrap kontextusában, írja le a következményt a replikátumok osztályarányára, és adja meg a javítást. Átadandó: ok, következmény, javított kód, visszaellenőrzés.

F5 (kísérleti/transzfer, nehézség 3). Tervezzen protokollt, amely a clean és corrupt GlyphLab teszt accuracy-különbségét páros jelenet-bootstrap intervallummal jelenti, és emellett három training seedet is rögzít. Válassza szét a tesztminta-, seed- és domain-bizonytalanságot. Átadandó: kérdés, hipotézis, egység, baseline, metrika, erőforrás, érvényességi korlát.

23.3.7 7. Megoldások és indoklás

F1. A GlyphLab generáló folyamatában a független húzási egység a scene_id: a jelenet pozíciója, vastagsága és hossza jelenetszinten dől el, a két view csak a jelenet két zajos megfigyelése. A képszintű bootstrap ezt a klaszterszerkezetet figyelmen kívül hagyja. Legyen 30 jelenet, mindegyikben két view, és minden jelenet vagy mindkét view-val helyes (As=1A_s=1), vagy mindkettővel hibás (As=0A_s=0); legyen 27 helyes jelenet. Ekkor a jelenetátlag 0,9, a jelenetek binomiálisak, a jelenet-átlag standard hibája 0,9⋅0,1/30\sqrt{0{,}9\cdot 0{,}1/30}. A képszintű bootstrap 60 képet lát, amelyek páronként duplikált kimenetűek, és 0,9⋅0,1/60\sqrt{0{,}9\cdot 0{,}1/60} nagyságrendű szórást becsül; a kettő aránya 1/21/\sqrt 2. A feltétel az, hogy a view-k a jeleneten belül tökéletesen korrelálnak; gyengébb korrelációnál az arány kisebb, de a képszintű eljárás akkor sem becsüli a jelenet-egység szerinti mintavételt.

F2. A 0. osztályban A‾(0)=5/6\bar A_{(0)}=5/6, az eltérések 1/61/6, 1/61/6, −1/3-1/3, négyzetösszegük 1/36+1/36+1/9=1/61/36+1/36+1/9=1/6. A lemma szerint a réteg replikátum-varianciája 132⋅16=1/54\frac1{3^2}\cdot\frac16=1/54. Az 1. osztályban A‾(1)=1/3\bar A_{(1)}=1/3, az eltérések 1/61/6, 1/61/6, −1/3-1/3, így ugyanúgy 1/541/54. A teljes replikátum a két rétegátlag átlaga, ezért varianciája 122(154+154)=1108\frac1{2^2}(\frac1{54}+\frac1{54})=\frac1{108}. Numerikusan 1/108≈0,0092591/108\approx 0{,}009259, szórás 0,0962250{,}096225.

F3. A kód:

import numpy as np

def stratified_scene_bootstrap(A_by_class, B, seed):
    rng = np.random.default_rng(seed)
    classes = list(A_by_class)
    out = np.empty(B, dtype=np.float64)
    for b in range(B):
        total = 0.0
        for k in classes:
            vals = np.asarray(A_by_class[k], dtype=np.float64)
            c = vals.shape[0]
            if c == 0:
                raise ValueError("empty class layer")
            total += vals[rng.integers(0, c, size=c)].mean()
        out[b] = total / len(classes)
    return out

A = {0: np.array([1.0, 1.0, 0.5]),
     1: np.array([0.5, 0.5, 0.0])}
repl = stratified_scene_bootstrap(A, 2000, 20261020)
assert repl.shape == (2000,)
assert abs(repl.mean() - 7/12) < 0.01
assert abs(repl.std(ddof=1) - np.sqrt(1/108)) < 0.02

Szélsőeset: ha egy rétegben c=1c=1, a replikátum az egyetlen értéket adja, a rétegvariancia 0; a kód ezt helyesen kezeli, mert az egyetlen elem átlaga determinisztikus. A tolerancia a B=2000B=2000 replikátum véletlen hibájára méretezett.

F4. A hiba: a kód az összes jelenetet egyetlen halmazként húzza visszatevéssel, így nem tartja rétegenként a ck=10c_k=10 jelenetszámot. Következmény: a replikátum osztályösszetétele véletlenszerűen eltér a GlyphLab fix osztályarányától; ha az osztályaccuracyk különböznek, a replikátumok átlaga a húzott osztályarány szerint kever, és a becslő várható értéke nem a célpopuláció fix 1/31/3-1/31/3-1/31/3 súlyozását tükrözi. Javítás: minden kk rétegből külön ckc_k húzás, ahogyan a bootstrap_from_dict teszi. Visszaellenőrzés a kézi példán: a rétegzett változat átlaga 7/127/12 körül marad, a hibás változat átlaga replikátumonként a véletlen osztályaránnyal együtt mozog.

F5. Egy lehetséges protokoll. Kérdés: mekkora a corrupt bemenet jelenetátlagos accuracy-csökkenése ugyanazon modellen? Hipotézis: a corrupt zaj az egyik orientációt jobban rontja. Adat: test_preds.npy és test_preds_corrupt.npy, mindkettő a metadata test sorrendben; a modellválasztás kizárólag val loss alapján történt. Megfigyelési egység: jelenet. Baseline: a clean jelenetátlagos accuracy. Metrika: Δs\Delta_s átlaga és 95%-os páros, osztály-rétegzett jelenet-bootstrap intervallum, B=2000B=2000, seed rögzített. Seed-rész: három training seed mindegyikére kiszámítjuk a clean jelenetaccuracyt, és a seed-átlagot/min–max tartományt a teszt-CI-től külön jelentjük. Érvényességi korlát: a CI a betanított modellre és a GlyphLab generáló eljárásra feltételes; más adateloszlásra, annotációs hibára és későbbi modellfrissítésre nem ad fedést. A futáshoz kis CPU elegendő, mert csak 60 predikción végzünk újramintavételt.

23.3.8 8. Gyakori tévedés, önellenőrzés és visszahívás

Gyakori tévedés: „a training seedekből vagy a képszintű szórásból számolt intervallum az alkalmazási kockázat intervalluma.” Ellenpélda: egy determinisztikus tanítási csővezeték három seeddel pontosan ugyanazt a súlyt állítja elő; a seed-szórás nulla, a seed-intervallum [0.9, 0.9], miközben a 30 tesztjelenetből fakadó mintavételi bizonytalanság nem nulla. Megfordítva: ha a tesztjelenetek nagyon hasonlók, a bootstrap intervallum szűk, de a modell egy másik adateloszláson mégis hibázhat; a szűk CI nem domain-garancia. Az 5.4. Függő képek, seedek és resampling döntése itt is érvényes: a független mintavételi egység a scene_id, nem a kép.

Önellenőrzés: szerepel-e a jelentésben a bootstrap egység, a rétegződés, BB és a seed? Ellenőriztem-e a predikciók sorrendjét a metadata index szerint? A tesztértékelést nem használtam-e modell- vagy küszöbválasztásra? A seed-ingadozást külön közöltem-e a tesztminta-intervallumtól? A 19.3. EfficientNet és architektúraskálázás hardveres mérési szórásához hasonlóan itt is igaz: két különböző szórás összegzése előtt meg kell nevezni, melyik véletlen forrást mérjük.

23.3.9 Rövid összefoglaló

A GlyphLab tesztminta független egysége a jelenet, ezért a jelenetátlagos accuracy a scene-átlagok átlaga, és a bootstrap is scene-egységen húz. Az osztály-rétegzés minden replikátumban rögzíti a ckc_k jelenetszámot; a replikátumsúlyok rétegenként 1-re normáltak, várható értékük az egyenletes jelenetsúly. A lemma a replikátumok feltételes átlagát és varianciáját indikátorhúzásokkal vezeti le. A clean–corrupt összevetés páros, mert ugyanazon jelenet/nézet párokat használ. A bootstrap intervallum a tesztminta-mintavételi bizonytalanság közelítése, nem alkalmazási kockázat; a training seedek szórása külön dimenzió.

23.3.10 Következő alkalmazás

A 23.4. Hibaszeletek és alkalmazási határ egység a hibaszeleteket vizsgálja: ugyanezt a jelenet-egységet és rétegzett bootstrapet használjuk osztályonkénti vagy nehézségi csoportonkénti hibaelemzésre.

23.3.11 Források