MLOps

Pipeline-ul Tau ML Nu Are Teste. Iata Cum sa Repari Asta.

Petru Constantin
--7 min lectura
#mlops#devidevs

Pipeline-ul Tau ML Nu Are Teste. Iata Cum sa Repari Asta.

Aplicatia ta are 90% test coverage. Pipeline-ul tau ML are 0%.

Nu ai deploya un REST API fara unit teste. Ai fi ras din PR review. Dar cumva, pipeline-ul ML care alimenteaza predictii catre acelasi API livreaza fara nicio validare, nicio poarta si o rugaciune ca datele de antrenament nu s-au schimbat de marti trecuta.

Conform Gartner, 85% din proiectele ML esueaza. Nu pentru ca modelele sunt proaste. Ci pentru ca tot ce e in jurul modelului - infrastructura de pipeline, validarea datelor, portile de deployment - e tinut laolalta cu scotch si Jupyter notebook-uri.

Daca ai modele in productie, acesta e momentul in care ori repari, ori te alaturi celor 85%.

Cele Trei Esecuri pe Care Nimeni Nu le Testeaza

Software-ul traditional esueaza zgomotos. Un null pointer arunca exceptie. Un API defect returneaza 500. Esti alertat, repari, mergi mai departe.

Pipeline-urile ML esueaza in liniste. Modelul continua sa serveasca predictii. Statusul HTTP este 200. Dar predictiile sunt gunoi pentru ca ceva s-a schimbat upstream si nimeni n-a observat.

Iata cei trei ucigasi silentiosi:

1. Data drift. Modelul tau s-a antrenat pe datele din Q4 2025. E martie 2026. Comportamentul clientilor s-a schimbat, o distributie de feature s-a modificat si acuratetea modelului a scazut cu 12% pe parcursul a doua luni. Nicio alerta nu s-a declansat pentru ca n-ai configurat vreodata una. Monitorizarea standard a aplicatiei (CPU, memorie, erori HTTP) arata verde pe toata linia. Modelul e perfect sanatos din perspectiva infrastructurii. E doar gresit.

2. Spargeri de schema. O echipa upstream a redenumit o coloana din user_age in customer_age. Pipeline-ul tau de feature-uri o umple silentios cu null-uri. Modelul consuma fericit null-urile si produce nonsens. Exact acest scenariu a spart un pipeline ML de productie cand o simpla schimbare de API a cascadat intr-o intrerupere completa.

3. Discrepanta antrenament-serving. Pipeline-ul de antrenament calculeaza feature-urile intr-un fel. Pipeline-ul de serving le calculeaza diferit. Modelul functioneaza excelent in evaluarea offline si cade in productie. Nimeni nu prinde asta pentru ca nimeni nu testeaza pentru asta. Acesta este deosebit de frecvent cand echipa de data science foloseste pandas pentru antrenament si ingineria foloseste Spark sau un feature store pentru serving. Aceeasi logica, doua implementari, diferente numerice subtile care se compun in predictii gresite.

Stratul 1: Validare Date cu Great Expectations

Inainte ca modelul tau sa vada un singur rand, valideaza datele. Great Expectations este unealta standard aici si se conecteaza direct in pytest.

import great_expectations as gx
 
context = gx.get_context()
 
# Defineste cum arata "date sanatoase"
suite = context.add_expectation_suite("training_data_quality")
 
# Acestea sunt testele unitare de date
validator = context.get_validator(
    batch_request=batch_request,
    expectation_suite_name="training_data_quality"
)
 
# Coloana exista si nu are null-uri
validator.expect_column_to_exist("user_age")
validator.expect_column_values_to_not_be_null("user_age")
 
# Distributia nu s-a schimbat drastic
validator.expect_column_mean_to_be_between("user_age", min_value=25, max_value=45)
validator.expect_column_values_to_be_between("user_age", min_value=13, max_value=120)
 
results = validator.validate()
assert results.success, f"Data validation failed: {results.statistics}"

Ruleaza asta in CI inainte de inceperea antrenamentului. Daca datele sunt gresite, pipeline-ul se opreste. Niciun model defect nu se creeaza vreodata.

Perspectiva cheie: trateaza asteptarile de date ca asertiunile aplicatiei. Nu ai sari peste validarea input-ului la un endpoint API. Nu o sari nici pe datele de antrenament.

Stratul 2: Porti de Validare Model

Un model antrenat ar trebui sa treaca o suita de teste inainte sa atinga productia. Nu "arata bine intr-un notebook." O poarta reala, automatizata.

# tests/test_model_quality.py
import mlflow
import pytest
 
@pytest.fixture
def candidate_model():
    """Incarca modelul care tocmai a terminat antrenamentul."""
    return mlflow.pyfunc.load_model("runs:/latest/model")
 
@pytest.fixture
def baseline_metrics():
    """Metricile modelului din productie, bara noastra minima."""
    prod_model = mlflow.pyfunc.load_model("models:/fraud-detector/Production")
    return evaluate_model(prod_model, test_dataset)
 
def test_accuracy_above_threshold(candidate_model):
    metrics = evaluate_model(candidate_model, test_dataset)
    assert metrics["auc"] >= 0.90, (
        f"AUC {metrics['auc']:.3f} sub pragul 0.90"
    )
 
def test_no_regression_vs_production(candidate_model, baseline_metrics):
    candidate_metrics = evaluate_model(candidate_model, test_dataset)
    assert candidate_metrics["auc"] >= baseline_metrics["auc"] - 0.02, (
        f"Candidat AUC {candidate_metrics['auc']:.3f} regresie vs "
        f"productie {baseline_metrics['auc']:.3f}"
    )
 
def test_inference_latency(candidate_model):
    import time
    start = time.perf_counter()
    candidate_model.predict(sample_batch)
    elapsed = time.perf_counter() - start
    assert elapsed < 0.1, f"Inferenta a durat {elapsed:.3f}s, limita e 0.1s"

Conecteaza asta in pipeline-ul CI. GitHub Actions, GitLab CI, ce folosesti. Promovarea in model registry din "Staging" in "Production" ar trebui controlata de aceasta suita de teste, nu de cineva care da click pe un buton intr-un UI.

Stratul 3: Teste de Paritate Antrenament-Serving

Acesta este cel pe care nimeni nu-l face, si cauzeaza cele mai urate bug-uri. Pipeline-ul tau de antrenament foloseste pandas. Pipeline-ul de serving foloseste un feature store Spark. Aceeasi logica, implementari diferite, rezultate diferite.

# tests/test_feature_parity.py
def test_feature_computation_matches():
    """Acelasi input ar trebui sa produca aceleasi feature-uri,
    indiferent de pipeline-ul care le calculeaza."""
    raw_input = load_test_fixture("sample_user_event.json")
 
    # Calculeaza feature-urile in ambele feluri
    training_features = training_pipeline.compute_features(raw_input)
    serving_features = serving_pipeline.compute_features(raw_input)
 
    for feature_name in training_features:
        assert abs(
            training_features[feature_name] - serving_features[feature_name]
        ) < 1e-6, (
            f"Feature {feature_name} difera: "
            f"antrenament={training_features[feature_name]}, "
            f"serving={serving_features[feature_name]}"
        )

Ruleaza asta nocturn. Cand cineva actualizeaza logica de feature-uri din pipeline-ul de antrenament si uita sa actualizeze si serving-ul, acest test prinde problema inainte sa o faca utilizatorii.

Punand Totul Laolalta: Pipeline-ul ML CI/CD

Iata cum arata un pipeline ML CI/CD real:

# .github/workflows/ml-pipeline.yml
name: ML Pipeline CI/CD
on:
  push:
    paths: ["ml/**", "features/**"]
 
jobs:
  data-validation:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pip install great-expectations pytest
      - run: pytest tests/test_data_quality.py -v
 
  model-training:
    needs: data-validation
    runs-on: [self-hosted, gpu]
    steps:
      - run: python ml/train.py --experiment ci-run-${{ github.sha }}
 
  model-validation:
    needs: model-training
    steps:
      - run: pytest tests/test_model_quality.py -v
      - run: pytest tests/test_feature_parity.py -v
 
  promote-to-staging:
    needs: model-validation
    steps:
      - run: |
          python -c "
          import mlflow
          client = mlflow.tracking.MlflowClient()
          client.transition_model_version_stage(
              name='fraud-detector',
              version='${{ env.MODEL_VERSION }}',
              stage='Staging'
          )"

Validarea datelor ruleaza prima. Daca esueaza, antrenamentul nu incepe niciodata. Validarea modelului ruleaza dupa antrenament. Daca modelul regreseaza sau e prea lent, nu este promovat niciodata. Fiecare etapa controleaza pe urmatoarea.

Cum Abordeaza DeviDevs Aceasta Problema

Construim platforme ML pentru companii care au modele in productie dar nicio infrastructura de testare in jurul lor. Pattern-ul e intotdeauna acelasi: o echipa de data science a construit ceva impresionant, l-a aruncat peste gard catre inginerie, si acum nimeni nu stie de ce predictiile au mers prost luna trecuta.

Rezolvarea nu e un framework nou sau un model mai sofisticat. E tratarea pipeline-ului ML ca sistemul de productie care este, cu aceeasi disciplina de testare pe care ai aplica-o oricarei alte cai critice.

Daca rulezi modele in productie fara porti de validare, nu faci MLOps. Faci development bazat pe speranta.

Ce Inseamna Asta Pentru Pipeline-ul Tau

Trei straturi. Validare date inainte de antrenament. Porti de calitate model inainte de promovare. Teste de paritate feature-uri intre antrenament si serving. Nimic din asta nu e exotic. E aceeasi disciplina CI/CD pe care o aplici deja codului aplicatiei, extinsa la pipeline-ul ML.

Rata de esec de 85% nu e despre modele proaste. E despre practici de inginerie proaste in jurul modelelor bune. Rezolvarea e plictisitoare, fara stralucire si complet in controlul tau.

Stii deja cum sa scrii teste. Stii deja cum sa configurezi porti CI/CD. Stii deja ca codul netestat e cod defect. Singurul lucru care lipseste e aplicarea aceleiasi discipline pipeline-ului ML care sta langa codul aplicatiei.

Incepe cu validarea datelor. Adauga porti model. Testeaza paritatea feature-urilor. Modelele tale merita aceeasi rigoare inginereasca ca API-urile tale.


Despre DeviDevs: Construim platforme ML, securizam sisteme AI si ajutam companiile sa se conformeze cu EU AI Act. devidevs.com


Sistemul tau AI e conform cu EU AI Act? Evaluare gratuita de risc - afla in 2 minute →

Ai nevoie de ajutor cu conformitatea EU AI Act sau securitatea AI?

Programeaza o consultatie gratuita de 30 de minute. Fara obligatii.

Programeaza un Apel

Weekly AI Security & Automation Digest

Get the latest on AI Security, workflow automation, secure integrations, and custom platform development delivered weekly.

No spam. Unsubscribe anytime.