- Je onderscheidt leren van parameters, modelkeuze en uiteindelijke evaluatie.
- Je maakt reproduceerbare, gescheiden datasets zonder de oorspronkelijke volgorde te veranderen.
Je wilt een model kiezen dat de bloei van nieuwe planten voorspelt. Als je steeds de toetsvragen bekijkt en daarna je aanpak aanpast, oefen je uiteindelijk op de toets. Een mooie score vertelt dan minder over planten die je echt nog niet hebt gezien.
We geven gegevens daarom drie verschillende rollen. Training leert de getallen van het model, validatie (validation) helpt een aanpak kiezen en test beoordeelt de gekozen aanpak pas aan het einde. In deze les bouw je de verdeling; het model zelf volgt later.
1. Schud herhaalbaar, zonder je bron te veranderen
Stel dat je wilt kiezen tussen een eenvoudige en een uitgebreidere voorspelregel. Beide leren op training; hun fouten op validatie helpen je kiezen. Daarna leg je de gekozen aanpak vast en meet je één keer op test, zonder die uitslag alsnog voor modelkeuze te gebruiken.
Voor losse, onafhankelijke records kun je eerst schudden. Denk aan kaartjes met record-ID's: je verandert de volgorde, niet welke kaartjes bestaan. Een seed is een startinstelling waarmee een toevalsgenerator dezelfde reeks keuzes kan herhalen.
Je kent import math; import random maakt nu de bibliotheek voor toevalskeuzes beschikbaar. random.Random(7) maakt een eigen generator met seed 7. Daarmee verander je niet de gedeelde generator die andere code misschien gebruikt.
import random
items = [1, 2, 3, 4, 5, 6]
kopie = list(items)
generator = random.Random(7)
generator.shuffle(kopie)
nogmaals = list(items)
random.Random(7).shuffle(nogmaals)
print(kopie == nogmaals) # True
print(sorted(kopie)) # [1, 2, 3, 4, 5, 6]
print(items) # [1, 2, 3, 4, 5, 6]
list(items) maakt een nieuwe lijst. .shuffle(kopie) herschikt die lijst zelf; gebruik daarom daarna kopie als resultaat, niet de terugkeerwaarde van shuffle. Voor dezelfde uitvoer maak je opnieuw een generator met dezelfde seed en geef je dezelfde oorspronkelijke lijst mee.
Maak geschud(items, seed) met deze stappen en geef de nieuwe lijst terug. De bronlijst blijft in haar oude volgorde. Gebruik een vaste seed om een experiment te herhalen, niet om steeds andere verdelingen te proberen totdat de testscore mooi uitvalt.
Veelgemaakte fout: reproduceerbaar verwarren met eerlijk. Een vaste seed bewijst niet dat je planten representatief zijn. Herhaalde metingen van dezelfde plant en tijdreeksen vragen bovendien een andere verdeling; die komen in de derde les.
2. Geef ieder kaartje precies één plek
Neem nu als aparte oefening de al geschudde lijst [4, 1, 6, 2, 5, 3]. We kiezen drie trainingsrecords en één validatierecord; er blijven twee testrecords over. Deze aantallen laten het mechanisme zien, maar zijn veel te klein voor een serieuze prestatiemeting.
Een slice kiest een stuk uit een Pythonlijst. In items[start:stop] telt start wel mee en stop niet; een weggelaten start betekent het begin, een weggelaten stop het einde. Posities beginnen bij nul, dus de eerste drie posities zijn 0, 1 en 2.
items = [4, 1, 6, 2, 5, 3]
n_train = 3
n_val = 1
grens = n_train + n_val
train = items[:n_train]
val = items[n_train:grens]
test = items[grens:]
print(train) # [4, 1, 6]
print(val) # [2]
print(test) # [5, 3]
De drie stukken sluiten aan: positie 3 begint validatie, positie 4 begint test. Slices maken nieuwe lijsten, zodat deze verdeling de invoer niet herschikt. Maak split3(items, n_train, n_val) en geef het tuple (train, val, test) terug.
De aantallen zijn gehele getallen. Weiger met ValueError een negatief aantal of een som groter dan de lijstlengte; nul is toegestaan in deze mechanische oefening. Voor een echte beoordeling heb je uiteraard bruikbare gegevens in iedere benodigde rol nodig.
3. Controleer identiteit over alle delen
Een correcte lengte sluit overlap niet uit. Training [1, 2], validatie [2] en test [3] bevatten vier plekken maar slechts drie unieke records: ID 2 staat tweemaal in de verdeling. Ook [1, 1] binnen training is een dubbel record.
Gebruik de set uit de vorige les om alle geziene ID's te onthouden. Loop eerst langs de drie lijsten en daarbinnen langs hun ID's. Een al geziene ID betekent een fout, ongeacht of hij uit dezelfde lijst of een ander deel kwam.
delen = [[1, 2], [2], [3]]
gezien = set()
goed = True
for i in range(len(delen)):
for j in range(len(delen[i])):
id = delen[i][j]
if id in gezien:
goed = False
gezien.add(id)
print(goed) # False, ID 2 komt terug
Maak gescheiden(train, val, test): geef bij de eerste herhaling False, anders na alle controles True. De invoer bestaat uit lijsten met tekst-ID's of gehele getallen, geen dictionaries met volledige records. Zulke ID's kunnen in een set worden opgeslagen.
Deze controle ontdekt identieke ID's, geen kopieën met een nieuwe naam. Daarom blijven het datacontract en de eerdere controle op dubbele metingen nodig.
Wat neem je mee?
- Training leert, validatie helpt kiezen en test blijft apart tot de aanpak vastligt.
- Schud een kopie met een eigen generator; een vaste seed maakt dezelfde proef herhaalbaar.
- Aansluitende slices verdelen posities; een setcontrole bewaakt unieke record-ID's.
De volgende les laat zien hoe de test toch kan meekijken, zelfs wanneer alle ID's netjes gescheiden zijn.