Werkplaats / De werkplaats
In jouw eigen tempo
← Klassieke machine learning
MODULE 02 · LES 08

Betekent 80% ook ongeveer acht van tien?

Beoordeel voorspelde kansen met de Brier-score, kalibratiegroepen en een gewogen afwijking.

55 min3 checkpoints
DIT GA JE ONTDEKKEN
  • Je meet de kwaliteit van kansvoorspellingen zonder ze eerst tot labels te reduceren.
  • Je maakt een kalibratietabel en benoemt de beperkingen van bin-afhankelijke samenvattingen.

Een model noemt honderd berichten “80% kans op spam”. Als maar vijftig ervan spam blijken, kan het toch vaak het juiste label kiezen, maar zijn kansgetallen zijn te zeker. Kalibratie (calibration) vraagt of voorspelde kansen overeenkomen met de waargenomen frequenties in vergelijkbare groepen.

Eén bericht kan niet voor 80% spam blijken: het label is 0 of 1. Je beoordeelt daarom groepen, met genoeg voorbeelden om toeval te beperken. Gebruik gegevens waarop het kansmodel niet is getraind.

1. Meet een kansfout vóór de drempel

Neem drie voorspelde kansen [0.1,0.2,0.9] en werkelijke labels [0,0,1]. Kans min label geeft [0.1,0.2,−0.1]. De kwadraten zijn 0.01,0.04 en 0.01; gemiddeld is dat 0.02.

Brier=1N∑(pi−yi)2

De binaire Brier-score lijkt op MSE, maar p is een kans en y een label 0/1. N is het aantal voorbeelden; i wijst één paar aan. Lager is beter, en perfecte kansvoorspellingen krijgen score nul.

probs = [0.1, 0.2, 0.9]
ys = [0, 0, 1]
totaal = 0
for i in range(len(probs)):
    fout = probs[i] - ys[i]
    totaal = totaal + fout ** 2
print(totaal / len(probs))  # ongeveer 0.02

Maak brier(probs,ys) met ValueError bij lege of ongelijk lange lijsten, kansen buiten [0,1] en andere labels dan 0/1. Overal 0.5 voorspellen geeft altijd 0.25 bij binaire labels. De score meet zowel kalibratie als onderscheid tussen gevallen; het is geen zuivere kalibratiemeter.

01
CHECKPOINTMeet kansfouten zonder drempel

2. Leg vergelijkbare voorspellingen bij elkaar

Verdeel het kansbereik in k gelijke intervallen (bins). Met k = 2 zijn dat [0,0.5) en [0.5,1]: de ondergrens hoort erbij, de bovengrens niet, behalve bij het laatste interval. Kans 0.5 komt dus rechts en kans 1 blijft in de laatste groep.

In onze eerste groep liggen kansen 0.1 en 0.2: gemiddeld 0.15, met nul positieve labels. De tweede bevat alleen 0.9: gemiddelde 0.9 en positieve fractie 1. Die ene waarneming is weinig bewijs; de tabel moet ook de aantallen tonen.

IndexAantalGem. kansPositief
020.150
110.901

Zoek de eerste bovengrens waar de kans strikt onder ligt. De grens van vak j is (j+1)/k; bij een exacte grens schuif je door naar rechts. Vergelijken met de randen voorkomt dat afronding bij vermenigvuldigen een grenswaarde links indeelt.

k = 2
aantallen = [0] * k
som_p = [0.0] * k
som_y = [0] * k
for i in range(len(probs)):
    vak = k - 1
    for j in range(k):
        grens = (j + 1) / k
        if probs[i] < grens:
            vak = j
            break
    aantallen[vak] = aantallen[vak] + 1
    som_p[vak] = som_p[vak] + probs[i]
    som_y[vak] = som_y[vak] + ys[i]
print(aantallen)  # [2, 1]

[0] * k maakt k tellers. break verlaat alleen de binnenste lus zodra het juiste vak is gevonden. Beginnen bij k−1 zorgt ervoor dat ook p=1 een geldig vak heeft.

tabel = []
for j in range(k):
    n = aantallen[j]
    if n > 0:
        p = som_p[j] / n
        y = som_y[j] / n
        tabel.append((j, n, p, y))
print(tabel)
# ongeveer [(0, 2, .15, 0), (1, 1, .9, 1)]

Maak kalibratietabel(probs,ys,k) als oplopende lijst van (index,aantal,gemiddelde_kans,fractie_positief). Hergebruik de invoercontrole van brier en weiger k die geen positief geheel getal is. Sla lege groepen over: geen waarnemingen betekent geen gemeten frequentie, dus niet frequentie nul.

02
CHECKPOINTVergelijk voorspelling en frequentie per groep

3. Weeg de afwijking met het aantal voorbeelden

In de eerste groep wijken kans en frequentie 0.15 af; in de tweede 0.10. De verwachte kalibratiefout (expected calibration error, ECE) in deze binaire oefening weegt iedere absolute groepsafwijking met haar aandeel voorbeelden. Onze groepen zijn ongelijk groot: gewichten 2/3 en 1/3.

ECE=∑ngN|p¯g−fg|

g benoemt een gevulde groep, n haar aantal en N het totale aantal. p met streep is haar gemiddelde kans en f haar positieve fractie. De uitkomst is 2/3 × 0.15 + 1/3 × 0.10, ongeveer 0.133333.

score = 0
for j in range(len(tabel)):
    index, n, p, f = tabel[j]
    aandeel = n / len(probs)
    score = score + aandeel * abs(p - f)
print(score)  # ongeveer 0.133333

Maak ece(probs,ys,k) met dezelfde tabel. Veelgemaakte fout: de twee afwijkingen ongewogen middelen; dat zou 0.125 geven en de kleine groep evenveel invloed geven als de grote. Bewaar daarom altijd ook aantallen en tabel bij de score.

Kies nu k = 1: alle kansen vallen samen. De gemiddelde kans is 0.4, de positieve fractie 1/3, dus ECE wordt ongeveer 0.066667. De afwijkingen in verschillende delen van die grote groep heffen elkaar deels op voordat je de absolute waarde neemt.

Een lagere ECE door een andere indeling bewijst geen beter model: de voorspellingen zijn niet veranderd. Vergelijk met een vooraf gekozen indeling en genoeg onafhankelijke voorbeelden. Er bestaan ook andere ECE-varianten; deze les gebruikt steeds de klasse-1-kans.

03
CHECKPOINTVat samen zonder de tabel te vergeten

Wat neem je mee?

  • Brier beoordeelt iedere kans, terwijl een kalibratietabel voorspelling en frequentie per groep vergelijkt.
  • Binranden, lege groepen en aantallen horen bij de betekenis van de uitkomst.
  • ECE hangt af van de gekozen indeling; toon de tabel en wees voorzichtig met kleine groepen.

Hierna onderzoek je beslisbomen: modellen die de invoerruimte met gerichte vragen opsplitsen.

DE VOLGENDE STAPStel steeds één gerichte vraag
JOUW EXPERIMENT

Probeer het zelf

Python
main.py

Python start zodra jij code uitvoert.

UITVOER

Je uitvoer verschijnt hier.
Alles rekent in je eigen browser.

Jouw checkpoints0 / 3
01
Meet kansfouten zonder drempel

Schrijf brier(probs,ys), met ValueError bij ongeldige invoer.

Een hint

De rekenvorm lijkt op MSE, maar hier zijn de voorspellingen kansen en de doelen binair.

02
Vergelijk voorspelling en frequentie per groep

Schrijf kalibratietabel(probs,ys,k), oplopend op intervalindex. Weiger k zonder positieve gehele waarde.

Een hint

Vergelijk p met randen(j+1)/k en kies het eerste vak waar p onder de bovengrens ligt; p=1 blijft in het laatste vak. Geef alleen gevulde groepen terug.

03
Vat samen zonder de tabel te vergeten

Schrijf ece(probs,ys,k) op basis van kalibratietabel.

Een hint

Weeg iedere absolute groepsafwijking met het aandeel voorbeelden in die groep.

Vastgelopen? Bekijk de voorbeeldoplossing
def brier(probs, ys):
    if not probs or len(probs) != len(ys):
        raise ValueError("Niet-lege kansparen vereist")
    totaal = 0
    for i in range(len(probs)):
        if not 0 <= probs[i] <= 1 or ys[i] not in (0, 1):
            raise ValueError("Ongeldig kanspaar")
        fout = probs[i] - ys[i]
        totaal = totaal + fout ** 2
    return totaal / len(probs)


def kalibratietabel(probs, ys, k):
    if type(k) is not int or k < 1:
        raise ValueError("Positief geheel aantal intervallen vereist")
    brier(probs, ys)
    aantallen = [0] * k
    som_p = [0.0] * k
    som_y = [0] * k
    for i in range(len(probs)):
        vak = k - 1
        for j in range(k):
            grens = (j + 1) / k
            if probs[i] < grens:
                vak = j
                break
        aantallen[vak] = aantallen[vak] + 1
        som_p[vak] = som_p[vak] + probs[i]
        som_y[vak] = som_y[vak] + ys[i]
    tabel = []
    for j in range(k):
        n = aantallen[j]
        if n > 0:
            p = som_p[j] / n
            y = som_y[j] / n
            tabel.append((j, n, p, y))
    return tabel


def ece(probs, ys, k):
    tabel = kalibratietabel(probs, ys, k)
    score = 0
    for j in range(len(tabel)):
        index, n, p, f = tabel[j]
        aandeel = n / len(probs)
        score = score + aandeel * abs(p - f)
    return score

Voer de code uit en verklaar voor jezelf waarom elke stap werkt.