A Northstar prediktív feladatban a rendelés pillanatában kell
megbecsülnünk annak valószínűségét, hogy a szállítás a vállalt 7 napnál
később érkezik. A megfigyelési egység egy rendelés = egy sor az
orders.csv állományban. A bemenet jelen laborban két, a
rendeléskor ismert feature: distance_km távolság
kilométerben és basket_minor kosárérték EUR-centben. A
célváltozó a bináris target_late: 1, ha
delivery_days > promised_days, egyébként 0. A kimenet
predict_proba szerinti
becslés, amely 0 és 1 közé esik, és két osztályra összegezve 1-et
ad.
Induló becslés a megoldás előtt. A cutoff
2025-05-01T00:00:00+00:00; a rendelések 2025 első 180
napjára esnek. Becsüld meg nagyságrendileg, hány train és hány teszt
sorra számítasz! Második kérdés: a C=0.2 vagy a
C=5 jelent erősebb L2 regularizálást a
LogisticRegression esetén? A választ a fogalmi rész után
ellenőrizzük.
A prediktív lineáris modell egy dimenziós feature-vektorból indul: az -edik megfigyelés, a cél folytonos esetén
A legkisebb négyzetek (ordinary least squares, OLS) a négyzetes hibát minimalizálja. A Ridge ehhez a vektor euklideszi normájának négyzetét büntetésként hozzáadja:
Itt a tanítósorok száma, a regularizálási erő. A Lasso ugyanezt az normával teszi:
A scikit-learn Lasso a fenti
-es
skálázást használja, a Ridge az első képlet szerinti
formát; ezért ugyanaz az
abszolút értéke a két modellnél nem közvetlenül egyenértékű. A fontos
irány közös: nagyobb alpha erősebb zsugorítás (shrinkage)
nullához. A büntetés a fit_intercept=True alapbeállítás
mellett az interceptet nem bünteti.
A és közötti különbség a Vektor, skalárszorzat, norma és távolság alfejezetből ismert távolságfogalomra épül. A gradiensszámítás szempontjából (Optimalizáló választás és konvergenciaellenőrzés) az L2-büntetés -t ad a gradienshez, ami minden lépésben arányosan húzza az együtthatót 0 felé; az L1-büntetés csak előjelét adja hozzá, ezért a megoldás egyes koordinátái pontosan 0-vá válhatnak. A Ridge jellemzően minden együtthatót kicsivé, de nem nullává tesz; a Lasso ritka megoldást adhat.
Bináris cél esetén a LogisticRegression a logisztikus
függvényen (sigmoid) keresztül köti a lineáris prediktort a
valószínűséghez:
ahol . A a logit, azaz a log odds:
A nem regularizált változat a negatív log-likelihoodot minimalizálja;
a likelihood fogalmát Paraméter, becslő és
likelihood vezette be, a GLM-keretet pedig GLM:
bináris és számlálási cél. Az sklearn regularizált
logisztikus célfüggvénye L2 büntetéssel, fogalmi alakban:
A LogisticRegression C paramétere tehát a
negatív likelihood súlya, ezért kisebb C erősebb
regularizálást jelent. Ez fordítottja a Ridge/Lasso
alpha irányának. A penalty='l2' a fenti
négyzetes büntetés, penalty='l1' az
-es
ritkító változat; penalty=None kikapcsolja a büntetést. A
modern scikit-learn a None értéket használja; ha a
telepített verzió hibát ad, a korábbi penalty='none'
szöveget kell ellenőrizni.
| Modellcsalád | scikit-learn paraméter | Paraméter növelésének hatása |
|---|---|---|
Ridge / Lasso |
alpha |
erősebb büntetés |
LogisticRegression |
C |
gyengébb büntetés (inverz) |
A coef_ a tanult
együtthatóvektor. Folytonos célú Ridge/Lasso
esetén coef_.shape == (p,); bináris
LogisticRegression esetén
coef_.shape == (1, p). Az utóbbi együttható a standardizált
feature egy egységnyi növekedéséhez tartozó log odds változás;
exponense,
,
odds ratio (OR). A basket_minor centben való megadása miatt
a nyers együttható nem hasonlítható össze a distance_km
együtthatójával: a büntetés nagysága mértékegységfüggő lenne. Ezért a
labor a StandardScaler-t tanítja a train halmazon, és azt
transzformálja a tesztre. A coef_ ezután „egy szórásnyi
feature-változásra” jutó hatást jelent.
Kézi kontroll. Legyen egy standardizált megfigyelés , , , , . Ekkor
Egy egységnyi növekedés log odds növekedést, azaz -szeres oddsot jelent.
A példa a lab/ munkakönyvtárból fut, ahol az
orders.csv rendelkezésre áll. A felosztás a Predikciós szerződés és hibateszt alfejezetben
rögzített szerződést követi: train = order_ts < cutoff
és label_available_at < cutoff; teszt =
order_ts >= cutoff. A köztes sáv (korábbi rendelés, még
nem kézbesített címke) kimarad. Nincs véletlen split, ezért a cutoff
maga a reprodukálhatósági rögzítő; a random_state csak a
solver jövőbeli konzisztenciáját jelzi.
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score, brier_score_loss
orders = pd.read_csv(
Path("orders.csv"),
parse_dates=["order_ts", "actual_delivery_at", "label_available_at"],
)
assert {"distance_km", "basket_minor", "target_late",
"order_ts", "label_available_at"}.issubset(orders.columns)
CUTOFF = pd.Timestamp("2025-05-01T00:00:00+00:00")
FEATURES = ["distance_km", "basket_minor"]
TARGET = "target_late"
train = orders[(orders["order_ts"] < CUTOFF) &
(orders["label_available_at"] < CUTOFF)]
test = orders[orders["order_ts"] >= CUTOFF]
purged = orders[(orders["order_ts"] < CUTOFF) &
(orders["label_available_at"] >= CUTOFF)]
print(f"train={len(train)}, test={len(test)}, purged={len(purged)}")
assert set(train.index).isdisjoint(set(test.index))
X_train, y_train = train[FEATURES], train[TARGET]
X_test, y_test = test[FEATURES], test[TARGET]
def make_pipeline(penalty, C=1.0):
if penalty is None:
model = LogisticRegression(penalty=None, solver="lbfgs",
max_iter=1000, random_state=42)
else:
model = LogisticRegression(penalty=penalty, C=C, solver="lbfgs",
max_iter=1000, random_state=42)
return Pipeline([
("scaler", StandardScaler()),
("model", model),
])
pipe_unreg = make_pipeline(None)
pipe_l2 = make_pipeline("l2", 0.2)
results = {}
for name, pipe in [("penalty=None", pipe_unreg),
("penalty=l2, C=0.2", pipe_l2)]:
pipe.fit(X_train, y_train)
proba = pipe.predict_proba(X_test) # shape: (n_test, 2)
p_test = proba[:, 1] # P(target_late=1)
assert np.allclose(proba.sum(axis=1), 1.0)
results[name] = (pipe, p_test)
print(f"{name}: AUC={roc_auc_score(y_test, p_test):.4f}, "
f"Brier={brier_score_loss(y_test, p_test):.4f}")
print(" coef (scaled):",
np.round(pipe.named_steps["model"].coef_[0], 4),
" intercept:",
round(pipe.named_steps["model"].intercept_[0], 4))
# Kézi logit-ellenőrzés a regularizált modellen
pipe_reg, p_test_reg = results["penalty=l2, C=0.2"]
X_test_scaled = pipe_reg.named_steps["scaler"].transform(X_test)
reg_model = pipe_reg.named_steps["model"]
z_manual = X_test_scaled @ reg_model.coef_[0] + reg_model.intercept_[0]
p_manual = 1.0 / (1.0 + np.exp(-z_manual))
print("max |p_manual - predict_proba|:",
np.max(np.abs(p_manual - p_test_reg)))Futtatás a lab/ mappában:
python s085_demo.pyVerzióérzékeny döntés: ha a telepített scikit-learn a
penalty=None értéket nem fogadja el, használd a
penalty='none' szöveges formát, és ellenőrizd, hogy a
lbfgs solver elérhető.
A fenti futásból származó számok szemléltető, elméletileg
várt értékek; automatizált futási ellenőrzés még nem történt.
Az várható, hogy a train nagyságrendileg 1500 sor, a teszt
nagyságrendileg 800 sor; a purged sáv nagyságrendileg a
cutoff előtti utolsó 7 nap rendeléseit tartalmazza. Az ellenőrző
invariantok: a predict_proba minden sora 1-re összegződik,
shape-je (len(X_test), 2); a kézi logit-számítás és a
predict_proba eltérése lebegőpontos tolerancián belül,
jellemzően
alatt marad.
A két modell összehasonlításában az L2 büntetés a maximum
likelihoodhoz képest nullához húzza az együtthatókat. A
penalty=None tehát jellemzően nagyobb abszolút értékű
együtthatókat ad, míg a C=0.2 erős büntetés kisebb
együtthatókat eredményez. A distance_km együtthatója
várhatóan pozitív: nagyobb távolság mellett nagyobb késési kockázat. A
basket_minor hatása gyengébb lehet, mert a kosárérték nem
feltétlenül a logisztikai késés fő mozgatója. A regularizálás
nem garantál jobb teszt AUC-t; akkor hasznos, ha
csökkenti a maximum likelihood nagy mintavételi varianciáját anélkül,
hogy túl nagy torzítást vinne be. Az AUC és a Brier itt a
modellváltozatok közötti gyors összehasonlítást szolgálja, nem a
kalibráció teljes értékelését.
A logisztikus regresszióból származó predict_proba
modellszerinti valószínűségbecslés; nem tekinthető automatikusan jól
kalibrált eseményvalószínűségnek. A kalibrációt és az osztályarány
eltolódását külön ellenőrizzük.
Értelmezés. Tegyük fel, hogy egy standardizált
futásban a LogisticRegression distance_km
együtthatója
.
Mekkora az odds ratio? Ha ugyanez a feature csak
szórásnyival nő, mennyi a log odds változás? Fogalmazd meg, miért nem
oksági hatás ez az állítás!
Megvalósítás. Ugyanezen a spliten, ugyanazzal a
két feature-rel taníts folytonos célra (delivery_days)
három modellt: LinearRegression,
Ridge(alpha=1.0) és
Lasso(alpha=0.1, max_iter=10000). Mindegyiket
StandardScaler-rel egy Pipeline-ben futtasd.
Számold a train és teszt RMSE-t, és írd ki a coef_ vektort.
Milyen reláció várható a Ridge és a LinearRegression együtthatói
között?
Hibakeresés. A következő kód a standardizálást a
teljes adaton végzi, mielőtt a Pipeline-t
meghívná:
from sklearn.preprocessing import StandardScaler
full = pd.concat([X_train, X_test])
X_all_scaled = StandardScaler().fit_transform(full[FEATURES])
# majd a scaled mátrixot használja a modellekhezMi a hiba, és milyen irányban torzíthatja a teszt AUC-t? Írj két ellenőrző számítást: hasonlítsd össze a train-only és a full scaler átlagvektorát!
priority_flag és quantity_sum oszlopokkal. A
C értékét válaszd 0.01, 0.2 és
10 értékekre. Melyiket választanád a teszteredmény alapján?
Miért helytelen ez a döntési sorrend? Hogyan választanál C-t a train
halmazon belüli időrendi validációval?1. megoldás. Az odds ratio . A szórásnyi növekedés log odds változása , az odds ratio . Ez prediktív asszociáció: a modell a rendeléskori jellemzők és a késés között tanul összefüggést, de a távolság nem feltétlenül beavatkozás; a zavaró tényezők elemzése az Oksági cél és megfigyelési torzítás alfejezetben szereplő oksági keretre tartozik.
2. megoldás. A kód a következő lehet:
from sklearn.linear_model import LinearRegression, Ridge, Lasso
target_continuous = train["delivery_days"]
ytest_continuous = test["delivery_days"]
for name, model in [
("ols", LinearRegression()),
("ridge", Ridge(alpha=1.0)),
("lasso", Lasso(alpha=0.1, max_iter=10000)),
]:
pipe = Pipeline([
("scaler", StandardScaler()),
("model", model),
])
pipe.fit(X_train, target_continuous)
pred_train = pipe.predict(X_train)
pred_test = pipe.predict(X_test)
rmse_train = np.sqrt(np.mean((target_continuous - pred_train) ** 2))
rmse_test = np.sqrt(np.mean((ytest_continuous - pred_test) ** 2))
print(f"{name}: train RMSE={rmse_train:.3f}, "
f"test RMSE={rmse_test:.3f}, "
f"coef={pipe.named_steps['model'].coef_}")Az invariánsok: coef_.shape == (2,); a Ridge együtthatói
abszolút értékben kisebbek vagy egyenlők, mint a büntetés nélküli OLS
megfelelői; a Lasso egyes együtthatója
-vá
válhat. A Ridge train RMSE-e általában nem kisebb az OLS-nél, mert a
büntetés a train illeszkedésből vesz el; a teszt RMSE változása az
adattól függ.
3. megoldás. A hiba a tesztadatból való szivárgás (leakage): a scaler a teszt átlagát és szórását is felhasználja, tehát a teszt standardizált értékei nem függetlenek a teszt megfigyelésektől. A teszt AUC jellemzően optimista lehet. Ellenőrzés:
full_scaler = StandardScaler().fit(pd.concat([X_train, X_test])[FEATURES])
train_scaler = StandardScaler().fit(X_train)
print(np.max(np.abs(full_scaler.mean_ - train_scaler.mean_)))
print(np.max(np.abs(full_scaler.scale_ - train_scaler.scale_)))Ha a két vektor gyakorlatilag egyezik, a szivárgás kicsi; normál
esetben a különbség nem nulla, és a helyes megoldás a scaler
fit-je kizárólag X_train-en, ahogy a
Pipeline a fenti fő példában teszi. Ez a Preprocessing tanulás kizárólag a training részen
alapelve.
4. megoldás. A teszt AUC alapján választani hibás,
mert a teszt addig érintetlen kell maradjon. A C
választásához a train halmazt időrendben bontsd tovább: az utolsó 20%-ot
validációra hagyva.
val_cutoff = train["order_ts"].quantile(0.8)
early_train = train[train["order_ts"] <= val_cutoff]
early_val = train[train["order_ts"] > val_cutoff]A C rácson a validációs AUC alapján válassz; a végső
teszt AUC-t csak egyszer, a kiválasztott modellel számold. A
C=0.01 erős büntetés, alulillesztést okozhat; a
C=10 gyenge büntetés, nagyobb varianciát enged. A három
érték közötti rangsor nem garantálható előre: az optimális C adatfüggő.
A keresztvalidáció és a nested kiértékelés a Hiperparaméter és nested értékelés alfejezetben
lesz részletesebb.
A statsmodels inferenciális outputja és a scikit-learn prediktív
estimator nem ugyanazt a célt szolgálja. A statsmodels
Logit összefoglaló táblája standard hibát, z-értéket és
p-értéket ad, amelyek akkor értelmesek, ha a modell feltevései és az
adatfelvételi folyamat igazak. A scikit-learn
LogisticRegression ezzel szemben prediktív illesztő
felület: a coef_ a büntetett célfüggvény optimuma, és a
C a predikciós kompromisszumot állítja.
Ellenpélda: egy elemző a statsmodels p-értéke alapján elhagyja a
basket_minor változót, mert p > 0.05. A predikciós
modellben ettől még a változó megtartása csökkentheti a teszt
Brier-értékét, mert a p-érték nem predikciós fontossági mutató, és a nem
szignifikáns eredmény nem bizonyítja a hatás hiányát (Nullmodell és p-érték). A reverse hiba is gyakori:
a sklearn coef_ nagyságából közvetlen oksági vagy
inferenciális állítást tenni. A coef_ a modell
reprezentációjának része, nem a világ becsült oksági paramétere.
Önellenőrzés. (1) Miért változik meg egy
Ridge-együttható, ha a basket_minor-t cent helyett euróban
adod meg? (2) A C=0.01 vagy a C=10 ad nagyobb
büntetést a logisztikus modellben? (3) Miért kell a standardizáló
átlagát és szórását a train halmazon belül tanítani? (4) Mit jelent, ha
a LogisticRegression coef_[0, 1] értéke 0.1
standardizált basket_minor esetén? A válaszokhoz térj
vissza a fenti képletekhez és a pipeline-művelethez.
A Ridge az L2, a Lasso az L1 büntetéssel zsugorítja a lineáris modell
együtthatóit; a scikit-learnben nagyobb alpha erősebb
büntetést jelent. A logisztikus regresszió a logit linkkel becsüli a
bináris kimenet valószínűségét, és a negatív log-likelihoodhoz
hozzáadott büntetést a C paraméter súlyozza: kisebb
C erősebb regularizálás. A coef_ a
standardizált feature-változás log odds hatását adja, ezért a skálázást
a train halmazon belül kell tanítani. A következő alkalmazásban ezt a
lineáris feltevést fák, random forest és gradiensboosting modellekkel
vetjük össze, amelyek nemlineáris határokat és interakciókat is képesek
reprezentálni.