- Je onderscheidt false positives en false negatives en berekent passende scores.
- Je ziet hoe een drempel de foutverdeling verandert zonder het kansmodel opnieuw te trainen.
Een spamfilter kan echte mail blokkeren of spam doorlaten. Die fouten hebben verschillende gevolgen, ook als het totale aantal fouten gelijk is. Een verwarringsmatrix (confusion matrix) houdt bij welke verkeerde keuze het model maakte.
We noemen spam klasse 1, de positieve klasse. Positief betekent hier een gekozen label, niet iets wenselijks. Stel je vier vakken voor: de werkelijke klasse bepaalt de rij, de voorspelde klasse de kolom.
1. Geef elke beslissing precies één vak
Een true positive (TP) is terecht als spam aangemerkt. Een false positive (FP) is gewone mail die het model toch blokkeert; een false negative (FN) is gemiste spam. Bij een true negative (TN) blijven werkelijkheid en voorspelling beide 0.
Neem zes berichten met kansen [0.10,0.25,0.40,0.55,0.70,0.90] en werkelijke labels [0,1,0,1,0,1]. Kies voorlopig drempel 0.30: kansen minstens zo hoog worden klasse 1. Loop eerst zelf door de tabel en tel de vier uitkomsten.
| Kans | Echt | Keuze | Uitkomst |
|---|---|---|---|
| 0.10 | 0 | 0 | TN |
| 0.25 | 1 | 0 | FN |
| 0.40 | 0 | 1 | FP |
| 0.55 | 1 | 1 | TP |
| 0.70 | 0 | 1 | FP |
| 0.90 | 1 | 1 | TP |
Je krijgt TP = 2, FP = 2, FN = 1 en TN = 1. Samen zijn dat weer zes berichten. De volgende code kiest per paar precies één sleutel in een dictionary.
ys = [0, 1, 0, 1, 0, 1]
pred = [0, 0, 1, 1, 1, 1]
c = {"tp": 0, "fp": 0, "fn": 0, "tn": 0}
for i in range(len(ys)):
if ys[i] == 1:
if pred[i] == 1:
vak = "tp"
else:
vak = "fn"
else:
if pred[i] == 1:
vak = "fp"
else:
vak = "tn"
c[vak] = c[vak] + 1
print(c) # tp2, fp2, fn1, tn1
Maak verwarring(ys,pred) en geef de dictionary terug. Controleer eerst gelijke lengtes en alleen labels 0/1, anders ValueError. Twee lege lijsten geven vier nullen; niets waarnemen is geen bewijs van een perfect model.
2. Kies de vraag die bij de gevolgen past
Precisie (precision) vraagt: hoeveel van de als spam gekozen berichten zijn werkelijk spam? In ons voorbeeld zijn twee van de vier blokkades terecht, dus 1/2. Recall, ook gevoeligheid genoemd, vraagt hoeveel echte spam je vindt: twee van de drie, dus 2/3.
De letters verwijzen naar de vier tellingen, niet naar kansen. De twee noemers kiezen verschillende groepen: alle positieve voorspellingen tegenover alle werkelijke positieven. Precision en recall zijn daardoor niet uitwisselbaar.
F1 combineert beide via 2TP/(2TP+FP+FN), hier 4/7, ongeveer 0.571. Accuracy telt alle juiste keuzes, (TP+TN)/N; N is het totale aantal voorbeelden. Hier is accuracy 3/6 = 0.5.
def deel(teller, noemer):
if noemer == 0:
return None
return teller / noemer
tp, fp = c["tp"], c["fp"]
fn, tn = c["fn"], c["tn"]
print(deel(tp, tp + fp)) # 0.5
print(deel(tp, tp + fn)) # 0.666...
print(deel(2 * tp, 2 * tp + fp + fn))
# ongeveer 0.571
print(deel(tp + tn, tp + fp + fn + tn))
# 0.5
Maak klasse_scores(c) met sleutels accuracy, precision, recall, f1. Ga uit van vier geldige niet-negatieve tellingen. Bij een noemer nul is de betreffende score None: zonder positieve voorspellingen kun je hun precision niet meten; zonder voorbeelden zijn alle vier scores ongedefinieerd.
Veelgemaakte fout: alleen accuracy melden bij zeldzame spam. Een filter dat alles doorlaat kan veel gewone mail juist behandelen, terwijl het iedere spam mist. F1 negeert juist de true negatives; geen van beide vervangt het bekijken van alle fouttypen.
023. Houd de kansen vast en verander de keuze
Bij drempel 0.50 worden de voorspellingen [0,0,0,1,1,1]. Nu is TP = 2, FP = 1, FN = 1 en TN = 2: je blokkeert één gewone mail minder. Precision stijgt van 1/2 naar 2/3, terwijl recall 2/3 blijft.
probs = [0.10, 0.25, 0.40, 0.55, 0.70, 0.90]
t = 0.50
pred = []
for i in range(len(probs)):
keuze = 0
if probs[i] >= t:
keuze = 1
pred.append(keuze)
print(pred) # [0, 0, 0, 1, 1, 1]
Maak bij_drempel(ys,probs,t) door eerst deze keuzen te maken en dan verwarring te gebruiken. Kansen en drempel moeten in [0,1] liggen; anders ValueError. Een kans precies op de drempel telt als positief.
Zes fictieve spamkansen met vaste labels. Verhoog de drempel om minder berichten als spam aan te merken.
| Werkelijkheid | Geen spam | Spam |
|---|---|---|
| Geen spam | TN 2 | FP 1 |
| Spam | FN 1 | TP 2 |
Zet de schuif eerst op 0.30 en daarna op 0.50 en vergelijk met je berekening. Voorspel vervolgens 0.80: alleen het laatste bericht wordt spam, dus TP = 1, FN = 2, FP = 0 en TN = 3. Precision wordt 1, maar recall daalt naar 1/3.
Een hogere drempel geeft nooit méér positieve keuzes op dezelfde kansen. Precision hoeft echter niet bij iedere stap te stijgen: het hangt af van welke berichten verdwijnen. Kies je drempel op validatiedata met passende foutkosten, en houd de eindtest apart.
03Wat neem je mee?
- De matrix onderscheidt gemiste positieven van onterechte positieve keuzes.
- Iedere score stelt een andere vraag; let op haar noemer en op ontbrekende waarnemingen.
- Een andere drempel verandert beslissingen zonder het kansmodel opnieuw te trainen.
Hierna beoordeel je de kansen zelf, vóór je ze omzet naar ja of nee.