23.1 Confusion matrix és aggregálási konvenció

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

Ez az egység a modellértékelés első rétegét adja: egy véges, egycímkés osztályozási predikcióhalmazt hogyan foglalunk össze úgy, hogy a közölt szám értelmezhető és ellenőrizhető legyen. A pontosság egyetlen skalár, de elrejti, hogy a modell mely osztályokat téveszti össze, és hogy a hiba a ritka vagy a gyakori osztályt érinti-e. Az 5.4. Függő képek, seedek és resampling alfejezetben megismert függő képek miatt a confusion matrix cellái képszintű pontszámok; a klaszteres mintavételi bizonytalanságot külön, a 23.3. Páros összehasonlítás és csoportszintű intervallum alfejezetben kapcsoljuk hozzá. A 8.4. II. ellenőrző pont: korrekt tanulási állítás szerint a tesztmetrika a modellválasztás lezárása után, független halmazon értelmezhető; a 21.4. Osztályarány, súlyozás és céleloszlás osztályarányai pedig közvetlenül meghatározzák, hogy az accuracy mennyire érzékeny a többségi osztályra. A 22.4. Augmentációs kísérlet és domainváltozás clean/corrupt párosítását most egyelőre egyetlen kiértékelő halmazra szűkítjük.

Induló kérdés. Egy kiértékelő halmazban 60 kép van: 50 vertical, 6 horizontal, 4 diagonal. Egy konstans modell minden képre a vertical osztályt adja. Mennyi az accuracy, és mennyi a három osztály recalljának számtani átlaga? Tippelje meg, mielőtt a 3–4. szakasz képleteivel kiszámolná; a két szám nagyon eltérő történetet mond.

Előismeret: osztályindex-célok és a 3-osztályos GlyphLab-séma a 22.1. Adateredet, címke és csoportos split alfejezetből; a logits és argmax kapcsolata a 7.2. Softmax és többosztályos cross-entropy alfejezetből; az inference_mode alatti predikciógyűjtés a 11.1. Tensor és autograd-életciklus alfejezetből.

23.1.2 2. A confusion matrix és az egyosztályos metrikák

Legyen KK az osztályok száma, NN a kiértékelt minták száma. A predikciókat két int64 vektor tárolja:

ytrue∈{0,…,K−1}N,ypred∈{0,…,K−1}N. y_{\mathrm{true}}\in\{0,\ldots,K-1\}^N,\qquad y_{\mathrm{pred}}\in\{0,\ldots,K-1\}^N.

Ha a modell logitsot ad, akkor ypred,n=argmaxjZn,jy_{\mathrm{pred},n}=\arg\max_j Z_{n,j}. A Z∈ℝN×KZ\in\mathbb R^{N\times K} pontszámokból a metrikához már nincs szükség softmaxra; a döntési szabályt az [D07] dokumentált CrossEntropyLoss-konvenciójától függetlenül, külön kiértékelő lépésben hajtjuk végre.

Definíció (confusion matrix). A C∈ℕK×KC\in\mathbb N^{K\times K} mátrix CijC_{ij} eleme azon minták száma, amelyekre ytrue=iy_{\mathrm{true}}=i és ypred=jy_{\mathrm{pred}}=j:

Cij=∑n=1N𝟏{ytrue,n=i,ypred,n=j}. C_{ij}=\sum_{n=1}^N \mathbf 1\{y_{\mathrm{true},n}=i,\,y_{\mathrm{pred},n}=j\}.

A könyvben a sor a tényleges, az oszlop a predikált osztály. A szakirodalomban a transzponált konvenció is előfordul; ezért minden riportban a tengelyeket meg kell nevezni. A cellák darabszámok, nem valószínűségek, és

∑i=0K−1∑j=0K−1Cij=N. \sum_{i=0}^{K-1}\sum_{j=0}^{K-1}C_{ij}=N.

A sorösszeg Ni=∑jCijN_i=\sum_j C_{ij} az ii osztály supportja, az oszlopösszeg N̂j=∑iCij\widehat N_j=\sum_i C_{ij} a jj osztályba sorolt minták száma.

Definíció (accuracy). Az accuracy a helyes döntések aránya:

Acc=1N∑i=0K−1Cii=tr(C)N. \mathrm{Acc}=\frac1N\sum_{i=0}^{K-1}C_{ii}=\frac{\operatorname{tr}(C)}{N}.

Ha N=0N=0, az accuracy definiálatlan; a továbbiakban N>0N>0.

Definíció (one-vs-rest sejtszámok). Rögzített ii osztályra a többi osztályt negatívként összevonva:

TPi=Cii,FNi=∑j≠iCij,FPi=∑j≠iCji. \mathrm{TP}_i=C_{ii},\quad \mathrm{FN}_i=\sum_{j\ne i}C_{ij},\quad \mathrm{FP}_i=\sum_{j\ne i}C_{ji}.

A TNi\mathrm{TN}_i a többi cella összege:

TNi=N−TPi−FPi−FNi. \mathrm{TN}_i =N-\mathrm{TP}_i-\mathrm{FP}_i-\mathrm{FN}_i.

Innen a precision és a recall osztályonként:

Pi=TPiTPi+FPi,Ri=TPiTPi+FNi. P_i=\frac{\mathrm{TP}_i}{\mathrm{TP}_i+\mathrm{FP}_i}, \qquad R_i=\frac{\mathrm{TP}_i}{\mathrm{TP}_i+\mathrm{FN}_i}.

A pontosság azt kérdezi: a modell által ii-nek mondott minták hányada valóban ii; a recall azt: a valódi ii-k hányadát találta meg. Mindkettő akkor értelmes, ha a nevező pozitív:

A nulla nevezőre három konvenciót rögzítünk. A raise mód hibát dob. A zero mód a nulla nevezőjű értéket 00-nak veszi. Az omit mód NaN-t ad, és a makro átlagból kihagyja az érintett osztályt; a könyv laborja az omit módot használja, mert így látható marad, hogy az osztály nem volt megfigyelhető. A döntést a riportban mindig közölni kell.

Az F1 osztályonként a precision és a recall harmonikus közepe:

F1i=2PiRiPi+Ri, F1_i=\frac{2P_iR_i}{P_i+R_i},

feltéve, hogy a két érték definiált. Ha TPi+FPi=0\mathrm{TP}_i+\mathrm{FP}_i=0 és TPi+FNi=0\mathrm{TP}_i+\mathrm{FN}_i=0 egyszerre, akkor az osztály sem a célban, sem a predikcióban nem szerepel; ekkor F1i=0F1_i=0-nak definiáljuk, függetlenül a zero_division beállítástól. Ha az egyik komponens omit miatt NaN, az F1iF1_i is NaN.

23.1.3 3. Levezetés kézi többosztályos mátrixból

Tekintsük a K=3K=3, N=13N=13 esetet:

C=(510031102). C= \begin{pmatrix} 5&1&0\\ 0&3&1\\ 1&0&2 \end{pmatrix}.

Az accuracy a főátló összege osztva NN-nel:

Acc=5+3+213=1013≈0,7692. \mathrm{Acc}=\frac{5+3+2}{13}=\frac{10}{13}\approx0{,}7692.

A 0. osztályra: TP0=5\mathrm{TP}_0=5, FN0=1+0=1\mathrm{FN}_0=1+0=1, FP0=0+1=1\mathrm{FP}_0=0+1=1, tehát

P0=55+1=56,R0=55+1=56,F10=2⋅56⋅5656+56=56. P_0=\frac{5}{5+1}=\frac56,\qquad R_0=\frac{5}{5+1}=\frac56,\qquad F1_0=\frac{2\cdot\frac56\cdot\frac56}{\frac56+\frac56}=\frac56.

Az 1. osztályra: TP1=3\mathrm{TP}_1=3, FN1=0+1=1\mathrm{FN}_1=0+1=1, FP1=1+0=1\mathrm{FP}_1=1+0=1, így

P1=34,R1=34,F11=34. P_1=\frac34,\qquad R_1=\frac34,\qquad F1_1=\frac34.

A 2. osztályra: TP2=2\mathrm{TP}_2=2, FN2=1+0=1\mathrm{FN}_2=1+0=1, FP2=0+1=1\mathrm{FP}_2=0+1=1, így

P2=23,R2=23,F12=23. P_2=\frac23,\qquad R_2=\frac23,\qquad F1_2=\frac23.

A mikro összegzés a cellákat osztályok fölött összeadja:

TPmicro=∑iTPi=10,FPmicro=∑iFPi=3,FNmicro=∑iFNi=3. \mathrm{TP}_{\mathrm{micro}}=\sum_i \mathrm{TP}_i=10,\qquad \mathrm{FP}_{\mathrm{micro}}=\sum_i \mathrm{FP}_i=3,\qquad \mathrm{FN}_{\mathrm{micro}}=\sum_i \mathrm{FN}_i=3.

Egycímkés osztályozásban minden minta pontosan egy predikciót kap, ezért minden téves cella egyszer jelenik meg FP\mathrm{FP}-ként és egyszer FN\mathrm{FN}-ként; így TPmicro+FPmicro=N\mathrm{TP}_{\mathrm{micro}}+\mathrm{FP}_{\mathrm{micro}}=N és TPmicro+FNmicro=N\mathrm{TP}_{\mathrm{micro}}+\mathrm{FN}_{\mathrm{micro}}=N. Következésképpen

Pmicro=Rmicro=F1micro=1013=Acc. P_{\mathrm{micro}}=R_{\mathrm{micro}} =\mathrm{F1}_{\mathrm{micro}} =\frac{10}{13}=\mathrm{Acc}.

Állítás. Egycímkés, N>0N>0 mintás osztályozásban Pmicro=Rmicro=F1micro=AccP_{\mathrm{micro}}=R_{\mathrm{micro}}=\mathrm{F1}_{\mathrm{micro}}=\mathrm{Acc}.

Bizonyítás. A definíciókból

TPmicro+FPmicro=∑i(Cii+∑j≠iCji)=∑i,jCij=N, \mathrm{TP}_{\mathrm{micro}}+\mathrm{FP}_{\mathrm{micro}} =\sum_i\left(C_{ii}+\sum_{j\ne i}C_{ji}\right) =\sum_{i,j}C_{ij}=N,

mert a belső összeg az ii oszlop összege. Ugyanígy a recall nevezője a sorösszegek összege, ami szintén NN. A közös számláló ∑iCii\sum_i C_{ii}, tehát a három mikro érték egyaránt tr(C)/N\operatorname{tr}(C)/N. ▫\square

A makro átlagolás a fenti mátrixon:

Pmacro=13(56+34+23)=13⋅94=34=0,75, P_{\mathrm{macro}} =\frac13\left(\frac56+\frac34+\frac23\right) =\frac13\cdot\frac94=\frac34=0{,}75,

és ugyanígy Rmacro=F1macro=0,75R_{\mathrm{macro}}=F1_{\mathrm{macro}}=0{,}75. Ez a példa elfedi a makro PP, RR és F1F1 különbségét, mert minden osztályra véletlenül Pi=RiP_i=R_i. A következő mátrix megmutatja a nulla nevezőt is:

Cz=(420130110),N=12,tr(Cz)=7. C_z= \begin{pmatrix} 4&2&0\\ 1&3&0\\ 1&1&0 \end{pmatrix},\qquad N=12,\quad \operatorname{tr}(C_z)=7.

A 2. osztályt senkinek sem jósolta a modell, ezért FP2=0\mathrm{FP}_2=0 és TP2=0\mathrm{TP}_2=0; a P2P_2 nevezője nulla. A 0. osztályra:

P0=44+2=23,R0=44+2=23. P_0=\frac{4}{4+2}=\frac23,\qquad R_0=\frac{4}{4+2}=\frac23.

Az 1. osztályra:

P1=33+3=12,R1=33+1=34,F11=2⋅12⋅3412+34=3454=35=0,6. P_1=\frac{3}{3+3}=\frac12,\qquad R_1=\frac{3}{3+1}=\frac34,\qquad F1_1=\frac{2\cdot\frac12\cdot\frac34}{\frac12+\frac34} =\frac{\frac34}{\frac54}=\frac35=0{,}6.

A 2. osztályra R2=00+2=0R_2=\frac{0}{0+2}=0, de P2P_2 definiálatlan. zero_division='omit' mellett:

Pmacro=23+122=762=712≈0,5833, P_{\mathrm{macro}}=\frac{\frac23+\frac12}{2} =\frac{\frac76}{2}=\frac{7}{12}\approx0{,}5833,

míg

Rmacro=23+34+03=17123=1736≈0,4722. R_{\mathrm{macro}}=\frac{\frac23+\frac34+0}{3} =\frac{\frac{17}{12}}{3}=\frac{17}{36}\approx0{,}4722.

A makro F1F1 az érvényes osztályonkénti F1F1-ek átlaga:

F1macro=23+352=19152=1930≈0,6333. F1_{\mathrm{macro}}=\frac{\frac23+\frac35}{2} =\frac{\frac{19}{15}}{2}=\frac{19}{30}\approx0{,}6333.

A zero_division='zero' választás esetén P2=0P_2=0, F12=0F1_2=0, ezért

Pmacro(0)=23+12+03=718≈0,3889,F1macro(0)=23+35+03=1945≈0,4222. P_{\mathrm{macro}}^{(0)} =\frac{\frac23+\frac12+0}{3} =\frac{7}{18}\approx0{,}3889, \qquad F1_{\mathrm{macro}}^{(0)} =\frac{\frac23+\frac35+0}{3} =\frac{19}{45}\approx0{,}4222.

A mikro érték mindkét konvencióban

Acc=Pmicro=Rmicro=F1micro=712≈0,5833. \mathrm{Acc}=P_{\mathrm{micro}}=R_{\mathrm{micro}} =\mathrm{F1}_{\mathrm{micro}} =\frac{7}{12}\approx0{,}5833.

Ez a három szám egyszerre mutatja a nulla nevező hatását és a mikro/makro eltérést: ugyanazon a predikciós halmazon a makro F1F1 omit módban 0,63330{,}6333, zero módban 0,42220{,}4222, a mikro F1F1 pedig 0,58330{,}5833. A „0,63-as F1” és a „0,58-as F1” állítás tehát nem feltétlenül ellentmondás.

23.1.4 4. Micro/macro aggregálás és class prevalence

A mikro összegzés a mátrix összes nem átlós celláját egyenlően kezeli, ezért a gyakori osztályok hibái nagyobb darabszámot kapnak. A makro összegzés minden osztályt egy szavazatként kezel, ezért a ritka osztály hibája ugyanannyit nyom, mint a gyakorié. Egyik sem „helyesebb” általánosságban: a jelentéshez a felhasználási szerződésből kell megmondani, hogy egy tévesztés költsége darabszám-arányos-e.

Definíció (class prevalence). Az ii osztály prevalenciája a mintában

pi=NiN,∑ipi=1. p_i=\frac{N_i}{N},\qquad \sum_i p_i=1.

A prevalence a cél eloszlásának empirikus aránya, nem a modell tulajdonsága. A FP\mathrm{FP}, FN\mathrm{FN} definíciójából Ni=TPi+FNiN_i=\mathrm{TP}_i+\mathrm{FN}_i, ezért

Acc=∑iTPiN=∑i:Ni>0piTPiNi=∑i:Ni>0piRi. \mathrm{Acc} =\sum_i \frac{\mathrm{TP}_i}{N} =\sum_{i:N_i>0} p_i\frac{\mathrm{TP}_i}{N_i} =\sum_{i:N_i>0} p_i R_i.

Az accuracy tehát a recall prevalence-súlyozott átlaga. Ez a levezetés magyarázza az induló kérdést: a konstans vertical modellnél R0=1R_0=1, R1=R2=0R_1=R_2=0, a prevalenciák 50/60,6/60,4/6050/60,6/60,4/60, így

Acc=5060⋅1+660⋅0+460⋅0=56≈0,8333, \mathrm{Acc} =\frac{50}{60}\cdot 1+\frac{6}{60}\cdot 0+\frac{4}{60}\cdot 0 =\frac56\approx0{,}8333,

míg a makro recall

Rmacro=1+0+03=13≈0,3333. R_{\mathrm{macro}}=\frac{1+0+0}{3}=\frac13\approx0{,}3333.

Ugyanaz a modell tehát „83% pontosságú” és „33% makro recallú” egyszerre; a két szám nem ugyanazt a kérdést válaszolja meg.

A makro F1F1 definíciója a per-class F1iF1_i értékek átlaga. Nem a makro PP és makro RR harmonikus közepe. A CzC_z példán omit módban:

2PmacroRmacroPmacro+Rmacro=2⋅712⋅1736712+1736≈0,5219≠0,6333=F1macro. \frac{2P_{\mathrm{macro}}R_{\mathrm{macro}}}{P_{\mathrm{macro}}+R_{\mathrm{macro}}} =\frac{2\cdot\frac{7}{12}\cdot\frac{17}{36}}{\frac{7}{12}+\frac{17}{36}} \approx0{,}5219 \ne 0{,}6333=F1_{\mathrm{macro}}.

A két képlet közötti különbség akkor nagy, ha az osztályonkénti PiP_i és RiR_i párok eltérő irányban szórnak.

23.1.5 5. Labor: kézi minta és futó CNN-riport ellenőrzése

Cél. Egy saját NumPy-függvény számítsa ki a confusion matrixot és a metrikákat, és ellenőrizze a CzC_z kézi példát; majd a tanuló saját CNN-jének kiértékelő riportját értelmezze.

Input. A toy mód beépített tömböket használ. A riport mód bemenete:

A riportot a modell kiértékelésekor így menthetjük:

# a modell saját seed-protokollja után, eval módban:
model.eval()
with torch.inference_mode():
    logits_list, index_list = [], []
    for x, idx in eval_loader:
        logits_list.append(model(x))
        index_list.append(idx)
z = torch.cat(logits_list, dim=0).cpu().numpy().astype(np.float32)
idx = torch.cat(index_list, dim=0).cpu().numpy().astype(np.int64)
np.savez_compressed("my_run/eval_preds.npz", indices=idx, logits=z)

A metrika számítása determinisztikus, ezért véletlen seedre nincs szükség. A bemenő logits float32, a belső osztások float64-ben futnak; az eszköz NumPy-CPU.

Kód. Munkakönyvtár lab/, fájl metrics_check.py:

#!/usr/bin/env python3
# n089 labor: confusion matrix és aggregációs konvenciók.
# Toy:  python metrics_check.py --mode toy
# Riport: python metrics_check.py --mode report --report my_run/eval_preds.npz
import argparse
import numpy as np

def confusion_matrix(y_true, y_pred, num_classes=None):
    y_true = np.asarray(y_true, dtype=np.int64)
    y_pred = np.asarray(y_pred, dtype=np.int64)
    if y_true.shape != y_pred.shape:
        raise ValueError("y_true es y_pred alakja elter")
    if num_classes is None:
        num_classes = int(max(y_true.max(initial=0),
                              y_pred.max(initial=0))) + 1
    K = num_classes
    if K <= 0:
        raise ValueError("num_classes pozitiv kell legyen")
    if y_true.min(initial=0) < 0 or y_pred.min(initial=0) < 0 or \
       y_true.max(initial=0) >= K or y_pred.max(initial=0) >= K:
        raise ValueError("cimke kivul esik a [0, K) tartomanyon")
    C = np.zeros((K, K), dtype=np.int64)
    np.add.at(C, (y_true, y_pred), 1)
    return C

def precision_recall_from_confusion(C, zero_division="omit"):
    if zero_division not in {"raise", "zero", "omit"}:
        raise ValueError("ismeretlen zero_division mod: " + str(zero_division))
    K = C.shape[0]
    tp = np.diag(C).astype(np.float64)
    fp = C.sum(axis=0) - tp
    fn = C.sum(axis=1) - tp

    def safe(num, den):
        if zero_division == "raise":
            if np.any(den == 0):
                raise ZeroDivisionError("nulla nevezo a precision/recall-ban")
            return num / den
        fill = 0.0 if zero_division == "zero" else np.nan
        out = np.full(K, fill, dtype=np.float64)
        np.divide(num, den, out=out, where=den > 0)
        return out

    P = safe(tp, tp + fp)
    R = safe(tp, tp + fn)
    denom = P + R
    both_zero = (P == 0.0) & (R == 0.0)
    valid = np.isfinite(P) & np.isfinite(R)
    F1 = np.full(K, np.nan, dtype=np.float64)
    mask = valid & (denom > 0)
    F1[mask] = 2.0 * P[mask] * R[mask] / denom[mask]
    F1[both_zero] = 0.0
    return P, R, F1

def core_metrics(C, zero_division="omit"):
    N = int(C.sum())
    if N == 0:
        raise ValueError("ures matrixra a metrika definialatlan")
    P, R, F1 = precision_recall_from_confusion(C, zero_division)
    acc = float(np.diag(C).sum()) / N
    return {
        "accuracy": acc,
        "micro_precision": acc,
        "micro_recall": acc,
        "micro_f1": acc,
        "macro_precision": float(np.nanmean(P)),
        "macro_recall": float(np.nanmean(R)),
        "macro_f1": float(np.nanmean(F1)),
        "prevalence": C.sum(axis=1) / N,
        "per_class": {"precision": P, "recall": R, "f1": F1},
    }

TOY_TRUE = np.array([0]*6 + [1]*4 + [2]*2, dtype=np.int64)
TOY_PRED = np.array(
    [0,0,0,0,1,1,   # 0. sor: 4 db 0, 2 db 1
     0,1,1,1,       # 1. sor: 1 db 0, 3 db 1
     0,1],          # 2. sor: 1 db 0, 1 db 1
    dtype=np.int64)
EXPECTED = np.array([[4,2,0],
                     [1,3,0],
                     [1,1,0]], dtype=np.int64)

def run_toy():
    C = confusion_matrix(TOY_TRUE, TOY_PRED, num_classes=3)
    assert np.array_equal(C, EXPECTED)
    res = core_metrics(C, zero_division="omit")
    assert np.isclose(res["accuracy"], 7/12)
    assert np.isclose(res["micro_f1"], 7/12)
    assert np.isclose(res["macro_f1"], 19/30)
    assert np.isclose(res["per_class"]["precision"][2], np.nan, equal_nan=True)
    print("toy ellenorzes OK")
    for k in ["accuracy","micro_f1","macro_precision",
              "macro_recall","macro_f1"]:
        print(f"{k:18s} {res[k]:.4f}")

def run_report(path):
    data = np.load("data/labels.npy")
    rpt = np.load(path)
    indices = rpt["indices"].astype(np.int64)
    logits = rpt["logits"].astype(np.float32)
    if logits.ndim != 2 or logits.shape[1] != 3:
        raise ValueError("GlyphLab-riport: logits alak (N,3) kell")
    if indices.shape != (logits.shape[0],):
        raise ValueError("indices es logits hossza elter")
    y_true = data[indices]
    y_pred = np.argmax(logits, axis=1).astype(np.int64)
    C = confusion_matrix(y_true, y_pred, num_classes=3)
    assert C.sum() == len(indices)
    res = core_metrics(C, zero_division="omit")
    print("sorosszeg (tamogatas):", C.sum(axis=1))
    print("oszloposszeg (predikcio):", C.sum(axis=0))
    print("normalizalt sorok (recall-profil):")
    for i, row in enumerate(C):
        if row.sum() > 0:
            print(i, np.round(row / row.sum(), 3))
    for k in ["accuracy","micro_f1","macro_precision",
              "macro_recall","macro_f1"]:
        print(f"{k:18s} {res[k]:.4f}")

if __name__ == "__main__":
    p = argparse.ArgumentParser()
    p.add_argument("--mode", choices=["toy","report"], required=True)
    p.add_argument("--report", default="")
    a = p.parse_args()
    if a.mode == "toy":
        run_toy()
    else:
        run_report(a.report)

Futtatás és ellenőrzés.

cd lab
python metrics_check.py --mode toy

A toy mód várt kimenete magából a CzC_z levezetéséből számított, nem mért futás: accuracy 0,58330{,}5833, micro F1 0,58330{,}5833, macro precision 0,58330{,}5833, macro recall 0,47220{,}4722, macro F1 0,63330{,}6333. A második osztály precisionje NaN. A riport mód:

python metrics_check.py --mode report --report my_run/eval_preds.npz

A riport mód ellenőrzi a shape- és indexszerződést, majd kiírja a mátrixot, a támogatást és a normalizált sorokat. A sorösszegek az igaz eloszlást, az oszlopösszegek a predikciós eloszlást mutatják; a normalizált sor a recall, nem pedig a precision. A gyakori hiba az, ha a mátrixot a sklearn-stílusú normalize='true' nélkül közöljük, és az olvasó a nagy cellaszámot automatikusan jó osztályozási aránynak hiszi.

23.1.6 6. Feladatok F1–F5

F1 (fogalmi, nehézség 1). Magyarázza el, miért nem cserélhető fel a micro és a macro átlagolás. Adjon három osztályból álló, kis y_true/y_pred példát, amelyben az accuracy magas, de a macro recall alacsony. Elvárt: definíciók, a példa tömbjei, a két számérték.

F2 (levezetési, nehézség 2). A

C=(610040112) C= \begin{pmatrix} 6&1&0\\ 0&4&0\\ 1&1&2 \end{pmatrix}

mátrixból számítsa ki az accuracyt, az osztályonkénti precision/recall/F1 értékeket, a micro és a macro összegzést. Használja a zero_division='omit' konvenciót, és jelölje, ha valamelyik nevező nulla lenne. Elvárt: minden közbülső sejtszám és a három makro érték.

F3 (implementációs, nehézség 2). Implementálja a precision_recall_from_confusion függvényt a 5. szakasz specifikációja szerint, a három zero_division móddal. Tesztelje a CzC_z mátrixon omit és zero módban is. Elvárt: futtatható függvény, a két mód makro F1-értéke, és egy negatív teszt, amely raise módban nulla nevezőre hibát ad.

F4 (hibakeresési, nehézség 2). Adott a következő hibás kód:

tp = np.diag(C).astype(np.float64)
fp = C.sum(axis=1) - tp   # sor szerint
fn = C.sum(axis=0) - tp   # oszlop szerint
P = tp/(tp+fp)
R = tp/(tp+fn)
macro_p = P.mean()
macro_r = R.mean()
macro_f1 = 2*macro_p*macro_r/(macro_p+macro_r)

Hol a hiba? Számítsa ki a CzC_z mátrixon, zero_division='zero' mellett, a hibás és a javított makro F1F1 értéket. Elvárt: a hiba oka, következménye, a javított tengelyek és a numerikus ellenőrzés.

F5 (kísérleti/transzfer, nehézség 3). A GlyphLab kiértékelő halmaza osztályonként kiegyenlített, de a telepítési környezetben a feltételezett prevalencia p′=(0,6;0,3;0,1)p'=(0{,}6;0{,}3;0{,}1) a vertical, horizontal, diagonal célra. Tervezze meg, hogyan hasonlítaná össze a mikro és a makro metrika érzékenységét erre a prevalence-változásra anélkül, hogy a tesztet újra felhasználná modellválasztásra. Elvárt: protokoll, a mátrixból számított átsúlyozási képlet, legalább két értelmezési korlát.

23.1.7 7. Megoldások és indoklás

F1. A micro átlagolás a mátrix celláit osztályok fölött összegzi; a gyakori osztály darabszáma nagyobb súlyt kap. A macro átlagolás minden osztály per-class értékét azonos 1/K1/K súllyal veszi, függetlenül a supporttól. Példa: ytrue=(0,0,0,0,0,1,1,1,2)y_{\mathrm{true}}=(0,0,0,0,0,1,1,1,2) és ypred=(0,0,0,0,0,0,0,0,0)y_{\mathrm{pred}}=(0,0,0,0,0,0,0,0,0). Ekkor Acc=5/9≈0,556\mathrm{Acc}=5/9\approx0{,}556, és R=(1,0,0)R=(1,0,0), így Rmacro=1/3≈0,333R_{\mathrm{macro}}=1/3\approx0{,}333. Ez az accuracy–macro recall hézag a ritka osztályok teljes eltévesztéséből adódik.

F2. A mátrix sorösszegei: 7,4,47,4,4; oszlopösszegei: 7,6,27,6,2; N=15N=15, tr(C)=12\operatorname{tr}(C)=12, tehát

Acc=1215=0,8. \mathrm{Acc}=\frac{12}{15}=0{,}8.

A 0. osztályra TP=6\mathrm{TP}=6, FP=7−6=1\mathrm{FP}=7-6=1, FN=7−6=1\mathrm{FN}=7-6=1; ezért P0=R0=6/7≈0,8571P_0=R_0=6/7\approx0{,}8571, F10=6/7F1_0=6/7. Az 1. osztályra TP=4\mathrm{TP}=4, FP=6−4=2\mathrm{FP}=6-4=2, FN=4−4=0\mathrm{FN}=4-4=0; ezért P1=4/6≈0,6667P_1=4/6\approx0{,}6667, R1=1R_1=1, F11=0,8F1_1=0{,}8. A 2. osztályra TP=2\mathrm{TP}=2, FP=2−2=0\mathrm{FP}=2-2=0, FN=4−2=2\mathrm{FN}=4-2=2; ezért P2=1P_2=1, R2=0,5R_2=0{,}5, F12=2/3≈0,6667F1_2=2/3\approx0{,}6667. Nulla nevező nem lép fel. A mikro érték Acc=0,8\mathrm{Acc}=0{,}8. A makro értékek:

Pmacro=0,8571+0,6667+13≈0,8413, P_{\mathrm{macro}}=\frac{0{,}8571+0{,}6667+1}{3} \approx0{,}8413,

Rmacro=0,8571+1+0,53≈0,7857, R_{\mathrm{macro}}=\frac{0{,}8571+1+0{,}5}{3} \approx0{,}7857,

F1macro=0,8571+0,8+0,66673≈0,7746. F1_{\mathrm{macro}}=\frac{0{,}8571+0{,}8+0{,}6667}{3} \approx0{,}7746.

Szélsőeset-ellenőrzés: a mátrix összege 1515, a diagonál 1212, a FP2=0\mathrm{FP}_2=0 miatt a 2. osztály precisionje értelmesen 11; a FN1=0\mathrm{FN}_1=0 miatt az 1. osztály recallja 11.

F3. A labor metrics_check.py függvénye a megoldás. zero_division='omit' esetén F1macro=19/30≈0,6333F1_{\mathrm{macro}}=19/30\approx0{,}6333; zero_division='zero' esetén 19/45≈0,422219/45\approx0{,}4222. A raise negatív teszt:

C = np.array([[4,2,0],[1,3,0],[1,1,0]], dtype=np.int64)
try:
    precision_recall_from_confusion(C, zero_division="raise")
except ZeroDivisionError:
    print("raise mod helyesen hibazik")
else:
    raise AssertionError("nem dobott ZeroDivisionError-t")

A shape- és tartományellenőrzés a confusion_matrix-ban külön hibát ad, ha a címke kívül esik a [0,K)[0,K) intervallumon.

F4. A hiba a tengelyek felcserélése: a fp-t a sorösszegből, az fn-t az oszlopösszegből számolja. A C_z mátrixon a helyes P=(2/3,1/2,0)P=(2/3,1/2,0) és R=(2/3,3/4,0)R=(2/3,3/4,0); a hibás kód Pbad=RP_{\mathrm{bad}}=R és Rbad=PR_{\mathrm{bad}}=P értéket ad. A hibás makro átlagok ezért

Pmacro,bad=0,6667+0,75+03≈0,4722,Rmacro,bad=0,6667+0,5+03≈0,3889. P_{\mathrm{macro,bad}}=\frac{0{,}6667+0{,}75+0}{3}\approx0{,}4722, \qquad R_{\mathrm{macro,bad}}=\frac{0{,}6667+0{,}5+0}{3}\approx0{,}3889.

A hibás harmonikus képlet ezekből

F1bad=2⋅0,4722⋅0,38890,4722+0,3889≈0,4266. F1_{\mathrm{bad}} =\frac{2\cdot0{,}4722\cdot0{,}3889}{0{,}4722+0{,}3889} \approx0{,}4266.

A helyes makro F1F1 zero_division='zero' mellett 0,42220{,}4222. A felcserélt tengelyek önmagukban sem adnak helyes per-class jelentést, és a makro F1F1 harmonikus képlete további eltérést okoz.

F5. Egy lehetséges protokoll:

  1. A befagyasztott modellt egyszer kiértékeljük a rögzített tesztre; a confusion matrixot és a tesztet a további modellválasztásban nem használjuk.
  2. Kiszámítjuk RiR_i-t a mátrixból, majd az új prevalenciával az átsúlyozott accuracyt: Accp′=∑ip′iRi\mathrm{Acc}_{p'}=\sum_i p'_i R_i. Ugyanezt elvégezzük a jelenlegi pip_i prevalenciával is.
  3. Összehasonlítjuk a mikro F1F1-et, a makro F1F1-et és az Accp′\mathrm{Acc}_{p'} különbségét, és a bizonytalanságot jelenetalapú bootstrap segítségével (a scene_id a mintavételi egység, az 5.4. Függő képek, seedek és resampling szerint) számítjuk.
  4. Lehetséges baseline: a prevalencia szerinti többségi konstans modell új accuracyja 0,60{,}6.

Korlátok: (a) az átsúlyozás csak label shiftet modellez, azt feltételezi, hogy P(y|x)P(y|x) változatlan; ha a bemeneti eloszlás is eltolódik, a predikciós mátrix már nem reprezentálja a célkörnyezetet. (b) A metrikakülönbség nem bizonyítja, hogy a modell a telepített környezetben jobb vagy rosszabb; az igazi tesztet a célkörnyezetből, a 22.1. Adateredet, címke és csoportos split adatszerződése szerint kell gyűjteni.

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

Gyakori tévedés. Ugyanaz a metrikanevű szám eltérő averaging mellett nem összehasonlítható. A CzC_z példán a micro F1=0,5833F1=0{,}5833, a makro F1F1 omit módban 0,63330{,}6333, zero módban 0,42220{,}4222. Ha egy riport csak „F1 = 0,63” közlést tartalmaz, akkor sem a képlet, sem a nulla nevező konvenció nem reprodukálható. A közlésnek mindig tartalmaznia kell: KK, a mikró vagy makró szót, a zero_division módot és a confusion matrixot vagy a per-class táblázatot. Hasonlóan hibás a makro F1F1-et a makro PP és makro RR harmonikus közepeként számolni; a 4. szakasz ellenpéldája 0,52190{,}5219 és 0,63330{,}6333.

Önellenőrzés. A mátrix sorösszege az igaz support, oszlopösszege a predikciók száma; a főátló összege NN-nel osztva az accuracy. A recall a soron belüli találati arány, a precision az oszlopon belüli helyesség. Ha a mátrixot transzponálja, a precision és a recall felcserélődik; ezért az ábra tengelyfelirata és a kód tengelye ugyanazt kell jelentse. A mikro P=R=F1P=R=F1 egyenlőség csak egycímkés osztályozásra érvényes; multilabel esetben külön összegzés kell.

Visszahívás. Az 5.4. Függő képek, seedek és resampling alfejezetben bevezetett függő képek most is érvényesek: a confusion matrix cellái képszintű darabszámok, de az intervallumbecsléshez a scene_id klaszter szerint kell resamplingolni; ez a 23.3. Páros összehasonlítás és csoportszintű intervallum témája. A 8.4. II. ellenőrző pont: korrekt tanulási állítás tanulási szerződése szerint a metrikát a modellválasztás után, független halmazon számítjuk; a 5. szakasz riportja sem validálhat újra ugyanazon a halmazon, amelyen a checkpointot választottuk. A 11.1. Tensor és autograd-életciklus inference_mode-ja biztosítja, hogy a riportkészítés ne építsen autograd-gráfot, és ne módosítsa a modell állapotát.

23.1.9 Rövid összefoglaló

A confusion matrix CijC_{ij} darabszámokkal rögzíti, hányszor mondtunk jj-t, amikor az igazság ii. Az accuracy a főátló összege osztva NN-nel, és egyenlő a prevalence-súlyozott recall-lal. A one-vs-rest precision és recall osztályonként definiálja a találati és a lefedettségi hibát; nulla nevezőre a raise, zero és omit konvenciók egyikét kell közölni. A mikro összegzés egycímkés esetben P=R=F1=AccP=R=F1=\mathrm{Acc}; a makro minden osztályt egyenlően súlyoz, és a makro F1F1 a per-class F1F1-ek átlaga, nem a makro PP és RR harmonikus közepe. Kiegyensúlyozatlan adaton a mikro és makro szám lényegesen eltérhet, ezért az összehasonlításhoz a teljes konvenciót rögzíteni kell.

23.1.10 Következő alkalmazás és források

A 23.2. Kalibráció és döntési küszöb alfejezetben a softmax-kimenet, a döntési küszöb és a kalibráció következik: ott a confusion matrix celláinak folytonos küszöbtől való függése adja a precision–recall trade-off elemzését. A 23.3. Páros összehasonlítás és csoportszintű intervallum ehhez csoportszintű konfidenciaintervallumot kapcsol.