Vai al contenuto
AI.info

Guide pratiche

CI/CD per i progetti di machine learning

Come implementare l’integrazione e il deployment continui nei sistemi di machine learning. Una panoramica delle strategie di test, dei controlli di validazione dei modelli, del riaddestramento automatizzato e delle pipeline di deployment.

CI/CD per i progetti di machine learning

AI.info Team ·

Perché la CI/CD per il machine learning non è la CI/CD per il software

La CI/CD tradizionale sottopone a versionamento e a test un solo artefatto: il codice. Una pull request avvia una build, una suite di test e un deployment. I sistemi di machine learning hanno tre artefatti che cambiano indipendentemente e interagiscono in modo imprevedibile: codice (script di addestramento, trasformazioni delle feature, logica di serving), dati (set di addestramento, feature store, distribuzioni di riferimento) e modelli (pesi, iperparametri, architettura). Una suite di test superata dal codice non dice nulla sul fatto che il modello addestrato sui dati di questa settimana continui o meno a superare la soglia di prestazioni.

Ecco perché i team MLOps parlano di pipeline, anziché di pipeline a cui aggiungere il resto in un secondo momento: la pipeline di addestramento stessa è un artefatto della build che deve essere sottoposto a versionamento, testato e riproducibile, non soltanto il codice che la definisce.

Ne derivano direttamente tre conseguenze:

Le tre componenti da sottoporre a versionamento

Prima di costruire le pipeline, decidete come sottoporre a versionamento ciascuna componente: farlo a posteriori è complicato.

  1. Codice: Git, come di consueto. Script di addestramento, preprocessing, codice per il serving, infrastruttura come codice.
  2. Dati: DVC è l’opzione open source più diffusa: conserva in Git i riferimenti ai dati, mentre i file veri e propri risiedono su S3, GCS, Azure Blob o un remote locale. dvc.yaml descrive le fasi della pipeline (preparazione → addestramento → valutazione) come un grafo aciclico diretto (DAG), con output memorizzati nella cache e identificati tramite hash del contenuto. dvc repro riesegue soltanto le fasi i cui input sono cambiati, offrendo alle pipeline di machine learning un’esecuzione incrementale in stile Makefile.
  3. Modelli + esperimenti: MLflow Tracking registra parametri, metriche e artefatti per ogni esecuzione; MLflow Model Registry (o il Registry di Weights & Biases) conserva poi le versioni registrate dei modelli con alias come champion e challenger. Questi sostituiscono le precedenti etichette di fase (Staging/Production), che MLflow ha deprecato a partire dalla versione 2.9.0 a favore di alias modificabili con cui promuovere le versioni; gli alias restano il meccanismo documentato in MLflow 3, l’attuale serie principale di versioni dall’11 giugno 2025. Il Registry di W&B fa lo stesso con gli alias degli artefatti e può attivare un webhook per avviare una pipeline di deployment quando viene promossa una nuova versione.

Un dvc.yaml minimale che descrive una fase di addestramento si presenta così:

stages:
  prepare:
    cmd: python src/prepare.py --input data/raw --output data/processed
    deps:
      - src/prepare.py
      - data/raw
    outs:
      - data/processed

  train:
    cmd: python src/train.py --data data/processed --out models/model.pkl
    deps:
      - src/train.py
      - data/processed
    params:
      - train.learning_rate
      - train.n_estimators
    outs:
      - models/model.pkl
    metrics:
      - metrics/train_metrics.json:
          cache: false

  evaluate:
    cmd: python src/evaluate.py --model models/model.pkl --data data/processed
    deps:
      - src/evaluate.py
      - models/model.pkl
    metrics:
      - metrics/eval_metrics.json:
          cache: false

dvc.lock registra gli hash esatti di ogni dipendenza e output, quindi dvc repro mette a disposizione una cache della build: se data/raw e src/prepare.py non sono cambiati, DVC salta prepare e riutilizza l’output memorizzato nella cache. È l’equivalente più vicino, per il machine learning, alla cache di una build software, ed è ciò che rende le esecuzioni della CI abbastanza rapide da essere praticabili.

Una pipeline concreta: cosa viene eseguito a ogni push

Una pipeline CI realistica per un repository di ML, integrata con GitHub Actions, esegue in genere queste fasi nell’ordine indicato e si interrompe subito se falliscono i controlli meno costosi:

  1. Lint e test unitari — i consueti pytest / ruff / black sul codice che non dipende dai dati (logica delle trasformazioni, lettura della configurazione, contratti API per il livello di serving).
  2. Validazione dei dati — eseguire GX Core (o un equivalente) sui dati di addestramento in arrivo o aggiornati. I controlli comprendono la conformità allo schema, le soglie per la percentuale di valori nulli, la cardinalità delle categorie, l’integrità referenziale e verifiche di plausibilità delle distribuzioni (per esempio, che una colonna numerica resti entro i limiti minimo e massimo osservati storicamente). Questa fase dovrebbe bloccare la pipeline prima che vengano impiegate risorse di calcolo per l’addestramento. GX Core 1.0 ha riscritto l’API e il supporto per la precedente serie 0.18 è terminato: uno script di checkpoint ereditato da un progetto precedente alla versione 1.0 deve quindi essere adattato prima di poter essere eseguito.
  3. Esecuzione della pipeline tramite DVC — dvc repro (oppure l’avvio della fase equivalente in Airflow/Kubeflow/Dagster/Prefect) per riprodurre la pipeline di addestramento con l’esatta combinazione di codice e dati.
  4. Valutazione del modello rispetto a una baseline — confrontare le metriche del nuovo modello con quelle del modello attualmente registrato in produzione, non soltanto con una soglia assoluta. Così si rileva un degrado graduale che una soglia fissa non intercetterebbe.
  5. Test del modello — test comportamentali oltre le metriche aggregate: test di invarianza (la previsione non dovrebbe cambiare in seguito a perturbazioni irrilevanti per l’etichetta), test delle aspettative direzionali (aumentare il valore di una variabile nota per il suo effetto positivo non dovrebbe ridurre una previsione positiva) e test di funzionalità minima su un piccolo sottoinsieme selezionato, secondo l’approccio reso popolare da «ML Test Score» di Google e dalla metodologia CheckList di Ribeiro et al.
  6. Generazione del report — CML (Continuous Machine Learning, di Iterative, che sviluppa DVC) pubblica un commento sulla PR con metriche, grafici e un diff rispetto al branch di base, così chi esamina la PR vede le prestazioni del modello come vedrebbe un diff del codice. CML funziona ancora, ma mostra poca attività: l’avviso di manutenzione più recente fissato nel suo README risale al 2023. Va quindi considerato una dipendenza stabile, non una in fase di sviluppo attivo.
  7. Registrazione — se tutti i controlli vengono superati e il branch è main, registrare la versione del modello in MLflow o W&B Registry con un alias challenger, senza assegnarle direttamente champion.
  8. Distribuzione (una fase separata, spesso subordinata a un intervento manuale o a una soglia delle metriche) — promuovere challenger a champion e procedere al rilascio tramite distribuzione canary o shadow.

Un workflow GitHub Actions essenziale che implementa le fasi 1–4 e 6:

name: ml-ci

on:
  pull_request:
    branches: [main]

jobs:
  validate-and-train:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Lint and unit tests
        run: |
          ruff check src/
          pytest tests/unit -q

      - name: Data validation (GX Core)
        run: python scripts/run_gx_checkpoint.py --checkpoint training_data_checkpoint

      - name: Pull data and reproduce pipeline
        uses: iterative/setup-dvc@v1
      - run: |
          dvc pull
          dvc repro
          dvc push

      - name: Compare against production baseline
        run: python scripts/compare_to_baseline.py --new metrics/eval_metrics.json

      - name: Generate CML report
        uses: iterative/setup-cml@v2
      - env:
          REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          echo "## Model evaluation" > report.md
          cat metrics/eval_metrics.json >> report.md
          cml comment create report.md

Addestramento continuo (CT)

Il CT è l’estensione del CD specifica per il ML: anziché distribuire nuovo codice, o in aggiunta a questa operazione, il sistema riaddestra e ridistribuisce automaticamente il modello stesso. Due modalità comuni di attivazione:

Per l’orchestrazione, Airflow 3.x, Dagster, Kubeflow Pipelines e Prefect 3 sono le opzioni principali oggi; il modello di Dagster incentrato sugli asset (definire i prodotti di dati e modelli che una pipeline deve generare e lasciare allo scheduler il compito di stabilire cosa debba essere rigenerato) si presta particolarmente bene al CT, perché «questo modello è obsoleto rispetto ai suoi input?» è esattamente il tipo di questione legata all’aggiornamento degli asset a cui Dagster è progettato per rispondere. Kubeflow Pipelines resta la scelta predefinita quando l’addestramento viene già eseguito su Kubernetes.

Qualunque sia l’orchestratore che esegue il CT, dovrebbe richiamare la stessa pipeline usata nella CI e basata su DVC e sul registro: l’obiettivo è che un riaddestramento programmato e un’esecuzione CI avviata da una PR percorrano gli stessi identici percorsi di codice, con la sola differenza di ciò che li attiva.

Il registro dei modelli come punto di passaggio

Il registro è il punto di raccordo tra la pipeline di addestramento e quella di serving e distribuzione. Va considerato la fonte autorevole per stabilire «quali modelli possono essere in produzione», non un semplice archivio di metadati:

Strategie di distribuzione: canary e shadow

Distribuire una nuova versione di un modello comporta rischi diversi rispetto a distribuire nuovo codice: spesso il problema passa inosservato (previsioni sbagliate, non arresti anomali). Per questo, le strategie di rilascio graduale sono ancora più importanti che nella normale distribuzione continua delle applicazioni web.

Fase del rilascio Traffico verso il nuovo modello
1 5%
2 25%
3 100%

Per i team che non usano un’infrastruttura di serving nativa per Kubernetes, BentoML e soluzioni cloud-native equivalenti (endpoint SageMaker con varianti di produzione, ripartizione del traffico di Vertex AI) offrono funzionalità canary e shadow comparabili senza richiedere un’intera infrastruttura KServe/Seldon/Istio.

Monitoraggio del drift in produzione

Una volta distribuito, il modello richiede un monitoraggio che vada oltre le metriche dell’infrastruttura (latenza, tasso di errore). Evidently AI è lo strumento open source più utilizzato a questo scopo: esegue oltre 20 test statistici (indice di stabilità della popolazione, Kolmogorov-Smirnov, divergenza di Jensen-Shannon e altri, a seconda del tipo di caratteristica) per rilevare il drift dei dati (cambiamenti nelle distribuzioni delle caratteristiche in input) e il drift delle previsioni o delle variabili target (cambiamenti nelle distribuzioni delle previsioni o delle variabili target). Produce inoltre sia report interattivi sia suite di test leggibili automaticamente, i cui risultati possono essere usati come condizione per proseguire una pipeline.

L’approccio pratico consiste nel programmare un controllo del drift con Evidently (giornaliero o per ogni batch) che confronti gli input attuali in produzione con il dataset di riferimento usato durante l’addestramento. Se il drift supera una soglia configurata per le caratteristiche principali, si può avvisare il personale reperibile oppure avviare automaticamente la pipeline CT descritta sopra. Occorre registrare le previsioni e, quando disponibili, le etichette effettive che arrivano in un secondo momento, così da poter misurare il drift delle prestazioni, non solo quello degli input: il drift degli input senza un peggioramento delle prestazioni è comune e non richiede sempre un intervento.

Lista di controllo per la riproducibilità

Tutto quanto descritto sopra funziona solo se un’esecuzione dell’addestramento può essere riprodotta esattamente. In concreto, ciò significa fissare:

Una versione del modello registrata senza che tutti e cinque questi elementi siano tracciabili è, in pratica, irriproducibile e va considerata una passività anziché una risorsa distribuibile: se non si riesce a ricostruire esattamente come è stata prodotta, non si può analizzare un incidente in produzione, rispondere a una richiesta di verifica normativa o ripristinarla in sicurezza.

Mettere insieme i pezzi

Il principio è che le modifiche al codice, ai dati e ai modelli richiedono ciascuna un proprio sistema di versionamento e validazione, ma convergono in un’unica definizione della pipeline (le fasi di DVC o un DAG di orchestrazione) e in un unico punto di passaggio (il registro dei modelli) prima della distribuzione. La CI valida dati e codice in modo rapido e poco costoso; la pipeline di addestramento riproduce il modello in modo deterministico; il registro controlla e documenta il passaggio alla fase successiva; una distribuzione canary o shadow limita la portata dei possibili danni; e il monitoraggio del drift chiude il ciclo, riportando le informazioni all’addestramento continuo.

Nessuno di questi elementi è insolito: DVC, MLflow, GX Core, Evidently e KServe sono strumenti open source maturi e ampiamente utilizzati, mentre Seldon Core appartiene alla stessa categoria di strumenti ma richiede una licenza a pagamento per l’uso in produzione. A distinguere i sistemi di ML che restano affidabili in produzione da quelli che si deteriorano senza dare nell’occhio è però la disciplina necessaria a collegare questi elementi in un’unica pipeline, anziché trattare l’addestramento dei modelli come un processo manuale parallelo aggiunto alla normale CI/CD del software.

Esplora

Altri articoli