Faza 02 · lecția 13
Pipeline-uri de învățare automată
Scopul lecției: Un model nu este un produs. Un pipeline este. Pipeline-ul cuprinde totul, de la datele brute până la predicția pusă în producție, iar fiecare pas trebuie să fie reproductibil.
Versiunea curentă AlexBred.com: primele 100 de lecții ale programului în limba română.
Cuprinsul lecției
- Obiective de învățare
- Problema
- Conceptul
- Ce este un pipeline
- Scurgerea datelor: ucigașul tăcut
- Pipeline din scikit-learn
- ColumnTransformer: pipeline-uri diferite pentru coloane diferite
- Urmărirea experimentelor
- Versionarea modelelor
- Versionarea datelor cu DVC
- Experimente reproductibile
- De la notebook la un pipeline de producție
- Greșeli frecvente în pipeline-uri
- Construiți
- Pasul 1: transformator personalizat
- Pasul 2: pipeline de la zero
- Pasul 3: validare încrucișată cu pipeline
- Pasul 4: pipeline complet de producție cu scikit-learn
- Livrați
- Exerciții
- Termeni-cheie
- Lecturi suplimentare
Un model nu este un produs. Un pipeline este. Pipeline-ul cuprinde totul, de la datele brute până la predicția pusă în producție, iar fiecare pas trebuie să fie reproductibil.
Tip: Construire Limbaj: Python Cerințe preliminare: Faza 2, lecția 12 (Reglarea hiperparametrilor) Durată: ~120 de minute
Obiective de învățare
- Să construiți de la zero un pipeline ML care reunește imputarea, scalarea, codificarea și antrenarea modelului într-un singur obiect reproductibil.
- Să identificați situațiile de scurgere a datelor și să explicați cum le previn pipeline-urile prin ajustarea transformatoarelor numai pe datele de antrenare.
- Să construiți un
ColumnTransformercare aplică preprocesări diferite caracteristicilor numerice și celor categoriale. - Să implementați serializarea pipeline-ului și să demonstrați că același pipeline ajustat produce rezultate identice în mediile de antrenare și de producție.
Problema
Aveți un notebook care încarcă date, completează valorile lipsă cu mediana, scalează caracteristicile, antrenează un model și afișează acuratețea. Funcționează. Îl livrați.
O lună mai târziu, cineva reantrenează modelul și obține rezultate diferite. Mediana fusese calculată pe întregul set de date, inclusiv pe datele de testare — o scurgere de date. Parametrii de scalare nu fuseseră salvați, așa că inferența folosește alte statistici. Codul pentru ingineria caracteristicilor fusese copiat între antrenare și servire, iar copiile au evoluat diferit. În producție, într-o coloană categorială a apărut o valoare nouă, pe care codificatorul nu o mai întâlnise.
Aceste situații nu sunt ipotetice. Ele se numără printre cele mai frecvente motive pentru care sistemele ML eșuează în producție. Pipeline-urile le tratează prin reunirea fiecărui pas de transformare într-un singur obiect ordonat și reproductibil.
Notă tehnică a traducerii: Pipeline-ul previne aceste probleme numai dacă împărțirea datelor și validarea sunt făcute corect și dacă toate transformările învățate sunt incluse în el. Un pipeline nu elimină automat scurgerile provenite din construirea anterioară a caracteristicilor, din etichete sau din separarea temporală incorectă.
Conceptul
Ce este un pipeline
Un pipeline este o succesiune ordonată de transformări ale datelor, urmată de un model. Fiecare pas primește ca intrare rezultatul pasului precedent. Întregul pipeline este ajustat o singură dată pe datele de antrenare. La inferență, același pipeline ajustat transformă datele noi și produce predicții.
Pipeline-ul asigură că:
- transformările sunt ajustate numai pe datele de antrenare, fără scurgeri;
- la inferență sunt aplicate aceleași transformări;
- întregul obiect poate fi serializat și livrat ca un singur artefact;
- validarea încrucișată aplică pipeline-ul separat în fiecare pliu, prevenind scurgerile subtile.
Scurgerea datelor: ucigașul tăcut
Scurgerea datelor apare atunci când informații din setul de testare sau din datele viitoare contaminează antrenarea. Pipeline-urile previn formele cele mai frecvente.
Cu scurgere (greșit):
X = df.drop("target", axis=1)
y = df["target"]
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
X_train, X_test = X_scaled[:800], X_scaled[800:]
y_train, y_test = y[:800], y[800:]
Scalerul a văzut datele de testare. Media și abaterea standard includ eșantioane de testare. Aceasta umflă estimările acurateții.
Corect:
X_train, X_test = X[:800], X[800:]
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
Cu un pipeline, această separare este gestionată automat atunci când ajustați pipeline-ul numai pe setul de antrenare.
Pipeline din scikit-learn
Clasa Pipeline din scikit-learn înlănțuie transformatoare și un estimator. Ea expune metodele .fit(), .predict() și .score(), care aplică toți pașii în ordine.
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
pipe = Pipeline([
("scaler", StandardScaler()),
("model", LogisticRegression()),
])
pipe.fit(X_train, y_train)
predictions = pipe.predict(X_test)
Când apelați pipe.fit(X_train, y_train):
- Scalerul apelează
fit_transformpeX_train. - Modelul apelează
fitpeX_trainscalat.
Când apelați pipe.predict(X_test):
- Scalerul apelează
transform, nufit_transform, peX_test. - Modelul apelează
predictpeX_testscalat.
Scalerul nu vede niciodată datele de testare în timpul ajustării. Acesta este scopul esențial.
ColumnTransformer: pipeline-uri diferite pentru coloane diferite
Seturile de date reale au coloane numerice și categoriale care necesită preprocesări diferite. ColumnTransformer se ocupă de această situație.
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
numeric_pipe = Pipeline([
("impute", SimpleImputer(strategy="median")),
("scale", StandardScaler()),
])
categorical_pipe = Pipeline([
("impute", SimpleImputer(strategy="most_frequent")),
("encode", OneHotEncoder(handle_unknown="ignore")),
])
preprocessor = ColumnTransformer([
("num", numeric_pipe, ["age", "income", "score"]),
("cat", categorical_pipe, ["city", "gender", "plan"]),
])
full_pipeline = Pipeline([
("preprocess", preprocessor),
("model", GradientBoostingClassifier()),
])
Opțiunea handle_unknown="ignore" din OneHotEncoder este importantă în producție. Când apare o categorie nouă — de exemplu, un oraș pe care modelul nu l-a mai văzut — codificatorul produce un vector de zerouri în loc să se oprească din cauza unei erori.
Notă tehnică a traducerii: Pentru ca fragmentul să ruleze independent, trebuie importată și clasa
GradientBoostingClassifier, de exemplu prinfrom sklearn.ensemble import GradientBoostingClassifier. Ignorarea categoriilor necunoscute evită oprirea, dar poate elimina în tăcere un semnal util; monitorizați schema și frecvența categoriilor noi.
Urmărirea experimentelor
Un pipeline face antrenarea reproductibilă, dar trebuie să urmăriți și ce s-a întâmplat de la un experiment la altul: ce hiperparametri au fost folosiți, ce versiune a setului de date, ce metrici și ce versiune de cod.
MLflow este una dintre cele mai răspândite soluții open-source:
import mlflow
with mlflow.start_run():
mlflow.log_param("max_depth", 5)
mlflow.log_param("n_estimators", 100)
mlflow.log_param("learning_rate", 0.1)
pipe.fit(X_train, y_train)
accuracy = pipe.score(X_test, y_test)
mlflow.log_metric("accuracy", accuracy)
mlflow.sklearn.log_model(pipe, "model")
Fiecare rulare este înregistrată împreună cu parametrii, metricile, artefactele și modelul complet. Puteți compara rulările, puteți reproduce un experiment și puteți livra o versiune de model, dacă au fost păstrate și codul, datele, dependențele și configurația necesare.
Notă tehnică a traducerii: În versiunile MLflow recente este recomandat argumentul denumit
name, de exemplumlflow.sklearn.log_model(pipe, name="model"); forma pozițională din sursă corespunde API-ului mai vechi și poate emite un avertisment de depreciere. Înregistrarea unei rulări nu garantează singură reproductibilitatea completă.
Weights & Biases (wandb) oferă funcționalități similare printr-un tablou de bord găzduit:
import wandb
wandb.init(project="my-pipeline")
wandb.config.update({"max_depth": 5, "n_estimators": 100})
pipe.fit(X_train, y_train)
accuracy = pipe.score(X_test, y_test)
wandb.log({"accuracy": accuracy})
Versionarea modelelor
După urmărirea experimentelor, trebuie să gestionați versiunile modelelor. Ce model este în producție? Care este în preproducție? Care era cel de săptămâna trecută?
Model Registry din MLflow oferă:
- Urmărirea versiunilor: fiecare model salvat primește un număr de versiune.
- Tranziții între etape: „Staging”, „Production”, „Archived”.
- Flux de aprobare: modelele trebuie promovate explicit în producție.
- Revenire: puteți comuta la o versiune precedentă.
Notă tehnică a traducerii: Etapele Model Registry sunt depreciate începând cu MLflow 2.9. Fluxurile actuale folosesc aliasuri precum
champion, etichete și, adesea, modele înregistrate distinct pentru fiecare mediu. Aprobarea explicită depinde de fluxul și controalele de acces configurate, iar schimbarea rapidă a unui alias nu garantează singură o revenire operațională instantanee.
Versionarea datelor cu DVC
Codul este versionat cu Git. Și datele ar trebui versionate, însă Git nu gestionează bine fișierele mari. DVC (Data Version Control) rezolvă această problemă.
dvc init
dvc add data/training.csv
git add data/training.csv.dvc data/.gitignore
git commit -m "Track training data"
dvc push
DVC păstrează inițial datele în cache-ul local și înregistrează în Git un fișier .dvc mic, care conține informațiile de urmărire și hash-ul. Pentru a sincroniza datele cu S3, GCS, Azure sau alt spațiu de stocare, configurați mai întâi un remote DVC, apoi utilizați dvc push. După ce comutați la un commit Git, comanda dvc checkout restaurează datele exacte folosite din cache sau dintr-un remote disponibil.
Notă tehnică a traducerii: comanda
dvc pushdin exemplul sursei presupune că un remote a fost configurat deja, de exemplu prindvc remote add; numaidvc initșidvc addnu configurează automat S3, GCS sau Azure.
Astfel, fiecare commit Git poate fixa atât codul, cât și referința către date. Pentru reproductibilitate completă, spațiul DVC la distanță trebuie să rămână disponibil, iar fișierele .dvc, configurația și toate celelalte dependențe trebuie de asemenea versionate.
Experimente reproductibile
Un experiment reproductibil necesită patru lucruri:
- Semințe aleatoare fixe: setați semințele pentru NumPy,
randomși framework-ul folosit — PyTorch, scikit-learn etc. - Dependențe fixate:
requirements.txtsaupoetry.lockcu versiuni exacte. - Date versionate: DVC sau o soluție similară.
- Fișiere de configurare: toți hiperparametrii într-o configurație, nu înscriși direct în cod.
import numpy as np
import random
def set_seed(seed=42):
random.seed(seed)
np.random.seed(seed)
try:
import torch
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
torch.backends.cudnn.deterministic = True
except ImportError:
pass
Notă tehnică a traducerii: Semințele fixe nu garantează rezultate identice între versiuni de biblioteci, platforme, dispozitive sau toate operațiile paralele. PyTorch documentează surse suplimentare de nedeterminism, iar activarea algoritmilor determiniști poate reduce performanța fără să acopere automat fiecare operație.
De la notebook la un pipeline de producție
Evoluția tipică:
- Explorare în notebook: experimente rapide, vizualizări și idei de caracteristici.
- Extragerea funcțiilor: mutați preprocesarea, ingineria caracteristicilor și evaluarea în module.
- Construirea pipeline-ului: înlănțuiți transformările într-un
Pipelinescikit-learn sau într-o clasă proprie. - Gestionarea configurației: mutați toți hiperparametrii într-un fișier de configurare YAML sau JSON.
- Urmărirea experimentelor: adăugați jurnalizarea cu MLflow sau
wandb. - Validarea datelor: verificați schema, distribuțiile și tiparele valorilor lipsă înainte de antrenare.
- Teste: teste unitare pentru transformatoare și teste de integrare pentru pipeline-ul complet.
- Livrare: serializați pipeline-ul, înveliți-l într-un API — FastAPI sau Flask — și containerizați-l.
Greșeli frecvente în pipeline-uri
| Greșeală | De ce este dăunătoare | Remediere |
|---|---|---|
| Ajustarea pe toate datele înainte de împărțire | Scurgere de date | Folosiți Pipeline cu cross_val_score |
| Ingineria caracteristicilor în afara pipeline-ului | Transformări diferite la antrenare și servire | Introduceți toate transformările în Pipeline |
| Netratarea categoriilor necunoscute | O valoare nouă provoacă o eroare în producție | Folosiți OneHotEncoder(handle_unknown="ignore") |
| Numele coloanelor înscrise direct în cod | Codul se rupe când schema se schimbă | Folosiți liste de coloane din configurație |
| Lipsa validării datelor | Datele greșite produc în tăcere predicții eronate | Adăugați verificări ale schemei înainte de predicție |
| Diferențe între antrenare și servire | Modelul vede alte caracteristici în producție | Folosiți același obiect Pipeline în ambele cazuri |
Notă tehnică a traducerii: Mutarea listei de coloane în configurație nu face pipeline-ul imun la schimbări de schemă; doar explicitează contractul. Sunt necesare validarea schemei, gestionarea versiunilor și monitorizarea.
Construiți
Codul din code/pipeline.py construiește de la zero un pipeline ML complet.
Pasul 1: transformator personalizat
class CustomTransformer:
def __init__(self):
self.means = None
self.stds = None
def fit(self, X):
self.means = np.mean(X, axis=0)
self.stds = np.std(X, axis=0)
self.stds[self.stds == 0] = 1.0
return self
def transform(self, X):
return (X - self.means) / self.stds
def fit_transform(self, X):
return self.fit(X).transform(X)
Pasul 2: pipeline de la zero
class PipelineFromScratch:
def __init__(self, steps):
self.steps = steps
def fit(self, X, y=None):
X_current = X.copy()
for name, step in self.steps[:-1]:
X_current = step.fit_transform(X_current)
name, model = self.steps[-1]
model.fit(X_current, y)
return self
def predict(self, X):
X_current = X.copy()
for name, step in self.steps[:-1]:
X_current = step.transform(X_current)
name, model = self.steps[-1]
return model.predict(X_current)
Notă tehnică a traducerii: Aceste clase sunt exemple didactice, nu înlocuitori compleți pentru API-ul scikit-learn.
CustomTransformerpresupune o matrice NumPy numerică, bidimensională și mutabilă.PipelineFromScratchnu transmiteytransformatoarelor supravegheate, nu validează starea ajustată sau schema, nu oferă clonare origet_params/set_paramsși nu implementează serializarea. Obiectivul privind imputarea și codificarea este realizat în exemplul scikit-learn, nu deCustomTransformersingur.
Pasul 3: validare încrucișată cu pipeline
Codul demonstrează cum validarea încrucișată cu un pipeline previne scurgerea datelor: scalerul este ajustat separat pe datele de antrenare ale fiecărui pliu.
Pasul 4: pipeline complet de producție cu scikit-learn
Un pipeline complet cu ColumnTransformer, mai multe căi de preprocesare și un model, antrenat cu validare încrucișată corectă și jurnalizarea experimentelor.
Livrați
Această lecție produce:
outputs/prompt-ml-pipeline.md— o abilitate pentru construirea și depanarea pipeline-urilor ML;code/pipeline.py— un pipeline complet, de la implementarea proprie până la scikit-learn.
Exerciții
-
Construiți un pipeline care gestionează un set de date cu 3 coloane numerice și 2 coloane categoriale. Folosiți
ColumnTransformerpentru a aplica imputarea cu mediana și scalarea coloanelor numerice, respectiv imputarea cu valoarea cea mai frecventă și codificarea one-hot a celor categoriale. Antrenați-l cu validare încrucișată în 5 pliuri. -
Introduceți deliberat o scurgere de date: ajustați scalerul pe întregul set înainte de împărțire. Comparați scorul validării încrucișate cu scurgere cu scorul pipeline-ului corect. Cât de mare este diferența?
-
Serializați pipeline-ul cu
joblib.dump. Încărcați-l într-un script separat și rulați predicții. Verificați că predicțiile sunt identice pentru aceleași intrări și același mediu de execuție. Nu încărcați niciodată artefactejoblibdin surse în care nu aveți încredere. -
Adăugați în pipeline un transformator personalizat care creează caracteristici polinomiale de gradul 2 pentru cele mai importante două coloane numerice. Unde ar trebui plasat în pipeline?
-
Configurați urmărirea MLflow pentru pipeline. Rulați 5 experimente cu hiperparametri diferiți. Folosiți interfața MLflow (
mlflow ui) pentru a compara rulările și a alege cel mai bun model.
Notă tehnică a traducerii: Persistența bazată pe
pickle/joblibpoate executa cod la încărcare și necesită, de regulă, aceleași versiuni ale dependențelor ca la antrenare. Folosiți numai artefacte de încredere și înregistrați mediul de execuție.
Termeni-cheie
| Termen | Ce spun oamenii | Ce înseamnă de fapt |
|---|---|---|
| Pipeline | „Lanț de transformări + model” | O succesiune ordonată de transformatoare ajustate și un model, aplicate ca o singură unitate pentru a preveni scurgerile |
| Scurgerea datelor | „Informațiile din testare au ajuns în antrenare” | Folosirea informațiilor din afara setului de antrenare pentru construirea modelului, ceea ce umflă estimările performanței |
ColumnTransformer |
„Preprocesare diferită pentru fiecare coloană” | Aplică pipeline-uri diferite unor subseturi de coloane și combină rezultatele |
| Urmărirea experimentelor | „Jurnalizarea rulărilor” | Înregistrarea parametrilor, metricilor, artefactelor și versiunilor de cod pentru fiecare antrenare |
| MLflow | „Urmăriți și livrați modele” | Platformă open-source pentru urmărirea experimentelor, registrul de modele și livrare |
| DVC | „Git pentru date” | Sistem de versionare a fișierelor mari de date, care păstrează hash-uri în Git și datele într-un spațiu la distanță |
| Registru de modele | „Catalogul versiunilor de model” | Sistem care urmărește versiunile modelelor cu aliasuri, etichete sau stări gestionate de fluxul organizației |
| Diferențe între antrenare și servire | „În notebook funcționa” | Diferențe între prelucrarea datelor la antrenare și la inferență, care produc erori tăcute |
| Reproductibilitate | „Același cod, același rezultat” | Capacitatea de a obține aceleași rezultate din același cod, aceleași date, aceeași configurație și un mediu controlat |
Lecturi suplimentare
- Documentația Pipeline din scikit-learn — referința oficială pentru pipeline-uri.
- Documentația MLflow — urmărirea experimentelor și registrul de modele.
- Documentația DVC — versionarea datelor.
- Sculley și colaboratorii, „Hidden Technical Debt in Machine Learning Systems” (2015) — lucrarea fundamentală despre complexitatea sistemelor ML.
- Google ML Best Practices: Rules of ML — recomandări practice pentru ML în producție.
Sursă: Originalul în limba engleză
Navigare: ← Lecția 02.12 — Reglarea hiperparametrilor · Faza 2 — Bazele învățării automate · Catalog complet · în continuare: 02.14 — Naive Bayes