- Je verdeelt iedere rij precies één keer over validatiefolds.
- Je fit opnieuw per fold en kiest een parameter uitsluitend op validatiescores.
Je vergelijkt twee modellen, maar één andere train/validatiesplit draait de winnaar om. Was het model beter, of kreeg het toevallig eenvoudigere validatievoorbeelden? K-fold crossvalidatie (cross-validation) herhaalt de vergelijking met verschillende achtergehouden delen van dezelfde ontwikkeldata.
Ieder voorbeeld krijgt één validatiebeurt. De uiteindelijke testset blijft apart: je gebruikt de folds om een aanpak te kiezen, fit die daarna opnieuw op alle ontwikkeldata en beoordeelt haar ten slotte op die eindtest.
1. Geef iedere rij één beurt buiten de training
Neem vijf ontwikkelrijen met indices [0,1,2,3,4] en k = 2. Je kunt vijf niet eerlijk in twee gelijke gehele groepen verdelen. Geef daarom de eerste fold drie validatierijen en de tweede twee.
| Ronde | Training | Validatie |
|---|---|---|
| 1 | 3,4 | 0,1,2 |
| 2 | 0,1,2 | 3,4 |
Iedere rij staat precies eenmaal in een validatielijst. Binnen iedere ronde zijn training en validatie disjunct en vormen ze samen alle ontwikkelrijen. Een rij kan dus in een andere ronde wél training zijn.
n, k = 5, 2
start = 0
for i in range(k):
grootte = n // k
if i < n % k:
grootte = grootte + 1
einde = start + grootte
val = list(range(start, einde))
train = []
for j in range(n):
if j not in val:
train.append(j)
print(train, val)
start = einde
# [3, 4] [0, 1, 2]
# [0, 1, 2] [3, 4]
// geeft het gehele quotiënt en % de rest. Maak folds(n,k) als lijst van (train,val)-paren, met gehele aantallen en 2 ≤ k ≤ n; ongeldige aantallen geven ValueError. Er wordt hier niet geschud: de invoer bevat onafhankelijke voorbeelden in een passende volgorde.
Twaalf rijen, vier folds. Groen is training; oranje is validatie. Kies een ronde en volg de drie onbekende rijen.
Ook de scaler wordt elke ronde opnieuw op alleen de negen trainingsrijen gefit. De uiteindelijke testset zit niet in dit schema.
Het diagram heeft twaalf rijen en vier folds. Het toont rijlabels 1–12 voor de leesbaarheid; jouw code gebruikt indices vanaf nul. Zoek in ronde 3 welke drie rijen onbekend zijn en controleer dat training hun complement is.
Gewone folds bewaken geen plantgroepen of tijdsvolgorde. Gebruik bij afhankelijke gegevens de groeps- en tijdsplitsingen uit de datatrack.
012. Laat een functie per fold opnieuw leren
De evaluatiecode hoeft niet te weten welk model je kiest. Zij krijgt een functie fit_predict(train_x,train_y,val_x) mee: de functie zelf is het argument, zonder haar meteen aan te roepen. Pas binnen iedere ronde roep je haar met de drie juiste gegevenslijsten aan.
Begin met een eenvoudige baseline die alleen het trainingsgemiddelde leert. Bij doelen [0,1,2,3,4] is in ronde 1 het trainingsgemiddelde (3+4)/2 = 3.5. De voorspellingen voor de drie validatierijen zijn dus [3.5,3.5,3.5].
def fit_gemiddelde(train_x, train_y, val_x):
gemiddelde = sum(train_y) / len(train_y)
return [gemiddelde] * len(val_x)
xs = [10, 20, 30, 40, 50]
ys = [0, 1, 2, 3, 4]
p = fit_gemiddelde(
[40, 50], [3, 4], [10, 20, 30])
print(p) # [3.5, 3.5, 3.5]
De callback gebruikt train_x hier niet, omdat een constante baseline geen features nodig heeft. Een echte modelcallback bouwt bij iedere aanroep een nieuwe pipeline, fit haar op train_x/train_y en voorspelt val_x. Ook een scaler mag alleen uit die trainingsrijen leren.
3. Meet alleen de achtergehouden doelen
De kwadratische fouten in ronde 1 zijn 12.25, 6.25 en 2.25: MSE = 20.75/3, ongeveer 6.916667. In ronde 2 leert de baseline gemiddelde 1 en voorspelt dat voor doelen 3 en 4: MSE = (4+9)/2 = 6.5.
val_y = [0, 1, 2]
totaal = 0
for i in range(len(p)):
totaal = totaal + (val_y[i] - p[i]) ** 2
print(totaal / len(p)) # ongeveer 6.916667
Maak cv_scores(xs,ys,k,fit_predict) met een verse callbackaanroep per fold en één MSE per ronde. Controleer gelijke invoerlengtes en precies één voorspelling per validatierij; een verkeerde voorspellinglengte geeft ValueError. Gebruik de validatie-indices om doelen en voorspellingen correct te koppelen.
Veelgemaakte fout: de trainings-x-waarden als doelen doorgeven. Dat kan onzichtbaar blijven als een oefening toevallig x = y gebruikt; onze hoogtes en doelen zijn bewust verschillend.
024. Kies eerst het criterium, dan de winnaar
Een hyperparameter stel je vóór het fitten in, zoals een boomdiepte. Vergelijk per kandidaat het gemiddelde van zijn foldscores. Hier telt iedere fold even zwaar, ook wanneer de aantallen validatierijen verschillen.
j benoemt een fold en L haar gemiddelde verlies. Voor onze baseline geeft dit ongeveer 6.708333; alle vijf afzonderlijke fouten samen middelen zou 6.75 geven. Beide criteria zijn mogelijk, maar je moet vooraf weten welk je gebruikt.
resultaten = {1: [6, 8],
2: [4, 6],
3: [3, 7]}
kandidaten = []
for parameter, scores in resultaten.items():
gemiddelde = sum(scores) / len(scores)
paar = gemiddelde, parameter
kandidaten.append(paar)
print(min(kandidaten)[1]) # 2
Parameters 2 en 3 hebben beide gemiddelde 5; onze vaste tie-regel kiest de kleinste integerparameter. Maak kies_parameter(resultaten) en weiger een lege dictionary of lege scorelijsten. Gebruik eindige scores.
De winnende CV-score is al door de selectie beïnvloed. Beoordeel de uiteindelijk gekozen aanpak daarom op een aparte testset, of gebruik een buitenste crossvalidatieronde wanneer je het hele selectieproces wilt onderzoeken.
03Wat neem je mee?
- Iedere rij is eenmaal validatie; training is per ronde het complement.
- De callback leert iedere ronde opnieuw, inclusief voorbereiding.
- Het selectiecriterium en de aparte eindtest horen bij de modelkeuze.
Hierna onderzoek je waarom meer modelvrijheid soms helpt en soms vooral trainingsruis volgt.