Byg din første machine learning-model med scikit-learn i Python

Lær at bygge din første ML-model med scikit-learn i Python. Komplet begynderguide med pipelines, krydsvalidering, hyperparameter-tuning og kodeeksempler du kan bruge direkte.

Scikit-learn Guide 2026: Byg ML-Model i Python

Hvorfor scikit-learn er det bedste sted at starte med machine learning

Machine learning lyder avanceret. Og ja, det kan det også være. Men her er hemmeligheden: du kan bygge din første fungerende model med under 20 linjer Python-kode – hvis du bruger det rigtige værktøj. Det værktøj hedder scikit-learn.

Scikit-learn (de fleste kalder det bare sklearn) er det mest udbredte Python-bibliotek til klassisk machine learning. Det bruges overalt – fra startups til forskerhold på universiteterne – og det er bygget oven på NumPy, SciPy og matplotlib. Hvis du har prøvet lidt Python-datavidenskab, kender du sandsynligvis allerede de biblioteker.

I denne guide bygger vi en komplet machine learning pipeline fra bunden. Du lærer at indlæse data, forberede det, træne en model, evaluere resultaterne og sammenligne forskellige algoritmer. Al kode kan kopieres direkte ind i din Jupyter Notebook eller Python-fil, så du kan følge med undervejs.

Installation og opsætning

Først skal du have scikit-learn installeret. Vi bruger version 1.8 her, den nyeste stabile udgave. Kør dette i din terminal:

pip install scikit-learn pandas numpy matplotlib

Tjek derefter at alt er korrekt installeret:

import sklearn
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

print(f"scikit-learn version: {sklearn.__version__}")
print(f"pandas version: {pd.__version__}")
print(f"NumPy version: {np.__version__}")

Du bør se scikit-learn 1.8.x eller nyere. Bruger du en ældre version, fungerer det meste stadig – men enkelte funktioner kan mangle.

Machine learning i fem minutter: Det store overblik

Inden vi dykker ned i koden, lad os lige tage et skridt tilbage. Hvad er det egentlig, vi gør her? Machine learning handler om at finde mønstre i data og bruge dem til at lave forudsigelser. Processen ser typisk sådan ud:

  1. Indlæs data – hent dit datasæt ind i Python
  2. Udforsk og forstå data – se på fordelinger, manglende værdier, korrelationer
  3. Forbered data – rens, skaler og transformer features
  4. Opdel i trænings- og testsæt – så du kan evaluere objektivt
  5. Træn en model – lad algoritmen lære mønstre fra træningsdata
  6. Evaluer modellen – test på data, modellen aldrig har set
  7. Forbedre og gentag – prøv andre algoritmer eller juster parametre

Det smukke ved scikit-learn er, at alle algoritmer følger det samme mønster: fit() for at træne, predict() for at forudsige. Når du først har lært én model, kan du i princippet bruge dem alle. Det er ærligt talt ret elegant.

Vores projekt: Forudsig vintype baseret på kemiske målinger

Vi bruger scikit-learns indbyggede Wine-datasæt. Det indeholder kemiske analyser af 178 italienske vine fra tre forskellige producenter. Opgaven? At forudsige hvilken producent en vin kommer fra, baseret på 13 kemiske egenskaber som alkoholindhold, æblesyre og farveintensitet.

Det er et klassifikationsproblem – vi forsøger at placere hver vin i én af tre kategorier.

from sklearn.datasets import load_wine
import pandas as pd

# Indlæs datasættet
wine = load_wine()

# Konverter til pandas DataFrame for nemmere udforskning
df = pd.DataFrame(wine.data, columns=wine.feature_names)
df["target"] = wine.target
df["vintype"] = df["target"].map({0: "Producent A", 1: "Producent B", 2: "Producent C"})

print(f"Datasæt: {df.shape[0]} vine, {df.shape[1] - 2} features")
print(f"\nVintyper:\n{df['vintype'].value_counts()}")
print(f"\nFørste 5 rækker:")
print(df.head())

Du vil se 178 rækker med 13 numeriske features. Fordelingen mellem de tre klasser er nogenlunde jævn, hvilket gør det til et rigtig fint begynderdatasæt.

Udforsk data visuelt

Før vi bygger en model, bør vi faktisk forstå vores data. Det er fristende at springe dette trin over (det har vi alle gjort), men det kan spare dig for en masse hovedpine senere. Lad os se på et par vigtige features:

fig, axes = plt.subplots(1, 3, figsize=(15, 4))

for i, feature in enumerate(["alcohol", "flavanoids", "color_intensity"]):
    for vintype in range(3):
        subset = df[df["target"] == vintype]
        axes[i].hist(subset[feature], alpha=0.6, label=f"Producent {chr(65+vintype)}", bins=15)
    axes[i].set_title(feature.replace("_", " ").title())
    axes[i].legend()

plt.tight_layout()
plt.show()

Hvis du ser klart adskilte fordelinger for de tre klasser, er det et godt tegn. Det betyder, at modellen har noget at arbejde med. Features som flavanoids og color_intensity adskiller typisk klasserne ret tydeligt.

Opdel data: Træning vs. test

Den vigtigste regel i machine learning – og den er ikke til diskussion: evaluer aldrig din model på de samme data, den er trænet på. Det ville svare til at give en elev eksamensspørgsmålene på forhånd. Du tester hukommelse, ikke forståelse.

Scikit-learn gør dette nemt med train_test_split():

from sklearn.model_selection import train_test_split

# Features (X) og target (y)
X = df[wine.feature_names]
y = df["target"]

# Opdel: 80% træning, 20% test
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

print(f"Træningssæt: {X_train.shape[0]} vine")
print(f"Testsæt: {X_test.shape[0]} vine")
print(f"\nKlassefordeling i testsæt:\n{y_test.value_counts().sort_index()}")

Parameteren stratify=y sikrer, at klassefordelingen bevares i begge sæt. Og random_state=42? Det gør resultatet reproducerbart – du får de samme data hver gang du kører koden. (Ja, 42 er den mest brugte random state. Det er en ting.)

Dataforbehandling: Skalering af features

De fleste machine learning-algoritmer fungerer bedst, når alle features er på samme skala. Tænk over det: alkoholindhold ligger typisk mellem 11-15, mens prolineværdier kan være over 1.000. Uden skalering vil algoritmen fejlagtigt vægte proline højere, simpelthen fordi tallet er større.

Vi bruger StandardScaler, der transformerer hver feature til gennemsnit 0 og standardafvigelse 1:

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()

# Vigtigt: fit kun på træningsdata!
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

print("Gennemsnit før skalering (første 3 features):", X_train.iloc[:, :3].mean().values.round(2))
print("Gennemsnit efter skalering (første 3 features):", X_train_scaled[:, :3].mean(axis=0).round(2))

Kritisk detalje: Vi kalder fit_transform() på træningsdata, men kun transform() på testdata. Testdata skal skaleres med de samme parametre som træningsdata. Ellers lækker information fra testsættet ind i din model – det hedder data leakage, og det er ærligt talt en af de mest almindelige begynderfejl. Jeg har selv lavet den fejl mere end én gang.

Træn din første model: Random Forest

Så, nu til det sjove! Vi starter med en Random Forest Classifier – en robust algoritme, der fungerer overraskende godt uden meget tuning. Den bygger mange beslutningstræer og lader dem stemme om resultatet (demokrati i algoritmens verden, om man vil).

from sklearn.ensemble import RandomForestClassifier

# Opret og træn modellen
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train_scaled, y_train)

# Forudsig på testdata
y_pred = model.predict(X_test_scaled)

print(f"Forudsigelser: {y_pred[:10]}")
print(f"Faktiske:      {y_test.values[:10]}")

Det er det. Tre linjer: opret, træn, forudsig. Den konsistente API i scikit-learn gør det utroligt let at komme i gang.

Evaluer modellen: Hvor god er den egentlig?

At se på enkelte forudsigelser giver ikke det store billede. Vi har brug for systematiske evalueringsmetrikker.

Accuracy og classification report

from sklearn.metrics import accuracy_score, classification_report

accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy:.2%}")
print(f"\nDetaljeret rapport:")
print(classification_report(y_test, y_pred, target_names=["Producent A", "Producent B", "Producent C"]))

Du bør se en accuracy på omkring 97-100%. Ret imponerende for så lidt kode! Classification report giver dig mere nuanceret information:

  • Precision – af alle vine modellen sagde var fra Producent A, hvor mange var det faktisk?
  • Recall – af alle vine der faktisk var fra Producent A, hvor mange fandt modellen?
  • F1-score – det harmoniske gennemsnit af precision og recall (en slags kompromis mellem de to)

Confusion matrix: Se præcis hvor modellen fejler

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

cm = confusion_matrix(y_test, y_pred)
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=["Prod. A", "Prod. B", "Prod. C"])

fig, ax = plt.subplots(figsize=(7, 5))
disp.plot(ax=ax, cmap="Blues")
ax.set_title("Confusion Matrix – Random Forest")
plt.tight_layout()
plt.show()

Confusion matrix viser dig, vin for vin, hvad modellen forudsagde versus det korrekte svar. Tallene på diagonalen er korrekte forudsigelser. Alt uden for diagonalen? Fejl. Simpelt, men utrolig nyttigt.

Sammenlign flere algoritmer

En af de ting, jeg bedst kan lide ved scikit-learn, er at du nemt kan prøve forskellige algoritmer takket være den ensartede API. Lad os sammenligne fire populære klassifikationsalgoritmer:

from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import cross_val_score

modeller = {
    "Random Forest": RandomForestClassifier(n_estimators=100, random_state=42),
    "Gradient Boosting": GradientBoostingClassifier(n_estimators=100, random_state=42),
    "SVM (RBF kernel)": SVC(kernel="rbf", random_state=42),
    "K-Nearest Neighbors": KNeighborsClassifier(n_neighbors=5),
}

resultater = {}

for navn, model in modeller.items():
    scores = cross_val_score(model, X_train_scaled, y_train, cv=5, scoring="accuracy")
    resultater[navn] = scores
    print(f"{navn:25s} – Gns. accuracy: {scores.mean():.2%} (+/- {scores.std():.2%})")

# Visualiser resultaterne
fig, ax = plt.subplots(figsize=(10, 5))
ax.boxplot(resultater.values(), labels=resultater.keys())
ax.set_ylabel("Accuracy")
ax.set_title("5-fold krydsvalidering – Sammenligning af algoritmer")
plt.xticks(rotation=15)
plt.tight_layout()
plt.show()

Vi bruger 5-fold krydsvalidering her i stedet for et enkelt train/test-split. Det betyder, at modellen trænes og evalueres fem gange på forskellige opdelinger af data. Resultatet er et mere pålideligt estimat af modellens faktiske præstation.

Brug scikit-learn Pipelines: Den professionelle tilgang

I eksemplerne ovenfor skalerede vi data manuelt før træning. Det fungerer fint som demonstration, men i praksis er det en opskrift på fejl – du kan nemt glemme at skalere testdata, eller du kan anvende forkert skalering. Pipelines løser dette ved at samle forbehandling og model i ét objekt:

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

# Definer pipeline: skalering → model
pipeline = Pipeline([
    ("scaler", StandardScaler()),
    ("classifier", RandomForestClassifier(n_estimators=100, random_state=42))
])

# Krydsvalidering med pipeline – alt sker automatisk korrekt
scores = cross_val_score(pipeline, X, y, cv=5, scoring="accuracy")
print(f"Pipeline accuracy: {scores.mean():.2%} (+/- {scores.std():.2%}")

# Træn den endelige model på alle træningsdata
pipeline.fit(X_train, y_train)
pipeline_accuracy = pipeline.score(X_test, y_test)
print(f"Test accuracy med pipeline: {pipeline_accuracy:.2%}")

Med en pipeline behøver du aldrig bekymre dig om data leakage. Under krydsvalidering sørger scikit-learn automatisk for, at fit_transform() kun kaldes på træningsfolds og transform() på valideringsfolds. Det er virkelig en gamechanger, når projekterne bliver større.

Avanceret pipeline med ColumnTransformer

I virkelige projekter har du ofte blandede datatyper – tal og kategorier. ColumnTransformer håndterer dette elegant:

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer

# Eksempel med blandede features
numeriske_features = ["alcohol", "malic_acid", "ash"]
kategoriske_features = []  # Wine-datasættet har kun numeriske, men syntaksen er denne:

preprocessor = ColumnTransformer(transformers=[
    ("num", Pipeline([
        ("imputer", SimpleImputer(strategy="median")),
        ("scaler", StandardScaler())
    ]), numeriske_features),
    # ("cat", OneHotEncoder(handle_unknown="ignore"), kategoriske_features),
])

fuld_pipeline = Pipeline([
    ("preprocessor", preprocessor),
    ("classifier", RandomForestClassifier(n_estimators=100, random_state=42))
])

print("Pipeline-trin:", [step[0] for step in fuld_pipeline.steps])

Denne struktur er den, du bør bruge i produktionskode. Den er reproducerbar, nem at gemme med joblib, og kan serialiseres til deployment.

Hyperparameter-tuning med GridSearchCV

Standardindstillingerne for en algoritme er sjældent optimale – de er bare fornuftige defaults. GridSearchCV tester systematisk forskellige kombinationer af parametre og finder den bedste:

from sklearn.model_selection import GridSearchCV

# Definer parametre der skal testes
param_grid = {
    "classifier__n_estimators": [50, 100, 200],
    "classifier__max_depth": [None, 10, 20],
    "classifier__min_samples_split": [2, 5],
}

grid_search = GridSearchCV(
    pipeline,
    param_grid,
    cv=5,
    scoring="accuracy",
    n_jobs=-1,  # Brug alle CPU-kerner
    verbose=1
)

grid_search.fit(X_train, y_train)

print(f"\nBedste parametre: {grid_search.best_params_}")
print(f"Bedste CV-accuracy: {grid_search.best_score_:.2%}")
print(f"Test accuracy: {grid_search.score(X_test, y_test):.2%}")

Bemærk syntaksen classifier__n_estimators med dobbelt underscore. Det fortæller GridSearchCV, at n_estimators hører til classifier-trinnet i pipeline'en. Og n_jobs=-1 paralleliserer søgningen over alle tilgængelige CPU-kerner, hvilket kan gøre en mærkbar forskel i køretid.

Feature importance: Hvad driver forudsigelserne?

En model er kun rigtig nyttig, hvis du kan forstå den. Heldigvis giver Random Forest dig en indbygget feature importance, der viser hvilke features der bidrager mest:

# Træn pipeline og hent feature importances
pipeline.fit(X_train, y_train)
importances = pipeline.named_steps["classifier"].feature_importances_

# Sorter og vis
feature_imp = pd.Series(importances, index=wine.feature_names).sort_values(ascending=True)

fig, ax = plt.subplots(figsize=(10, 6))
feature_imp.plot(kind="barh", ax=ax, color="#3b82f6")
ax.set_title("Feature Importance – Random Forest")
ax.set_xlabel("Vigtighed")
plt.tight_layout()
plt.show()

print(f"\nTop 3 vigtigste features:")
for name, imp in feature_imp.tail(3).items():
    print(f"  {name}: {imp:.3f}")

Typisk vil flavanoids, color_intensity og proline dominere. Det giver faktisk god intuitiv mening – netop de kemiske egenskaber varierer mest mellem producenterne.

Gem og indlæs din model

Når du er tilfreds med din model, kan du gemme den til senere brug med joblib:

import joblib

# Gem den trænede pipeline
joblib.dump(pipeline, "vinmodel_pipeline.joblib")

# Indlæs igen senere
loaded_pipeline = joblib.load("vinmodel_pipeline.joblib")

# Test at den stadig virker
test_accuracy = loaded_pipeline.score(X_test, y_test)
print(f"Indlæst model accuracy: {test_accuracy:.2%}")

Hele pipeline'en gemmes – inklusiv skalering og model. Når du indlæser den igen, kan du kalde predict() direkte på rå data uden at bekymre dig om forbehandling. Det er den slags ting, der bare virker.

De vigtigste ting at huske

Her er en hurtig opsummering af de centrale principper:

  • Brug altid train/test-split – evaluer aldrig en model på data, den har set under træning
  • Skaler dine features – især for afstandsbaserede algoritmer som SVM og KNN
  • Brug pipelines – de forhindrer data leakage og gør koden reproducerbar
  • Krydsvalider – et enkelt train/test-split kan give et misvisende resultat
  • Start simpelt – prøv Random Forest eller Gradient Boosting først, inden du komplicerer tingene
  • Forstå din model – feature importance og confusion matrix er dine bedste venner

Ofte stillede spørgsmål

Hvad er forskellen mellem scikit-learn og TensorFlow/PyTorch?

Scikit-learn fokuserer på klassisk machine learning: beslutningstræer, random forest, SVM, lineær regression osv. TensorFlow og PyTorch er til deep learning med neurale netværk. Til de fleste dataprojekter med tabeldata (altså rækker og kolonner) er scikit-learn det rigtige valg. Deep learning giver primært en fordel ved billeder, tekst og lyd.

Hvor mange datapunkter skal jeg bruge?

Der er ingen fast grænse, men som tommelfingerregel bør du have mindst 10 gange så mange datapunkter som features til klassiske algoritmer. Med 13 features, som i vores vineksempel, er 178 datapunkter tilstrækkeligt. Generelt giver mere data bedre modeller – men kvaliteten af data er vigtigere end mængden.

Hvordan vælger jeg den rigtige algoritme?

Start med Random Forest eller Gradient Boosting til klassifikation og regression – de fungerer godt i langt de fleste situationer. Brug krydsvalidering til at sammenligne flere algoritmer på dine data. Scikit-learn har også et nyttigt algorithm cheat sheet, der kan hjælpe dig med at vælge baseret på datasættets størrelse og type.

Kan jeg bruge scikit-learn med store datasæt?

Scikit-learn fungerer bedst med datasæt, der passer i hukommelsen (op til et par GB). Til meget store datasæt kan du bruge partial_fit()-metoden på udvalgte algoritmer til inkrementel træning, eller skifte til biblioteker som Dask-ML eller Spark MLlib til distribueret beregning.

Hvad er data leakage, og hvordan undgår jeg det?

Data leakage opstår, når information fra testsættet utilsigtet bruges under træningen. Det giver urealistisk gode resultater, der desværre ikke holder i virkeligheden. Den bedste måde at undgå det? Brug scikit-learn pipelines, der automatisk sikrer at forbehandlingstrin kun fittes på træningsdata.

Om Forfatteren Editorial Team

Our team of expert writers and editors.