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ă.

Curs
AI Engineering from Scratch
Fază
Bazele învățării automate
Lectură
16 min.
Verificat
Cuprinsul lecției
  1. Obiective de învățare
  2. Problema
  3. Conceptul
  4. Ce este un pipeline
  5. Scurgerea datelor: ucigașul tăcut
  6. Pipeline din scikit-learn
  7. ColumnTransformer: pipeline-uri diferite pentru coloane diferite
  8. Urmărirea experimentelor
  9. Versionarea modelelor
  10. Versionarea datelor cu DVC
  11. Experimente reproductibile
  12. De la notebook la un pipeline de producție
  13. Greșeli frecvente în pipeline-uri
  14. Construiți
  15. Pasul 1: transformator personalizat
  16. Pasul 2: pipeline de la zero
  17. Pasul 3: validare încrucișată cu pipeline
  18. Pasul 4: pipeline complet de producție cu scikit-learn
  19. Livrați
  20. Exerciții
  21. Termeni-cheie
  22. 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 ColumnTransformer care 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-uri de învățare automată»

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):

  1. Scalerul apelează fit_transform pe X_train.
  2. Modelul apelează fit pe X_train scalat.

Când apelați pipe.predict(X_test):

  1. Scalerul apelează transform, nu fit_transform, pe X_test.
  2. Modelul apelează predict pe X_test scalat.

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 prin from 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 exemplu mlflow.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 push din exemplul sursei presupune că un remote a fost configurat deja, de exemplu prin dvc remote add; numai dvc init și dvc add nu 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:

  1. Semințe aleatoare fixe: setați semințele pentru NumPy, random și framework-ul folosit — PyTorch, scikit-learn etc.
  2. Dependențe fixate: requirements.txt sau poetry.lock cu versiuni exacte.
  3. Date versionate: DVC sau o soluție similară.
  4. 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

Диаграмма к уроку «Pipeline-uri de învățare automată»

Evoluția tipică:

  1. Explorare în notebook: experimente rapide, vizualizări și idei de caracteristici.
  2. Extragerea funcțiilor: mutați preprocesarea, ingineria caracteristicilor și evaluarea în module.
  3. Construirea pipeline-ului: înlănțuiți transformările într-un Pipeline scikit-learn sau într-o clasă proprie.
  4. Gestionarea configurației: mutați toți hiperparametrii într-un fișier de configurare YAML sau JSON.
  5. Urmărirea experimentelor: adăugați jurnalizarea cu MLflow sau wandb.
  6. Validarea datelor: verificați schema, distribuțiile și tiparele valorilor lipsă înainte de antrenare.
  7. Teste: teste unitare pentru transformatoare și teste de integrare pentru pipeline-ul complet.
  8. 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. CustomTransformer presupune o matrice NumPy numerică, bidimensională și mutabilă. PipelineFromScratch nu transmite y transformatoarelor supravegheate, nu validează starea ajustată sau schema, nu oferă clonare ori get_params/set_params și nu implementează serializarea. Obiectivul privind imputarea și codificarea este realizat în exemplul scikit-learn, nu de CustomTransformer singur.

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

  1. Construiți un pipeline care gestionează un set de date cu 3 coloane numerice și 2 coloane categoriale. Folosiți ColumnTransformer pentru 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.

  2. 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?

  3. 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ă artefacte joblib din surse în care nu aveți încredere.

  4. 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?

  5. 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/joblib poate 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

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