Feature engineering
Kunna skapa, koda och skala features, inklusive one-hot och normalisering.
Förkunskaper
Intuition
En modell ser bara tal. Feature engineering är att omvandla verkligheten till tal på ett sätt som gör mönstren synliga.
Kategoriska variabler:
| Metod | Hur | Passar |
|---|---|---|
| One-hot | en 0/1-kolumn per värde | få kategorier, linjära modeller |
| Ordinal | 0, 1, 2 … | när ordningen är verklig (låg/medel/hög) |
| Target encoding | ersätt med målets medelvärde i kategorin | många kategorier — men läcker lätt |
| Embedding | inlärd vektor | mycket många kategorier, neurala nät |
Fällan med ordinal kodning: koda «Malmö = 1, Lund = 2, Umeå = 3» så påstår du att Lund ligger mellan Malmö och Umeå och att Umeå är tre gånger Malmö. Det är nonsens, och en linjär modell kommer att tro på det.
Numeriska variabler skalas för att inte en variabel ska dominera bara för att den mäts i en större enhet:
| Metod | Formel | Resultat |
|---|---|---|
| Standardisering | medel 0, std 1 | |
| Min–max | intervallet [0, 1] | |
| Robust | tål avvikare | |
| Log | drar ihop en högersvans |
Formellt
Läckage är det stora felet i förbehandling, och det finns i tre former:
- Statistik från testdata. Beräkna och över hela datasetet → testmängdens fördelning har påverkat träningen. Rätt:
fitpå träning,transformpå båda. - Target encoding utan korsvalidering. Ersätter du en kategori med målets medelvärde använder du målet som feature. Utan out-of-fold-beräkning memorerar modellen sina egna etiketter.
- Features från framtiden. «Antal köp senaste månaden» beräknat över hela perioden innehåller information som inte fanns vid beslutstillfället. Det här är det svåraste att upptäcka och det vanligaste i tidsserieproblem.
Symtomet på läckage är alltid detsamma: misstänkt bra valideringsresultat som inte håller i produktion. En modell som plötsligt får 0,99 AUC har oftast inte blivit bra — den har fått se facit.
Pipeline löser problem 1 strukturellt genom att göra hela förbehandlingen till en del av modellen. Det är inte bara snyggare kod — det är den enda formen som överlever korsvalidering korrekt, eftersom fit då körs om inom varje veck.
Vilka modeller behöver skalning?
| Behöver | Behöver inte |
|---|---|
| kNN, SVM, k-means (avståndsbaserade) | beslutsträd |
| linjär/logistisk regression med regularisering | random forest |
| neurala nät | gradient boosting |
Träd delar på tröskelvärden och bryr sig inte om skalan. Allt som mäter avstånd eller straffar koefficienternas storlek gör det.
Domänkunskap slår automatik. De features som brukar göra störst skillnad är de någon som kan området föreslår: förhållanden (pris per kvadratmeter), tidsskillnader (dagar sedan senaste besök), och cykliska omkodningar (veckodag som sin/cos, så att söndag ligger nära måndag).
Kod
import numpy as np, pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
num = ["alder", "timmar"]
kat = ["stad", "program"]
forbehandling = ColumnTransformer([
("num", Pipeline([("fyll", SimpleImputer(strategy="median")),
("skala", StandardScaler())]), num),
("kat", Pipeline([("fyll", SimpleImputer(strategy="most_frequent")),
("onehot", OneHotEncoder(handle_unknown="ignore"))]), kat),
])
modell = Pipeline([("forbehandling", forbehandling),
("klassificerare", LogisticRegression(max_iter=1000))])
# Korsvalideringen kör om HELA förbehandlingen inom varje veck → inget läckage
print(cross_val_score(modell, X, y, cv=5, scoring="roc_auc").mean())
# handle_unknown="ignore" behövs: produktionsdata innehåller kategorier
# som inte fanns i träningen, och utan detta kraschar transform.
# Domänkunskap: cykliska variabler
df = pd.DataFrame({"veckodag": [0, 1, 5, 6]}) # 0 = måndag, 6 = söndag
df["dag_sin"] = np.sin(2 * np.pi * df.veckodag / 7)
df["dag_cos"] = np.cos(2 * np.pi * df.veckodag / 7)
# Avståndet mellan söndag (6) och måndag (0) blir nu litet, som det ska vara:
for a, b in [(6, 0), (0, 3)]:
va = np.array([np.sin(2*np.pi*a/7), np.cos(2*np.pi*a/7)])
vb = np.array([np.sin(2*np.pi*b/7), np.cos(2*np.pi*b/7)])
print(f"dag {a} → {b}: avstånd {np.linalg.norm(va - vb):.3f}")
# dag 6 → 0: avstånd 0.868
# dag 0 → 3: avstånd 1.950 ← söndag ligger nära måndag, som i verkligheten
Den cykliska kodningen är ett bra exempel på att en enkel idé ur domänkunskap ofta ger mer än en större modell.
Behärskning innebär
- Kodar kategoriska variabler
- Skalar numeriska variabler korrekt
- Undviker läckage i förbehandlingen
Logga in för att göra övningarna och bygga upp din behärskning.
Källor
- scikit-learn User Guide (BSD-3) — BSD-3-Clause
- pandas — User Guide (BSD-3) — BSD-3-Clause
- Dive into Deep Learning (CC BY-SA 4.0) — CC BY-SA 4.0