A VII. ellenőrző pont azt a pillanatot modellezi, amikor a data scientistnek nem algoritmust kell választania, hanem tudatosan döntenie kell a reprezentáció, az értelmezhetőség, a bizonytalanság, az értékelés, a számítási költség és az adathatár között. Három beérkező kérést vizsgálunk:
target_late bináris változó. A split a purged time-based
cut: order_ts < cutoff és
label_available_at < cutoff a tanítóhalmaz,
order_ts >= cutoff a teszt. Előzetes becslés: a konstans
modell log loss-ja a várható késési ráta függvényében nagyjából 0,50
körül van; egy értelmes logisztikus regresszió ennél jobbnak kell
lennie, de a javulás mértékét csak a tanítás után tudjuk.bug, feature_request,
account). Megfigyelési egység a jegy. Nincs előre
betanított nagy modell; a kérdés, hogy a TF-IDF + logisztikus baseline
elegendő-e, vagy embedding szükséges. Előzetes becslés: ha a szókincs
jól szeparál, a macro-F1 0,70 fölött lehet, de ezt a baseline
igazolja.orders táblájából aggregált napi rendelésszámot
kell előrejelezni 7 napra. Megfigyelési egység a nap, a cél a következő
napok összrendelésszáma. Előzetes becslés: a naiv prediktor (a jelenlegi
napi átlag) pontossága erős alapvonal; a neurális modellnek ezt kell
meghaladnia.A feladatunk: mindháromhoz módszer- és környezetválasztási lapot készíteni, P1-hez kis futtatható baseline-t, P2-höz és P3-hoz ellenőrizhető kísérleti tervet adni. A döntés kritériumai nem az “újdonság”, hanem a hat dimenzió.
Representation (reprezentáció). Mi a modell bemenete
és milyen formában? Tabuláris adatnál a jellemzővektor:
distance_km, priority_flag,
basket_minor, stb. Szövegnél token-számláló mátrix vagy
sűrű embedding. Idősornál csúszó ablakból képzett vektor. A
reprezentáció nem semleges: a szöveg szókincse nagy és ritka, ezért
TF-IDF mellett ritka mátrixot használunk; a tabuláris
adatnál a numerikus jellemzők közvetlenül bemenetek. Egy jó
reprezentációval egyszerűbb modell is elegendő lehet.
Példa: a basket_minor pénzérték nagyságrendje nagy (pl.
50 000 cent), a distance_km kicsi (pl. 2–120).
Standardizálás nélkül a logisztikus regresszió optimalizálása lassan
konvergál. A reprezentáció itt a skálázást is jelenti.
Interpretability (értelmezhetőség). A döntéshozó
számára fontos lehet, hogy miért késik a rendelés. A logisztikus
regresszió koefficiensei közvetlenül magyarázhatók: ha a
distance_km koefficiense pozitív, a távolság növeli a késés
valószínűségét. A többrétegű neurális háló súlyai nem közvetlenül
értelmezhetők; magyarázathoz gradiens-alapú vagy perturbációs módszer
kell. Az értelmezhetőség nem abszolút tulajdonság, hanem a fogyasztó
kérdésétől függ.
Uncertainty (bizonytalanság). A modell kimenete egy $ \hat{p} $ becsült valószínűség, de ez önmagában nem mondja meg, mennyire megbízható a becslés új bemenetre. A kalibrációs görbe azt méri, hogy a modell 0,7-es valószínűségű jóslatai valóban 70%-ban helyesek-e. A konformális predikció szűkebb/tágabb intervallumot adhat a bizonytalanságra. Az LLM-eknél a nem-determinizmus külön mintavételi bizonytalanságot visz be, amit a [Adatkezelés és reprodukálhatóság külső modellnél] megismételt futtatásokkal kezelt.
Evaluation (értékelés). A metrika és a split a feladathoz igazodik. Ritka eseménynél az accuracy félrevezető: ha a késési ráta 10%, egy „mindig időben” modell 90%-os pontosságú, de használhatatlan. A log loss a valószínűségi kalibrációt bünteti, a ROC-AUC a rangolást méri. Az időbeli feladatnál a [VI. ellenőrző pont: szivárgásmentes modellkísérlet]-ben tanult purged split szükséges, nem véletlenszerű keresztvalidáció. És a tesztkészletet a döntéshozatal nem érintheti, különben az értékelés optimista.
Compute (számítási költség). A Northstar 2400 rendelése egy CPU-n milliszekundum alatt tanítható. A GPU bevezetése ilyen adatméreten nem gyorsítás, hanem adminisztratív terhelés. A [Thread, process és könyvtári szálak] és [Optimálás változatlan jelentéssel] tanulsága: a mérési terv nélküli „gyorsítsunk” nem eredmény. Számítási költségnek tekintjük a paraméterszámot, memóriát, betanítási időt és az új hardver karbantartási költségét is.
Data boundary (adathatár). Mi ismert a predikció
pillanatában, és mi nem? A Northstar delivery_days a
rendeléskor nem ismert, tehát predictorba tiltott. A szöveges feladatnál
a jegy teljes szövege ismert, de a jövőbeli jegyek szókincse nem. Az
idősornál a mai napi adat a holnapi előrejelzéshez ismert, de a holnapi
rendelések nem. Az adathatár megsértése leakage-hez vezet; ez a [VI. ellenőrző pont: szivárgásmentes modellkísérlet]
fő témája volt.
A hat dimenzió együttesen adja a döntési lapot (decision sheet). A lap nem automatikus algoritmus, hanem átlátható indoklás.
Egy fontos képlet a log loss-hoz:
ahol $ y_i \in {0,1} $ a valódi címke, $ \hat{p}_i \in (0,1) $ a modell becsült valószínűsége, $ n $ a tesztminták száma. Mindkét vektor alakja $ (n,) $. Ha $ p = 0{,}2 $ és a modell mindig 0,2-t mond, a veszteség $ -0{,}2\ln 0{,}2 - 0{,}8\ln 0{,}8 \approx 0{,}5003 $. Ezt használjuk kézi kontrollként.
A következő kód a Northstar adatot és a splitet használja. A futáshoz
előbb a lab generátort és a starter kísérletet kell futtatni; a
starter_experiment.py létrehozza az
artifacts/split.json-t. A kódot itt
szemléltetőnek jelölöm: e fejezet generálási módjában
nem futtattam, a kimeneti számok elméletileg várt tartományok.
# platform: Linux/macOS, munkakönyvtár: a könyv lab/ könyvtára
python generate_data.py --seed 20261020 --customers 300 --orders 2400
python starter_experiment.py --cutoff 2025-05-01T00:00:00+00:00Ezután a baseline Python-kód:
import json
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import log_loss, roc_auc_score, accuracy_score
import torch
import torch.nn as nn
import torch.optim as optim
# Reprodukálható seed
rng = np.random.default_rng(20261020)
torch.manual_seed(20261020)
# -- Adatbetöltés --
customers = pd.read_csv("customers.csv")
orders = pd.read_csv("orders.csv")
orders["order_ts"] = pd.to_datetime(orders["order_ts"], utc=True)
orders["label_available_at"] = pd.to_datetime(
orders["label_available_at"], utc=True
)
customers["signup_date"] = pd.to_datetime(customers["signup_date"])
df = orders.merge(customers, on="customer_id", how="inner")
# Csak a split előtti, címkével rendelkező rendeléseket használjuk.
with open("artifacts/split.json", "r") as f:
split = json.load(f)
train_ids = set(split["train_order_ids"])
test_ids = set(split["test_order_ids"])
df_train = df[df["order_id"].isin(train_ids)].copy()
df_test = df[df["order_id"].isin(test_ids)].copy()
# -- Feature-ök és címke --
features = ["distance_km", "priority_flag", "basket_minor",
"quantity_sum", "account_age_days", "segment",
"region", "acquisition_channel"]
categorical = ["segment", "region", "acquisition_channel"]
numeric = [f for f in features if f not in categorical]
X_train = df_train[features]
y_train = df_train["target_late"].astype(int).to_numpy()
X_test = df_test[features]
y_test = df_test["target_late"].astype(int).to_numpy()
# -- Baseline: logisztikus regresszió --
preprocessor = ColumnTransformer(
transformers=[
("num", StandardScaler(), numeric),
("cat", OneHotEncoder(handle_unknown="ignore"), categorical),
]
)
baseline = Pipeline([
("pre", preprocessor),
("clf", LogisticRegression(max_iter=2000, C=1.0, random_state=20261020)),
])
baseline.fit(X_train, y_train)
p_base = baseline.predict_proba(X_test)[:, 1]
# Ellenőrzés: p_base shape (n_test,) és értékkészlet (0,1)
assert p_base.shape == (len(y_test),)
assert np.all((p_base > 0) & (p_base < 1))
# -- PyTorch MLP --
torch.manual_seed(20261020)
# Ugyanaz a preprocesszor; a kategorikus oszlopokból one-hot mátrixot készítünk
# a sklearn transformjával, hogy a PyTorch-bemenet numerikus legyen.
X_train_pre = baseline.named_steps["pre"].transform(X_train).toarray()
X_test_pre = baseline.named_steps["pre"].transform(X_test).toarray()
X_train_t = torch.tensor(X_train_pre, dtype=torch.float32)
y_train_t = torch.tensor(y_train, dtype=torch.float32).unsqueeze(1)
X_test_t = torch.tensor(X_test_pre, dtype=torch.float32)
class MLP(nn.Module):
def __init__(self, in_dim):
super().__init__()
self.net = nn.Sequential(
nn.Linear(in_dim, 32),
nn.ReLU(),
nn.Linear(32, 16),
nn.ReLU(),
nn.Linear(16, 1),
)
def forward(self, x):
return self.net(x)
model = MLP(in_dim=X_train_pre.shape[1])
optimizer = optim.Adam(model.parameters(), lr=0.01)
loss_fn = nn.BCEWithLogitsLoss()
n_epochs = 30
batch_size = 64
for epoch in range(n_epochs):
model.train()
perm = torch.randperm(len(X_train_t))
for i in range(0, len(X_train_t), batch_size):
idx = perm[i:i + batch_size]
optimizer.zero_grad()
logits = model(X_train_t[idx])
loss = loss_fn(logits, y_train_t[idx])
loss.backward()
optimizer.step()
model.eval()
with torch.no_grad():
logits_test = model(X_test_t)
p_torch = torch.sigmoid(logits_test).squeeze().numpy()
# -- Kiértékelés --
def report(name, p):
loss = log_loss(y_test, p)
auc = roc_auc_score(y_test, p)
acc = accuracy_score(y_test, (p >= 0.5).astype(int))
print(f"{name:25s} log_loss={loss:.4f} AUC={auc:.4f} acc={acc:.4f}")
report("baseline_logistic", p_base)
report("mlp_cpu", p_torch)
print("teszt_méret:", len(y_test), " késési_ráta:", y_test.mean())A TensorBoard áttekintés céljából a betanítási veszteséget
naplózhatnánk SummaryWriter-rel, például:
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter("runs/mlp_cpu")
writer.add_scalar("loss/train", loss.item(), epoch)
writer.close()A futtatás után a tensorboard --logdir runs parancs
indítja a vizualizációt. A TensorBoard a kísérletkövetés [Futásazonosító és kísérleti táblázat] egyszerű,
lokális előképe; nem helyettesíti a metrikák szöveges
dokumentálását.
A fenti kód nem futtatott, ezért a számokat „szemléltető” vagy „elméletileg várt” jelöléssel közöljük. A kontrollhoz a log loss képletét használjuk.
p_base és p_torch
hossza a tesztminták számával egyezik, minden elem a (0,1) intervallumba
esik. A p_base a logisztikus regresszió kimenete, tehát
monoton függvénye a lineáris kombinációnak; ezt az olvasó közvetlenül is
ellenőrizheti egy-egy mintán.A döntési lap P1-re:
| Dimenzió | Döntés | Indoklás |
|---|---|---|
| Reprezentáció | Tabuláris vektor, skálázás + one-hot | A jellemzők közvetlenül rendelkezésre állnak; a ritka szöveg nem jelenik meg. |
| Értelmezhetőség | Logisztikus regresszió az elsődleges; MLP csak összehasonlításra | A döntéshozó a distance_km és
priority_flag hatását kéri. |
| Bizonytalanság | Kalibrációs görbe a tesztkészleten | A hat_p becslés nem garantáltan kalibrált; a log loss
ezt méri. |
| Értékelés | Purged time-based split, log loss + AUC | A [VI. ellenőrző pont: szivárgásmentes modellkísérlet] szerint a teszt nem vezérelhet keresést. |
| Számítás | CPU, torch 2.x, 30 epoch |
2400 soron a GPU nem indokolt; a mérési terv a [Optimálás változatlan jelentéssel] szerint. |
| Adathatár | Csak rendeléskor ismert feature-ök; a delivery_days
tiltott |
A [VI. ellenőrző pont: szivárgásmentes modellkísérlet] és az adatszerződés szerint. |
A szakosodási döntésnél az összehasonlított modellekhez egységes adatfelosztás és költségkeret szükséges. A tesztértékek többszöri felhasználása modellválasztásra optimista becslést eredményez.
P2 – Szöveges support-jegy osztályozás. A baseline:
TfidfVectorizer(max_features=5000, min_df=3) +
LogisticRegression(max_iter=2000). A kísérleti terv:
TfidfVectorizer csak a
tanítószövegen fit-elődik.P3 – Napi rendelés-előrejelzés. A neurális jelölt: egyszerű MLP csúszó ablakkal, a bemenet az elmúlt 14 nap napi rendelésszáma, a kimenet a következő 7 nap összege. A baseline: az elmúlt 14 nap napi átlagának 7-szerese (a naiv szint). A kísérleti terv:
delivery_days nincs a célban, mert a rendelésszám csak az
order_ts-ből származik.A P2 és P3 tervnek közös vonása: a split explicit, a baseline a legegyszerűbb érvényes prediktor, és a metrikát a döntési célhoz kötjük. A terv önmagában nem eredmény; a végrehajtásnál a manifestet és a kódot verziózni kell.
Egy kolléga azt mondja: „A P1 feladatban a neurális modell accuracy-je 89%, ezért jobb, mint a logisztikus regresszióé.” Miért nem elegendő ez az állítás a módszerválasztáshoz? Nevezz meg legalább három hiányzó információt, és mondd meg, milyen további mérést kellene végezni.
Válaszd a P2 szöveges feladatot. Írj kis szintetikus adatgeneráló kódot (20 jegy, 3 kategória), majd taníts TF-IDF + logisztikus baseline-t. A megoldásban add meg a macro-F1 értékét és egy token-koefficiens példát. A kódot megjelölheted szemléltetőnek, de a logikát ellenőrizd kézzel.
Valaki a P1 feladatra a következőt jelenti: „GPU-ra tettem a modellt, a betanítás 10-szer gyorsabb lett, de a teszt log loss 0,65.” Mi lehet a hiba? Adj legalább két lehetséges okot, és mindegyikhez egy ellenőrző lépést.
A P3 idősornál a stakeholder azt kéri, hogy a modell ne csak a
rendelésszámot, hanem a késési arányt is jelezze előre naponta. Hogyan
változik a döntési lap evaluation, uncertainty
és data boundary mezője? Adj konkrét javaslatot.
Az accuracy önmagában nem elég, mert:
További mérés: ugyanazon a purged teszten a log loss, AUC és a kalibrációs görbe; valamint a modellkártya robusztussági mezője [Robusztusság és korlátokkal írt modellkártya] szerint.
Ellenőrző invariant: a log loss a ritka konstans modellnél $ 0{,}11 \cdot \ln(0{,}11) + 0{,}89 \cdot \ln(0{,}89) \approx 0{,}347 $, tehát a 0,35 alatti log loss már értelmes, a 0,5 fölötti nem.
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import f1_score, classification_report
from sklearn.model_selection import train_test_split
# Szintetikus 20 jegy; a tokenek kategóriánként szeparálnak.
texts = [
"app crashes when saving file", "bug in login screen",
"button does nothing", "error during export",
"crash after update", "cannot open settings",
"login fails with timeout", "please add dark mode",
"feature request export csv", "would like keyboard shortcut",
"account blocked after payment", "cannot reset password",
"need invoice history", "subscription upgrade failed",
"billing page empty", "delete my data",
"dark mode would help", "csv export needed",
"mobile app crash", "reset password link expired",
]
labels = ["bug", "bug", "bug", "bug", "bug", "bug", "bug",
"feature_request", "feature_request", "feature_request",
"account", "account", "account", "account", "account", "account",
"feature_request", "feature_request", "bug", "account"]
X_train, X_test, y_train, y_test = train_test_split(
texts, labels, test_size=0.3, random_state=20261020, stratify=labels
)
vec = TfidfVectorizer(min_df=1)
X_tr = vec.fit_transform(X_train)
X_te = vec.transform(X_test)
clf = LogisticRegression(max_iter=2000, random_state=20261020)
clf.fit(X_tr, y_train)
pred = clf.predict(X_te)
macro_f1 = f1_score(y_test, pred, average="macro")
print("macro-F1:", macro_f1)
print(classification_report(y_test, pred))
# Token-koefficiens példa: a logisztikus egy-versus-rest koefficiensek
coef = clf.coef_[0]
for w, c in sorted(zip(vec.get_feature_names_out(), coef),
key=lambda x: abs(x[1]), reverse=True)[:5]:
print(f"{w:20s} {c:.3f}")A kód szemléltető; a szintetikus adatban a crash,
login, export, csv,
billing, password tokenek erősen szeparálnak,
ezért a macro-F1 várhatóan 0,8 fölötti. Ellenőrző
invariant: a vektorizáló után az X_tr sorainak
összege mindig pozitív, mert minden dokumentumban van legalább egy
token. A macro-F1 0 és 1 közötti. Tolerancia: a
train_test_split seed-jétől függően az érték 0,75 és 1,00
között változhat; a stratify miatt minden kategória megjelenik a
tesztben.
Két lehetséges hiba:
X_train_pre és X_test_pre oszlopainak átlaga a
tanítófázisban nulla legyen, a tesztnél közel nulla; és a két fázisban
ugyanazt a fitted preprocesszort használjuk.Továbbá a GPU-val kapcsolatban: a 2400 soron a GPU adatmozgatási ideje nagyobb lehet, mint a tényleges számítás. A [Optimálás változatlan jelentéssel] szerint a változatlan jelentés melletti optimalizálás a kiindulópont.
Ha a cél napi késési arány, akkor:
delivery_days szükséges, ami a kézbesítés napján válik
ismertté. Ha a mai napra napi késési arányt jelzünk, a kézbesítés lehet,
hogy mai, de a nap végéig nem teljes. Ezért a napi arányhoz a cutoff
előtti napok teljesen megfigyelt címkéit kell használni, különben
cenzorált célhoz jutunk. A megfigyelési szerződésben rögzíteni kell,
hogy a napi arány csak a teljesen megfigyelt napokra értelmezett, vagy
külön cenzorált modellt kell építeni.A leggyakoribb hiba ezen az ellenőrző ponton: az új eszköz bevezetése önmagában eredményként van kommunikálva. Példa: a P1 feladatban valaki azt jelenti, hogy „bevezettük a PyTorch-ot és a GPU-t, a rendszer kész”. De ha a logisztikus baseline log loss-ja 0,41, az MLP-é 0,46, akkor az új eszköz rosszabb eredményt ad. A tudományos állítás a metrikában és a bizonytalanságban van, nem a keretrendszer nevében.
Visszautalás: a [Adatkezelés és reprodukálhatóság külső modellnél] külső LLM-eknél is ezt tanította: a futtatás azonosítása kód-, modell- és promptverzióval történik, nem az API névvel. Az eszköz nem helyettesíti a mérést.
Önellenőrzés: ha egy döntési lapon a compute mező csak
annyit mond, hogy „GPU-t használunk, mert gyorsabb”, az nem mérési terv.
A [Optimálás változatlan jelentéssel] szerint a
gyorsítás feltétele az azonos input, azonos metrika és a tényleges mért
futásidő.
A VII. ellenőrző pont a módszerválasztást hat dimenzióban teszi tudatossá: reprezentáció, értelmezhetőség, bizonytalanság, értékelés, számítási költség és adathatár. A tabuláris Northstar-feladathoz logisztikus baseline-t és CPU-s MLP-t hasonlítottunk purged spliten, log loss-szal és AUC-vel; a szöveges és idősoros feladatokhoz kísérleti tervet adtunk, amely a baseline-t és az időbeli splitet előre rögzíti. Az eszköz nem helyettesíti a mérést: a GPU vagy az új keretrendszer csak akkor döntés, ha a metrika és a költség igazolja. Következő lépésként a [Modulhatár és tiszta függvény] ezt a döntési fegyelmet moduláris Python-projektbe fordítja.