Machine Learning Academy · Lektion

Hvorfor De ikke kan evaluere på træningsdata

Demonstrér datalækage ved at evaluere en model, der har memoriseret data, og se, hvorfor tilbageholdte testdata er afgørende for ærlige estimater af ydeevnen.

Lektion 1 af 412 trin

Hvorfor De ikke kan evaluere på træningsdata er en gratis Machine Learning Academy-lektion på CoddyKit. Dette er lektion 1 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Machine Learning Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Machine Learning Academy-kurset indeholder 4 lektioner i alt.

Evalueringsfælden

Efter at have trænet en model er det mest fristende at teste den på de samme data, som du brugte til træningen. Modellen vil sandsynligvis få en meget høj score — nogle gange 100 % accuracy — og det føles som en succes. Det er det ikke. Dette er den mest grundlæggende fejl i machine learning, og den giver resultater, der er helt ubrugelige til at forudsige præstationen i den virkelige verden.

At forstå hvorfor dette mislykkes, er ikke blot en teknisk detalje — det ændrer din opfattelse af hele formålet med machine learning. Målet er aldrig at præstere godt på træningsdata. Målet er altid at generalisere til nye, data som modellen ikke har set før.

Memorisering kontra generalisering

Overvej forskellen mellem en elev, der memorerer hvert svar i en bog til eksamensforberedelse, og en elev, der faktisk forstår stoffet. Den første elev klarer alle øvelsesopgaver perfekt, men mislykkes, når den rigtige eksamen bruger en lidt anderledes formulering. Den anden elev klarer måske ikke øvelsesopgaverne perfekt, men håndterer nye spørgsmål med selvtillid.

En ML-model, der »memorerer« træningseksempler (en model med kraftig overtilpasning), opfører sig præcis som den første elev. Den opnår perfekt accuracy på træningsdata, men mislykkes på nye input. Dette fænomen kaldes data leakage, når det sker under evalueringen — du har »lækket« svarene ind i testen.

En demonstration: Memorisering i praksis

Lad os bevise det empirisk. Et beslutningstræ med ubegrænset dybde vil memorere hvert træningseksempel perfekt og opnå 100 % accuracy på træningsdata. Men dets accuracy på testdata vil være meget lavere, fordi det har lært støj i stedet for det egentlige underliggende mønster.

Dette eksperiment gør problemet konkret og målbart: accuracy på træningsdata er meningsløs som estimat for præstationen — den måler hukommelse, ikke intelligens.

from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# Unlimited depth: memorises training data
model = DecisionTreeClassifier()  # no max_depth limit
model.fit(X_train, y_train)

train_acc = model.score(X_train, y_train)
test_acc  = model.score(X_test, y_test)

print(f'Training accuracy: {train_acc:.3f}')  # 1.000 -- perfect memorisation
print(f'Test accuracy:     {test_acc:.3f}')   # much lower
print(f'Overfit gap:       {train_acc - test_acc:.3f}')

Hvorfor accuracy på træningsdata er optimistisk skæv

Modellens parametre blev specifikt optimeret til at minimere fejlen på træningsmængden. Det betyder, at træningsfejlen garanteret er lavere end den sande generaliseringsfejl for enhver rimeligt kompleks model. Forskellen mellem træningsfejlen og testfejlen kaldes optimismen i træningsfejlen.

Jo flere parametre en model har i forhold til antallet af træningseksempler, desto større er optimismen. Et neuralt netværk med millioner af parametre og kun 1.000 træningseksempler kan nemt opnå 0 % træningsfejl, mens den sande fejlrate er 50 %. Træningsfejl er ikke et pålideligt estimat for generalisering — punktum.

from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
import numpy as np

X, y = make_classification(n_samples=500, n_features=20, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# KNN with k=1: memorises perfectly (always finds the exact training point)
knn1 = KNeighborsClassifier(n_neighbors=1)
knn1.fit(X_train, y_train)

print(f'k=1 Training accuracy: {knn1.score(X_train, y_train):.3f}')  # 1.000
print(f'k=1 Test accuracy:     {knn1.score(X_test, y_test):.3f}')    # lower

# k=10: generalisers better
knn10 = KNeighborsClassifier(n_neighbors=10)
knn10.fit(X_train, y_train)
print(f'k=10 Test accuracy:    {knn10.score(X_test, y_test):.3f}')

Testmængden, der holdes ude: Løsningen

Løsningen er enkel, men skal håndhæves konsekvent: Reserver en del af dine data, før du begynder at modellere, og brug dem aldrig til træning eller til at træffe beslutninger, der påvirker modellen.

Denne testmængde, der holdes ude, er dit ærlige estimat for generaliseringspræstationen. Fordi modellen aldrig har set den, er dens præstation på denne mængde det bedst tilgængelige estimat for, hvordan den vil præstere på fremtidige data. Testmængden er et måleinstrument til engangsbrug — hvis du bruger den flere gange til at justere din model, bliver den ugyldig.

from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
import numpy as np

X, y = load_breast_cancer(return_X_y=True)

# CORRECT workflow: split BEFORE any analysis
X_train, X_test, y_train, y_test = train_test_split(
    X, y,
    test_size=0.20,   # 20% held out for final evaluation only
    random_state=42,  # reproducible split
    stratify=y        # maintain class ratio in both splits
)

print(f'Training set:  {X_train.shape[0]} examples')
print(f'Test set:      {X_test.shape[0]} examples')
print(f'Train class balance: {y_train.mean():.3f}')
print(f'Test class balance:  {y_test.mean():.3f}')

Data leakage: Den subtile version

At bruge testdata til den endelige evaluering er den mest åbenlyse form for datalækage. Der findes mere subtile former, som er lige så ødelæggende:

  • Lækage under forbehandling: at tilpasse en skaleringsfunktion til hele datasættet, før det opdeles, og derefter skalere trænings- og testdata — skaleringsfunktionen har dermed "set" statistikker fra testdataene.
  • Funktionslækage: at medtage en funktion, der er afledt af målvariablen (f.eks. et flag for "diagnose bekræftet", som kun sættes, når en patient faktisk er syg).
  • Tidslækage: at bruge fremtidige data til at forudsige tidligere hændelser (f.eks. at medtage salgstal for 3. kvartal ved forudsigelse af resultater for 1. kvartal).
from sklearn.preprocessing import StandardScaler
import numpy as np

# WRONG: fit scaler on full dataset before splitting
X = np.random.randn(1000, 5)
scaler_wrong = StandardScaler()
X_scaled_all = scaler_wrong.fit_transform(X)  # leakage! scaler saw test data

# Correct: split first, then fit scaler ONLY on training data
from sklearn.model_selection import train_test_split
X_train, X_test = train_test_split(X, test_size=0.2)
scaler_correct = StandardScaler()
X_train_s = scaler_correct.fit_transform(X_train)  # fit on train only
X_test_s  = scaler_correct.transform(X_test)        # apply to test
print('Correct preprocessing: scaler fitted on training data only.')

Tredeling: træning, validering og test

Når du bruger testsættet til at træffe beslutninger — f.eks. til at vælge mellem to modeller eller udvælge en tærskel — er det ikke længere et rent estimat af generaliseringsevnen. Hold testsættet uberørt ved at indføre et valideringssæt:

  • Træningssæt: tilpas modellens parametre.
  • Valideringssæt: juster hyperparametre, vælg modeller, og tilpas tærskler.
  • Testsæt: endelig evaluering én gang. Brug det kun én gang, helt til sidst.

En almindelig opdeling er 70 % træning / 15 % validering / 15 % test. Krydsevaluering er et mere effektivt alternativ, hvor du skiftevis bruger valideringsfoldere på træningssættet.

from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer

X, y = load_breast_cancer(return_X_y=True)

# First split off test set (15%)
X_temp, X_test, y_temp, y_test = train_test_split(X, y, test_size=0.15, random_state=42)
# Then split remaining into train and validation
X_train, X_val, y_train, y_val = train_test_split(X_temp, y_temp, test_size=0.176, random_state=42)
# 0.176 of 85% ≈ 15% of the original

print(f'Train: {X_train.shape[0]}')
print(f'Val:   {X_val.shape[0]}')
print(f'Test:  {X_test.shape[0]}')

Sådan forurening af testsættet oppuster resultaterne

Antag, at du træner 10 forskellige modeller, evaluerer hver af dem på testsættet og vælger den med den højeste testnøjagtighed. Denne proces har forurenet testsættet — du har brugt testresultatet til at træffe en modelbeslutning. Den valgte model er optimeret til testsættet, og dens rapporterede nøjagtighed er nu for optimistisk.

Det er netop derfor, konkurrencer som Kaggle har en offentlig rangliste (valideringssæt) og en privat rangliste (det egentlige testsæt, som først afsløres til sidst). Hold, der overtilpasser den offentlige rangliste ved at indsende mange bidrag, klarer sig ofte dårligt på den private rangliste.

from sklearn.datasets import make_classification
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
import numpy as np

X, y = make_classification(n_samples=500, random_state=99)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

# Contamination: picking best max_depth based on test set
best_acc, best_depth = 0, 1
for depth in range(1, 20):
    model = DecisionTreeClassifier(max_depth=depth)
    model.fit(X_train, y_train)
    acc = model.score(X_test, y_test)
    if acc > best_acc:
        best_acc, best_depth = acc, depth

print(f'Best depth selected by test: {best_depth}, acc: {best_acc:.3f}')
print('This accuracy is now overly optimistic!')

Genskabelighed: Parametret random_state

Parameteren random_state i train_test_split bestemmer, hvilke eksempler der ender i hvilke dele. Uden den giver hver kørsel en anden opdeling, hvilket gør resultater vanskelige at genskabe og sammenligne.

Sæt altid random_state til et fast heltal i al kode, du deler med andre, eller som du vil kunne genskabe senere. Alle heltal fungerer — konventionen er at bruge 42, 0 eller 1 — så længe du dokumenterer, hvilken værdi du brugte. Den konkrete heltalsværdi er ikke vigtig; det er konsekvensen, der er vigtig.

from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris

X, y = load_iris(return_X_y=True)

# Without random_state: different results every run
X_train1, X_test1, _, _ = train_test_split(X, y)          # no seed
X_train2, X_test2, _, _ = train_test_split(X, y)          # no seed
print('Different splits without seed:', not (X_train1 == X_train2).all())

# With random_state: same result every run
X_train3, X_test3, _, _ = train_test_split(X, y, random_state=42)
X_train4, X_test4, _, _ = train_test_split(X, y, random_state=42)
print('Same splits with seed:', (X_train3 == X_train4).all())

Den gyldne regel for evaluering af ML

Den ene regel, der sammenfatter alt i denne lektion, er: Testdatasættet må aldrig påvirke en beslutning, der træffes under modeludviklingen. Det betyder:

  • Ingen forbehandling, der er tilpasset testdata.
  • Ingen model valgt på baggrund af testresultater.
  • Ingen tærskel justeret på baggrund af testresultater.
  • Ingen funktioner udviklet efter at have set testfejl.
  • Testsættet må kun bruges én gang, helt til sidst, til at rapportere den endelige ydeevne.

Hvis du følger denne regel, sikrer du, at den rapporterede ydeevne er et ærligt estimat af, hvordan modellen vil opføre sig i produktion på fremtidige data.

Hurtigt tjek

Test din forståelse af begreberne inden for Machine Learning med Python fra denne lektion.

Opsummering af lektionen

I denne lektion har du lært, at træningsnøjagtighed altid er optimistisk skæv, fordi modellens parametre blev optimeret på disse data, at et separat testsæt, som aldrig påvirker beslutninger under træningen, giver et ærligt estimat af generaliseringsevnen, og at datadækage — herunder lækage under forbehandling og forurening af testsættet — giver vildledende høje estimater af ydeevnen. Næste gang lærer du train_test_split-funktionen i scikit-learn grundigt at kende, herunder forholdet mellem teststørrelser, tilfældige frø og stratificering til problemer med ubalanceret klassifikation.

Gratis at komme i gang

Lær Python med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
30
Lektioner
120

Ofte stillede spørgsmål

Er lektionen “Hvorfor De ikke kan evaluere på træningsdata” gratis?

Ja — hele teksten til “Hvorfor De ikke kan evaluere på træningsdata” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Machine Learning Academy-kurset, skal du opgradere til CoddyKit PRO. Machine Learning Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Hvorfor De ikke kan evaluere på træningsdata”?

Demonstrér datalækage ved at evaluere en model, der har memoriseret data, og se, hvorfor tilbageholdte testdata er afgørende for ærlige estimater af ydeevnen. Du øver dig i Machine Learning Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Machine Learning Academy?

Der kræves ingen tidligere erfaring. Machine Learning Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 1 af 4.

Hvor lang tid tager lektionen “Hvorfor De ikke kan evaluere på træningsdata”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Machine Learning Academy-lektion?

Ja. Alle Machine Learning Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Hvorfor De ikke kan evaluere på træningsdata
  2. train_test_split: Forhold, seeds og stratificering
  3. Bias-variance-afvejning: Underfitting eller overfitting
  4. Baseline-modeller: Slå altid DummyClassifier
← Tilbage til Machine Learning Academy