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.

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:
- Il versionamento deve comprendere dati e modelli, non solo il codice. Git gestisce bene il codice, ma fatica con dataset da diversi gigabyte e pesi dei modelli in formato binario. Strumenti come DVC (Data Version Control) e i registri dei modelli di MLflow o Weights & Biases colmano questa lacuna tracciando i file di grandi dimensioni e collegando una specifica versione dei dati, un commit del codice e il modello risultante in un’unica unità riproducibile.
- I test devono includere controlli statistici, non soltanto asserzioni. Un test unitario che verifica se una funzione restituisce il tipo corretto non dice nulla sul fatto che un nuovo batch di dati di addestramento abbia subito una deriva, presenti picchi di valori nulli o contenga un join non funzionante. I framework di validazione dei dati esistono proprio per questo: Great Expectations, rinominato GX Core con il rilascio della versione 1.0 nell’agosto 2024, è quello a cui ricorre la maggior parte dei team.
- «Superare i test» non è una questione binaria: dipende da una soglia. La CI per il software chiede: «Si è rotto qualcosa?» La CI per il machine learning chiede: «Accuratezza/AUC/latenza sono rimaste entro un intervallo accettabile, e il modello è ancora migliore (o non significativamente peggiore) di quello in produzione?» Questo richiede confronti con un riferimento, non asserzioni del tipo superato/non superato.
Le tre componenti da sottoporre a versionamento
Prima di costruire le pipeline, decidete come sottoporre a versionamento ciascuna componente: farlo a posteriori è complicato.
- Codice: Git, come di consueto. Script di addestramento, preprocessing, codice per il serving, infrastruttura come codice.
- 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.yamldescrive 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 reproriesegue soltanto le fasi i cui input sono cambiati, offrendo alle pipeline di machine learning un’esecuzione incrementale in stile Makefile. - 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
championechallenger. 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:
- Lint e test unitari — i consueti
pytest/ruff/blacksul codice che non dipende dai dati (logica delle trasformazioni, lettura della configurazione, contratti API per il livello di serving). - 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.
- 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. - 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.
- 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.
- 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.
- Registrazione — se tutti i controlli vengono superati e il branch è
main, registrare la versione del modello in MLflow o W&B Registry con un aliaschallenger, senza assegnarle direttamentechampion. - Distribuzione (una fase separata, spesso subordinata a un intervento manuale o a una soglia delle metriche) — promuovere
challengerachampione 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:
- Riaddestramento programmato — una pipeline avviata da cron (Airflow, Dagster o Prefect) ripete l’addestramento sulla finestra di dati più recente; è utile quando le distribuzioni dei dati cambiano gradualmente e in modo prevedibile (domanda stagionale, prezzi).
- Riaddestramento attivato dal drift — un processo di monitoraggio (si veda sotto) rileva che il drift delle variabili o delle previsioni ha superato una soglia e avvia automaticamente la pipeline di addestramento, invece di attendere la successiva esecuzione programmata.
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:
- Registrare ogni modello che supera la CI, associandogli l’hash del commit, la versione dei dati (commit DVC) e le metriche di valutazione.
- Usare alias (
champion,challenger,archived) anziché nomi delle fasi inseriti come testo libero: in questo modo la promozione richiede una sola chiamata API atomica e resta una traccia di audit chiara di che cosa è stato promosso, quando e da quale esecuzione della pipeline. - Subordinare la promozione a
championall’approvazione umana per i modelli ad alto impatto, oppure automatizzarla completamente quando il passaggio di confronto con il modello di riferimento è affidabile e la posta in gioco è più bassa. - Collegare un webhook del registro (sia il supporto per i webhook di MLflow — ancora sperimentale e disponibile solo in MLflow open source con un backend SQL — sia quello di W&B sono utilizzabili a questo scopo) per avviare automaticamente il flusso di distribuzione al momento della promozione, completando il percorso dalla registrazione al rilascio senza un passaggio manuale di distribuzione.
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.
- Distribuzione shadow: il nuovo modello riceve una copia speculare del traffico di produzione e genera previsioni che vengono registrate, ma mai restituite agli utenti. Il rischio per gli utenti è nullo; è utile per verificare la latenza e la correttezza funzionale, ma non permette di valutare l’impatto effettivo sull’attività, perché i suoi output non influenzano mai una decisione. Sia KServe sia Seldon Core supportano l’invio di copie delle richieste di produzione a un modello shadow.
- Distribuzione canary: una piccola percentuale del traffico reale viene indirizzata alla nuova versione del modello e i suoi risultati vengono confrontati con quelli del modello in uso prima di aumentare la quota. KServe offre questa funzione tramite il campo
canaryTrafficPercentnella specifica InferenceService; Seldon Core ottiene lo stesso risultato ripartendo il traffico tramite Istio, con una precisazione sulla licenza: dal 22 gennaio 2024 Seldon distribuisce Core 1, Core 2, Alibi Detect e Alibi Explain con la Business Source License v1.1 anziché con Apache 2.0, perciò il loro uso gratuito è limitato agli ambienti non di produzione. MLServer, il runtime di serving sottostante, è rimasto sotto licenza Apache 2.0. Entrambi consentono di spostare progressivamente il traffico (per esempio, 5% → 25% → 100%) monitorando le metriche a ogni passaggio e di tornare immediatamente alla versione precedente ripristinando la ripartizione iniziale.
| Fase del rilascio | Traffico verso il nuovo modello |
|---|---|
| 1 | 5% |
| 2 | 25% |
| 3 | 100% |
- Test A/B: è una pratica correlata, ma risponde a una domanda di business (la variante B ottiene più conversioni?) anziché a una domanda sulla sicurezza (la variante B non è peggiore?). In genere dura più a lungo e richiede una pianificazione della potenza statistica, mentre la distribuzione canary è una verifica di sicurezza di breve durata.
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:
- Codice: l’hash esatto del commit (Git).
- Dati: la versione esatta dei dati tracciata da DVC (oppure l’ID di uno snapshot immutabile di un feature store o di un data warehouse).
- Ambiente: addestramento in container con versioni delle dipendenze fissate (
requirements.txtcon hash, oppure la creazione di un’immagine basata su un lockfile), anziché taglatestche cambiano nel tempo. - Iperparametri e seed casuali: registrati come parametri dell’esecuzione in MLflow/W&B, non inseriti direttamente nel codice e poi dimenticati.
- Non determinismo legato all’hardware: quando il non determinismo delle GPU conta (per esempio, in ambienti regolamentati), documentarlo esplicitamente anziché dare per scontata una riproducibilità che non esiste. Alcune operazioni non sono realmente riproducibili bit per bit tra diverse versioni dei driver GPU, e fingere il contrario causa più danni che riconoscere il limite.
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.