A Hibaszeletek és felelős értékelés
alfejezetben a modell hibáit szeletekre bontva vizsgáltuk. Most más
irányból kérdezünk: meddig bízhatunk a modellben, ha a jövőbeli adat
vagy a mérési környezet eltolódik? A Northstar rendeléskori késésmodellt
használjuk; a megfigyelési egység az order_id, a bemenet
egy rendelés pillanatában ismert feature-sor, a cél a
target_late indikátor (1, ha
delivery_days > 7). A már rögzített teszthalmazon két
perturbációt szimulálunk: a south régió eltűnését és a
distance_km +15%-os mérési eltolódását. Az eredmény egy
stresszteszt-jegyzőkönyv és egy kitöltött modellkártya. Előzetes
becslés: a kategóriaeltűnés kisebb, a distance-eltolódás nagyobb log
loss változást okoz, mert a távolság erősebb prediktor; a tényleges
értéket a tanuló gépe adja.
Robusztusság (robustness). A modell robusztussága azt méri, mennyire marad stabil a teljesítménye, ha a bemenet eloszlása, mérési skálája vagy hiányzási mintázata megváltozik. Rögzített teszthalmazon a referencia log loss:
ahol a teszt sorainak száma, a tényleges címke, a prediktált késési valószínűség. A perturbált adatra számított különbsége . Az érzékenységi mutatót a perturbáció nagyságához viszonyítjuk: , ahol a relatív eltolódás, például . A log loss mértékegysége nat; mértékegysége nat/szorzó. A csak a specifikált perturbációra adott mért válasz, nem általános garancia.
Hiányzási mintázat eltolódása (missingness shift). A
hiányzó értékek arányának vagy mechanizmusának megváltozása a scoring
időben. Ha a tanítóban a distance_km hiánytalan, a pipeline
mediánnal pótol; ha a scoringban a hiány pont a nagy távolságú
rendeléseknél jelenik meg, a medián alábecsüli a valódi értéket. Kézi
kontroll: három tanító távolság 10, 20, 30 km, a medián 20. A scoringban
10, 20, hiány, a hiányzó valódi értéke 50; a pótolt 20, tehát a modell
30 km-rel rövidebb utat lát. A hiányzás okának azonosítása a Hiány és anomália mint vizsgálati kérdés
adatminőségi vizsgálata, nem a modell feladata; a modell csak a pótlási
szabályt örökli.
Sémaváltozás (schema change). A bemeneti oszlopok
nevének, típusának, kategóriáinak vagy mértékegységének megváltozása.
Példák: új region érték, a basket_minor string
dtype, a distance_km mérföldben. A scikit-learn
OneHotEncoder paramétere dönt:
handle_unknown="error" hibát dob,
handle_unknown="ignore" csendben nullázza az ismeretlen
dummy oszlopokat, és a sort a referencia szintre teszi. A Feature store és adatkontraktus szükségessége
adatszerződése az első védvonal; a sémavalidációt a predict
előtt kötelező lefuttatni.
Stresszteszt. Meghatározott perturbációk sorozata, amelynek célja a töréspont megtalálása, nem a jövőbeli garancia. Példa: a distance_km szorzó mellett kiszámítjuk a log loss-t; ha -nál meghaladja az előre rögzített bevezetési küszöböt, a kártya rögzíti: „30%-os distance-eltolódás nem támogatott”.
Modellkártya (model card). Strukturált dokumentáció: azonosító, rendeltetés, bemeneti és kimeneti szerződés, tanítási adat és cutoff, referenciametrika, stressztesztek, alkalmazási tartomány, nem támogatott használat, korlátok. A kártya a felelős átadás eszköze, nem a kód helyettesítője.
| Jelenség | Mi változik | Northstar példa | Első védvonal |
|---|---|---|---|
| missingness shift | hiányzási mechanizmus | distance_km 10% hiány a nagy távolságoknál |
adatminőségi monitor, imputálási jegyzőkönyv |
| schema change | oszlop, dtype, kategória | új region érték, forintban érkező
basket_minor |
bemeneti sémavalidáció |
| measurement shift | mérési skála | distance_km +15% |
kalibráció, eloszlás-ellenőrzés |
A mérési eltolódás kézi kontrollja: ha a tanító
distance_km átlaga
és szórása
,
akkor egy
távolság standardizált értéke
.
A
szorzójú perturbáció után
,
a logit változása
,
ahol
a distance együtthatója a logisztikus modellben. Ha
,
a nagyobb távolság nagyobb késési valószínűséget ad.
A lab/ projektgyökérből futtatandó script a szerződés
szerinti fájlokat használja. A kód a splitet maga számítja a cutoff és a
label-availability szabály alapján, így nem igényel ismeretlen
artifactot. A random_state=42 a logisztikus optimalizáló
indulását rögzíti; az adatgeneráló seedje 20261020, a kettő külön
szerep.
# s104_robustness.py -- futtatás a lab/ projektgyökérből:
# python s104_robustness.py
import json
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import log_loss, roc_auc_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
# 1. Betöltés és feature-összerakás
orders = pd.read_csv("data/orders.csv", parse_dates=["order_ts", "actual_delivery_at",
"label_available_at"])
customers = pd.read_csv("data/customers.csv", parse_dates=["signup_date"])
df = orders.merge(customers[["customer_id", "signup_date"]], on="customer_id")
df["account_age_days"] = (df["order_ts"] - df["signup_date"]).dt.days
# target_late utólag ismert, prediktorból mindig kizárva; itt kizárólag tanító címke.
feature_cols = ["distance_km", "priority_flag", "basket_minor",
"quantity_sum", "account_age_days"]
category_cols = ["segment", "region", "acquisition_channel"]
# 2. Időbeli split a szerződés alapján
cutoff = pd.Timestamp("2025-05-01T00:00:00+00:00")
train = df[(df["order_ts"] < cutoff) & (df["label_available_at"] < cutoff)].copy()
test = df[df["order_ts"] >= cutoff].copy()
# purged: order_ts < cutoff, de label_available_at >= cutoff; kimarad a példából.
X_train, y_train = train[feature_cols + category_cols], train["target_late"]
X_test, y_test = test[feature_cols + category_cols], test["target_late"]
# 3. Pipeline és tanítás
preprocessor = ColumnTransformer([
("num", StandardScaler(), feature_cols),
("cat", OneHotEncoder(drop="first", handle_unknown="ignore"), category_cols),
])
model = Pipeline([
("prep", preprocessor),
("clf", LogisticRegression(max_iter=1000, random_state=42)),
])
model.fit(X_train, y_train)
# 4. Referencia metrika
p_ref = model.predict_proba(X_test)[:, 1]
ref = {"auc": roc_auc_score(y_test, p_ref),
"log_loss": log_loss(y_test, p_ref)}
# 5. Stresszteszt 1: south régió eltűnik a scoringból
X_region = X_test.copy()
X_region["region"] = X_region["region"].replace("south", "central")
p_reg = model.predict_proba(X_region)[:, 1]
region_result = {"auc": roc_auc_score(y_test, p_reg),
"log_loss": log_loss(y_test, p_reg)}
# 6. Stresszteszt 2: distance_km mérési eltolódás +15%
X_dist = X_test.copy()
X_dist["distance_km"] = X_dist["distance_km"] * 1.15
p_dist = model.predict_proba(X_dist)[:, 1]
dist_result = {"auc": roc_auc_score(y_test, p_dist),
"log_loss": log_loss(y_test, p_dist)}
print(f"test rows: {len(X_test)}")
print(f"reference: AUC={ref['auc']:.3f} log_loss={ref['log_loss']:.3f}")
print(f"region removed: AUC={region_result['auc']:.3f} "
f"log_loss={region_result['log_loss']:.3f}")
print(f"distance +15%: AUC={dist_result['auc']:.3f} "
f"log_loss={dist_result['log_loss']:.3f}")
# 7. Kézi invariant-ellenőrzés
assert X_region.shape == X_test.shape
assert X_dist.shape == X_test.shape
assert X_region["region"].nunique() == 2 # south valóban eltűnt
np.testing.assert_allclose(X_dist["distance_km"].mean(),
X_test["distance_km"].mean() * 1.15, rtol=1e-12)
print("shape és átlag-skála invariant rendben")A kimenet szemléltető, nem e könyv futási jegyzéke; a tanuló gépén az értékek eltérhetnek:
test rows: 786
reference: AUC=0.72 log_loss=0.41
region removed: AUC=0.71 log_loss=0.42
distance +15%: AUC=0.65 log_loss=0.49
shape és átlag-skála invariant rendben
Eredmény értelmezése. A south régió
eltűnése kicsi
értéket ad, mert a régió gyenge prediktor; a +15%-os distance-eltolódás
nagyobbat, mert a távolság a késés fő mozgatója. A shape-invariant
garantálja, hogy nem véletlenül dobtunk ki sorokat; a distance-átlag
1,15-szeresre növekedése a mérési perturbáció pontos betartását
ellenőrzi. A log loss növekedése itt a kalibrálatlanság és a rosszabb
rangsorolás keveréke; a két komponenst a Hibaszeletek és felelős értékelés szeletelt vagy a
kalibrációs görbe elemzés külön vizsgálhatja.
A fenti két mérésből kitöltött modellkártya vázlata:
northstar_late_v3,
2026-10-20.distance_km (km),
priority_flag (0/1), basket_minor (EUR-cent),
quantity_sum (db), account_age_days (nap),
segment, region,
acquisition_channel; cutoff: 2025-05-01.south régió eltűnése
;
distance_km +15%
(szemléltető).distance_km mérföldben;
basket_minor forintban; hiányzó distance_km 5%
feletti scoring arány; oksági következtetés a szállítási díj
hatásáról.A kártya „Nem támogatott használat” mezője konkrét, mérhető feltételeket tartalmaz; az általános jogi szöveg nem helyettesíti a technikai határt.
np.random.default_rng(7) használatával) állítsa a
distance_km értéket NaN-re, a numerikus ágba
tegyen SimpleImputer(strategy="median") lépést a
StandardScaler elé, majd számítsa ki az AUC-t és log loss-t
a referencia értékekkel együtt. A medián a teljes tanító
distance_km oszlopából származzon.island régió. A pipeline
OneHotEncoder(handle_unknown="error") beállítással fut. Mi
történik a predict hívásnál? Adjon diagnózist és két
lehetséges, dokumentált megoldást a modellkártya szempontjából.distance_km értéket mérföldben küldeni (1 mérföld = 1,609
km). A kártya melyik mezője tiltja ezt, és milyen bemeneti ellenőrzést
kell bevezetni, hogy a hiba ne jusson el a modellhez? Adjon egy konkrét
drift-ellenőrző szabályt a tanító medián és a scoring medián
alapján.1. megoldás. A stresszteszt egy rögzített
perturbációra adott mérés; a jövőbeli eltolódások tere nem fedhető le
véges teszttel. Ellenpélda: a basket_minor értékek
forintban kezdenek érkezni a scoringba. A tanító
StandardScaler euró-cent eloszlásra illeszkedett, ezért a
forintban érkező, nagyságrendileg 400-szoros értékek a numerikus
feature-öket szélsőséges standardizált értékekké teszik, a logisztikus
modell predikciói 0 vagy 1 közelébe torzulnak, és a log loss
drasztikusan nő. A stresszteszt nem tartalmazta ezt a skálaváltást,
ezért nem bizonyíték rá.
2. megoldás. A numerikus ág módosítása:
from sklearn.impute import SimpleImputer
numeric_pipe = Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler()),
])
preprocessor = ColumnTransformer([
("num", numeric_pipe, feature_cols),
("cat", OneHotEncoder(drop="first", handle_unknown="ignore"), category_cols),
])A hiányzó teszt előállítása:
rng = np.random.default_rng(7)
X_missing = X_test.copy()
mask = rng.random(len(X_missing)) < 0.10
X_missing.loc[mask, "distance_km"] = np.nan
p_miss = model.predict_proba(X_missing)[:, 1]
missing_result = {"auc": roc_auc_score(y_test, p_miss),
"log_loss": log_loss(y_test, p_miss)}Az ellenőrző invariant: mask.sum() körülbelül a sorok
10%-a, és a nem maszkolt sorok distance értékei változatlanok. A log
loss várt iránya: a mediánnal pótolt hiányzó értékek szisztematikusan
alábecsülik a nagy távolságú rendeléseket, ezért a modell kevésbé
valószínűsíti a késést azoknál a soroknál, a log loss általában nő; a
pontos mértéket a tanuló saját futása adja. A túl alacsony imputálási
aránynál a változás nem szignifikáns, ezért a kártya a scoring 5%
feletti hiányzást eleve tiltja.
3. megoldás. Az OneHotEncoder.transform
ValueError kivételt dob, mert a scoringban olyan kategória
jelenik meg, amelyet a tanító nem látott. A diagnózis: sémaváltozás, nem
pedig modellhiba. Két dokumentált megoldás: (a) a bemeneti validátor
elutasítja az ismeretlen kategóriájú sort, és a rendelést emberi
felülvizsgálatra küldi; ekkor a modell csak a támogatott tartományon
prediktál. (b) Ha az üzleti döntés az ismeretlen kategória lenémítása,
akkor handle_unknown="ignore" beállítást használunk, és a
modellkártya rögzíti: „Az ismeretlen régió a referencia szintre kerül; a
predikció félrevezető lehet, ezért az ilyen sorokat külön riportálni
kell.” A csendes váltás dokumentáció nélkül azt a hamis látszatot kelti,
hogy a modell támogatja az új régiót.
4. megoldás. A kártya Bemeneti szerződés
mezője tiltja: distance_km numerikus, kilométerben,
pozitív; a tanító mediánt és IQR-t a futtatás nyomtatja. A bemeneti
ellenőrzés a scoring első 500 sorában kiszámítja a mediánt; ha
akkor a scoring leáll és riaszt, mert a feltételezett azonos skála sérül. A mérföldes adat -szeres mediánt adna, a log-hányados körülbelül 0,48, ami azonnal kiszűri. Az Adat- és predikciómonitoring címke nélkül monitoring ezt a szabályt automatizálja a batch folyamatban.
A leggyakoribb hiba a stresszteszt zöld eredményének általános garancialként kezelése. A Modellcsalád választása tudományos jegyzőkönyvvel modellcsalád-választásnál azt tanultuk, hogy a tesztet egyszer használjuk a kiválasztott modell jelentéséhez; a stresszteszt ugyanígy csak a specifikált perturbációról ad bizonyítékot. Ha a +15% distance-teszt elfogadható, az nem jelenti, hogy a modell a +25%-os eltolódásra, a forintban küldött kosárértékre vagy az új régióra is megbízható. A kártya „Ki nem próbált” szekciója ezért kötelező; a modell akkor felelős átadású, ha pontosan megmondja, mit nem tud.
Önellenőrzés. Ellenőrizze, hogy a perturbált teszt shape-je egyezik a referencia teszttel, a célvektor érintetlen maradt, a perturbáció csak a megcélzott oszlopot változtatta, és a modellkártya tiltásai konkrét, mérhető feltételeket tartalmaznak-e. Ha a kártya csak „általános környezeti változásra” hivatkozik mérés nélkül, az nem teljesíti a feladatot.
A robusztussági tesztben a perturbáció nagysága és a modellhibát mérő küszöb előre rögzítendő. A szintetikus régiótörlés nem bizonyít teljesítményt valódi eloszláseltolódás mellett.
A robusztussági jegyzőkönyv azt méri, hogyan változik a referencia log loss specifikált perturbációk hatására: a régió eltűnése kicsi, a +15%-os distance-eltolódás nagyobb értéket ad. A missingness shift, a séma- és mértékegység-változás külön meghibásodási mód; mindegyikhez konkrét bemeneti validáció tartozik. A modellkártya ezeket a határokat a „Nem támogatott használat” mezőben rögzíti, így a jövőbeli felhasználó nem feltételezi a nem igazolt tartományt. A stresszteszt nem bizonyíték minden eloszlásváltozásra, csak a felsorolt perturbációra. A következő lépésben a Szöveges baseline és ritka mátrix szöveges baseline-oknál ugyanezt a gondolatot alkalmazzuk a szöveg-előfeldolgozás sérüléseire.
További olvasmány és elsődleges dokumentáció: [SklearnCalibration] scikit-learn Calibration, https://scikit-learn.org/stable/modules/calibration.html (hozzáférés: 2026-10-21).