Imperfezioni svelate la realtà intrigante dietro la creazione del nostro corso MLOps

Imperfezioni rivelano realtà intrigante dietro creazione corso MLOps

IL FRAMEWORK MLOPS A 7 PASSI PER IL FULL STACK

Lezione Bonus: Dietro le Quinte di un Progetto ML ‘Imperfetto’ – Lezioni e Approfondimenti

Foto di Hassan Pasha su Unsplash

Questo articolo rappresenta una lezione bonus finale di un corso di 7 lezioni che ti ha guidato passo dopo passo su come progettare, implementare e distribuire un sistema ML utilizzando buone pratiche MLOps. Durante il corso, hai costruito un modello pronto per la produzione per prevedere i livelli di consumo di energia per le prossime 24 ore in vari tipi di consumatori in Danimarca.

Durante il corso, hai imparato tutti i fondamenti per progettare, codificare e distribuire un sistema ML utilizzando un’architettura di batch-serving.

Questo corso si rivolge a ingegneri ML o software di livello intermedio / avanzato che vogliono migliorare le proprie competenze costruendo i propri progetti ML end-to-end.

Oggi i certificati sono ovunque. Costruire progetti avanzati end-to-end che poi puoi mostrare è il miglior modo per ottenere riconoscimento come ingegnere professionista.

Indice:

  • Introduzione al corso
  • Lezioni del corso
  • Fonte dati
  • Lezione Bonus: Dietro le Quinte di un Progetto ML ‘Imperfetto’ – Lezioni e Approfondimenti
  • Conclusione
  • Riferimenti

Introduzione al corso

Durante il corso di 7 lezioni, hai imparato come:

  • progettare un’architettura di batch-serving
  • utilizzare Hopsworks come feature store
  • progettare una pipeline di feature engineering che legge i dati da un API
  • costruire una pipeline di addestramento con messa a punto degli iperparametri
  • utilizzare W&B come piattaforma ML per tracciare i tuoi esperimenti, modelli e metadati
  • implementare una pipeline di previsione batch
  • utilizzare Poetry per creare i tuoi pacchetti Python
  • distribuire il tuo server PyPi privato
  • orchestrare il tutto con Airflow
  • utilizzare le previsioni per codificare un’app Web utilizzando FastAPI e Streamlit
  • utilizzare Docker per containerizzare il tuo codice
  • utilizzare Great Expectations per garantire la validazione e l’integrità dei dati
  • monitorare le prestazioni delle previsioni nel tempo
  • distribuire tutto su GCP
  • costruire una pipeline CI/CD utilizzando le Azioni di GitHub

Se non hai seguito la serie e sembra qualcosa che ti interessa, voglio farti sapere che dopo aver completato il corso, capirai tutto ciò che ho detto prima. Inoltre, vedrai PERCHÉ ho usato tutti questi strumenti e come funzionano insieme come un sistema.

Se vuoi ottenere il massimo da questo corso, ti suggerisco di accedere al repository GitHub contenente il codice di tutte le lezioni. Questo corso è progettato per leggere rapidamente e replicare il codice lungo gli articoli.

Durante il corso, hai imparato come implementare il diagramma qui sotto. Dopo averlo spiegato passo passo, non sembra più così spaventoso, vero?

Diagramma dell'architettura costruita durante il corso [Immagine dell'autore].

In questa lezione bonus finale, vogliamo parlare di miglioramenti potenziali che possono essere fatti all’architettura attuale e alle scelte di progettazione fatte durante il corso. Vogliamo anche evidenziare i compromessi che abbiamo dovuto fare e darti alcune idee per futuri progetti.

Pensaci come la sezione dietro le quinte 👀

Lezioni del corso:

  1. Batch Serving. Feature Stores. Feature Engineering Pipelines.
  2. Training Pipelines. ML Platforms. Hyperparameter Tuning.
  3. Batch Prediction Pipeline. Package Python Modules with Poetry.
  4. Private PyPi Server. Orchestrate Everything with Airflow.
  5. Data Validation for Quality and Integrity using GE. Model Performance Continuous Monitoring.
  6. Consume and Visualize your Model’s Predictions using FastAPI and Streamlit. Dockerize Everything.
  7. Deploy All the ML Components to GCP. Build a CI/CD Pipeline Using Github Actions.
  8. [Bonus] Dietro le Quinte di un Progetto ML ‘Imperfetto’ – Lezioni e Approfondimenti

La lezione bonus condividerà apertamente i compromessi, le scelte di progettazione e i miglioramenti potenziali del corso.

Pertanto, ti incoraggiamo vivamente a leggere il resto del corso se sei interessato a costruire sistemi di apprendimento automatico pronti per la produzione 👇

Un framework per la costruzione di un pipeline di ingegneria delle feature pronto per la produzione

Lezione 1: Batch Serving. Feature Stores. Feature Engineering Pipelines.

towardsdatascience.com

Origine dei dati

Abbiamo utilizzato un’API gratuita e aperta che fornisce valori di consumo energetico orario per tutti i tipi di consumatori energetici in Danimarca [1].

Forniscono un’interfaccia intuitiva in cui è possibile interrogare e visualizzare i dati. Puoi accedere ai dati qui [1].

I dati hanno 4 attributi principali:

  • Ora UTC: la data e l’ora UTC in cui è stato osservato il punto dati.
  • Area di prezzo: la Danimarca è divisa in due aree di prezzo: DK1 e DK2 — divise dal Grande Belt. DK1 si trova ad ovest del Grande Belt, mentre DK2 si trova ad est del Grande Belt.
  • Tipo di consumatore: Il tipo di consumatore è il codice DE35 dell’industria, di proprietà e mantenuto da Danish Energy.
  • Consumo totale: Consumo totale di elettricità in kWh

Nota: le osservazioni hanno un ritardo di 15 giorni! Ma per il nostro caso d’uso dimostrativo, non è un problema, poiché possiamo simulare gli stessi passaggi come se fosse in tempo reale.

Uno screenshot della nostra web app che mostra come abbiamo previsto il consumo energetico per area = 1 e consumer_type = 212 [Immagine dell'autore].

I punti dati hanno una risoluzione oraria. Ad esempio: “2023–04–15 21:00Z”, “2023–04–15 20:00Z”, “2023–04–15 19:00Z”, ecc.

Modelleremo i dati come serie temporali multiple. Ogni tupla unica di area di prezzo e tipo di consumatore rappresenta la sua serie temporale unica.

Quindi, costruiremo un modello che prevede in modo indipendente il consumo energetico per le prossime 24 ore per ogni serie temporale.

Guarda il video qui sotto per capire meglio come sono fatti i dati 👇

Panoramica del corso e dell’origine dei dati [Video dell’autore].

Lezione bonus: dietro le quinte di un progetto di apprendimento automatico “imperfetto” — lezioni e conoscenze acquisite

Niente più chiacchiere. Andiamo direttamente dietro le quinte 🔥

Diagramma dell'architettura costruita durante il corso [Immagine dell'autore].

Codice generale

#1. Codice duplicato

L’elefante nella stanza è che abbiamo avuto parecchio codice duplicato tra diversi moduli Python, che non rispetta il potente principio DRY.

Come ad esempio i file settings.py e utils.py delle pipeline di ML o il componente di grafico a linee e menu a tendina dell’interfaccia utente .

Questo codice potrebbe essere rifattorizzato in un modulo comune condiviso tra tutti gli altri moduli.

#2. Classi, non funzioni!

Modellare il tuo codice usando le classi è una buona pratica, ma abbiamo usato solo funzioni durante il corso.

Avremmo potuto creare una classe centrale per ogni pipeline, come FeaturesExtractor, Trainer e BatchPredictor.

Inoltre, invece di restituire semplici dizionari contenenti i metadati di una esecuzione, avremmo potuto creare una classe RunResult per avere un maggiore controllo su come i dati vengono passati.

#3. Nessun test 😟

Ogni base di codice è migliore con un insieme di test di unità e integrazione per convalidare tutte le modifiche apportate al codice.

Design della pipeline

#1. Il DAG ha uno stato

Perché il DAG ha uno stato, non è molto facile eseguirlo in parallelo. Il nostro problema è che hai bisogno di previsioni dalle esecuzioni precedenti per calcolare le metriche di monitoraggio.

Quindi, per definizione, non puoi eseguire più istanze parallele dello stesso DAG in diversi momenti.

Quando diventa un problema?

Quando si esegue il backfilling. Diciamo che si vuole eseguire il backfilling ogni ora per gli ultimi 2 mesi. Se si esegue il programma in sequenza, ci vorrà per sempre.

Come soluzione, suggeriamo di spostare il componente di monitoraggio in un diverso DAG.

#2. Evitare di usare “:latest” come versione delle risorse

Se si utilizza il tag “:latest” per accedere a risorse come:

  • l’artefatto del modello,
  • i dati (vista delle caratteristiche del Feature Store o insieme di dati di formazione),
  • l’artefatto della configurazione migliore, ecc.

… si introducono dipendenze tra più esecuzioni della pipeline di ML.

È sottile, ma lasciatemi spiegare 👇

Supponiamo che si eseguano in parallelo 2 pipeline di ML: A e B. La pipeline A genera per prima una nuova versione dell’insieme di dati. Poi, per qualsiasi motivo, la pipeline B avvia la pipeline di formazione prima della pipeline A e accede alla versione “latest” dell’insieme di dati, che è stata creata dalla pipeline A.

Ciò è noto anche come “condizione di gara” nel calcolo parallelo.

In questo caso, può essere facilmente risolto codificando la versione delle risorse tra i compiti della stessa pipeline.

Ad esempio, anziché accedere a ” dataset:latest “, accedere a ” dataset:v3 “.

Come avete visto, la velocità è cruciale quando si tratta di backfilling. Pertanto, eseguire un DAG in parallelo è essenziale a lungo termine.

Airflow

#1. Utilizzare i Docker Tasks

Questo non è necessariamente un problema, ma volevo evidenziare che invece di utilizzare un ambiente Python, si potrebbe anche aver caricato il proprio codice all’interno di un contenitore Docker – Task Docker Decorator Docs [2].

Il principale vantaggio è che questo rende il sistema più scalabile.

Ma la buona notizia è che il processo appreso durante il corso è estremamente simile. Invece di caricare il pacchetto Python in un registro PyPi, si carica un’immagine Docker in un registro Docker.

#2. Compiti atomici più piccoli

Nel nostro caso, i compiti all’interno del DAG contengono molta logica. Essi essenzialmente eseguono un’intera applicazione.

Questo non è necessariamente male, ma dividerlo in pezzi più piccoli è una buona pratica. Pertanto, il debug, il monitoraggio e il riavvio del DAG da un determinato punto di fallimento sono più accessibili.

Ad esempio, anziché leggere/scrivere dati da GCS in Python, potremmo avere usato uno degli operatori GCS di Airflow – GCS Airflow operators [3].

#3. Iniettare le impostazioni di ottimizzazione degli iperparametri da Airflow

Attualmente, le impostazioni di ottimizzazione degli iperparametri sono codificate in un file configs/gridsearch.py.

Questo non è molto flessibile, poiché l’unica opzione per modificare la configurazione è quella di caricare una nuova versione su git, il che non è molto pratico.

Una soluzione sarebbe quella di iniettare le impostazioni da un file YAML, che può essere facilmente aggiunto al flusso di lavoro Airflow.

Un bello strumento di configurazione YAML per i modelli di ML è Hydra di facebookresearch. Provalo, mi ringrazierai dopo.

Monitoraggio

#1. Non monitorare lo stato di salute del sistema

Avremmo potuto facilmente aggiungere un meccanismo di monitoraggio dello stato di salute del sistema inviando periodicamente una richiesta al punto finale /health dell’API.

Questo avrebbe potuto essere riflesso con un pannello verde/rosso sull’interfaccia utente.

#2. Nessun avviso

In base alla metrica MAPE che stiamo costantemente monitorando, avremmo potuto aggiungere un sistema di avvisi come:

  • avvisi [soglia_B > MAPE > soglia_A]: informare l’ingegnere che potrebbe esserci qualcosa che non va;
  • allarmi [MAPE > soglia_B > soglia_A]: informare l’ingegnere che c’è qualcosa che non va + attivare la logica di ottimizzazione degli iperparametri.

#3. Arricchire l’interfaccia utente

Avremmo potuto aggiungere la metrica MAPE per ogni serie temporale individualmente.

#4. Non reinventare la ruota!

Abbiamo implementato un mini strumento di monitoraggio solo come esempio. Ma in uno scenario del mondo reale, dovresti sfruttare strumenti esistenti come EvidentlyAI o Arize.

Questi strumenti e pacchetti ti danno già soluzioni professionali. In questo modo puoi concentrarti sull’aggiunta di valore.

#5. Monitorare le deviazioni

Come piacevole da avere, sarebbe stato utile anche monitorare le deviazioni dei dati e dei concetti. Ma poiché abbiamo GT quasi in tempo reale, questo è solo un bel tocco.

Web App – Pannello di previsioni

#1. Arricchire l’interfaccia utente

L’interfaccia utente è piuttosto basilare. Ad esempio, avremmo potuto arricchire l’interfaccia utente aggiungendo testi e avvisi quando i dati non sono validi (non superano la suite di convalida).

#2. Richiediamo i dati in modo ingenuo

Le nostre richieste all’API sono piuttosto ingenui. Di solito, questi passaggi sono protetti da un insieme di eccezioni che catturano diversi comportamenti sui codici di risposta 300, 400 e 500.

#3. Le impostazioni sono codificate

Avremmo potuto iniettare le impostazioni utilizzando un file .env. Ciò avrebbe reso il programma più configurabile. Simile al codice Web App FastAPI.

Pipeline di distribuzione e CI/CD

#1. Implementazione parziale di CI

Per completare completamente la pipeline CI/CD, avremmo potuto creare le immagini Docker dell’App Web, caricarle su un registro Docker e scaricarle da lì durante la distribuzione sulla VM GCP.

Stessa cosa quando si costruiscono i pacchetti Python con Poetry.

Questo è come si fa “by the book”.

Inoltre, se avessimo avuto dei test, avremmo dovuto eseguirli prima di distribuire il codice.

Un’altra idea è eseguire comandi come flake8 per verificare che il codice sia scritto utilizzando le convenzioni PEP8.

#2. Host PyPi su una VM diversa

Inoltre, sarebbe consigliabile ospitare il server PyPi su una VM diversa o almeno completamente indipendente dal componente Airflow.

Così facendo, il sistema sarebbe stato più modulare e flessibile.

#3. Ospitare i file .env su un bucket GCS

Invece di completare e copiare manualmente i file .env, avremmo potuto archiviarli su un bucket GCS e scaricarli automaticamente all’interno della pipeline CI/CD.

#4. Automatizzare l’infrastruttura

Hai visto quanto sia noioso impostare manualmente tutte le risorse GCP di cui hai bisogno … e questa è una piccola infrastruttura. Immagina come sia quando hai 100 o 1000 componenti all’interno della tua infrastruttura.

Ecco perché si consiglia di automatizzare la creazione della tua infrastruttura con strumenti IoC come Terraform.

Conclusione

Se sei arrivato fin qui, voglio ringraziarti e dirti quanto apprezzi che hai seguito il mio corso Full Stack 7-Steps MLOps Framework 🙏

In questa lezione bonus, hai visto che nessun sistema è perfetto e devi sempre fare certi compromessi a causa di:

  • vincoli di tempo,
  • vincoli di risorse,
  • cattiva pianificazione.

Ora che vedi che tutti stanno ancora imparando e non sanno tutto, non hai scuse per non costruire il tuo prossimo progetto fantastico 🔥

Connettiamoci su LinkedIn e fammi sapere se hai domande o solo condividi i tuoi progetti con me.

Accedi qui al repository GitHub.

💡 Il mio obiettivo è aiutare gli ingegneri di machine learning a migliorare nella progettazione e nella produzione di sistemi ML. Seguimi su LinkedIn o iscriviti alla mia newsletter settimanale per maggiori informazioni!

🔥 Se ti piace leggere articoli come questo e desideri supportare la mia scrittura, considera di diventare un membro di Nisoo. Utilizzando il mio link di riferimento, puoi supportarmi senza costi aggiuntivi e godere di un accesso illimitato alla ricca collezione di storie di Nisoo.

Come membro di Nisoo, una parte della tua quota di iscrizione va agli scrittori che leggi e hai accesso completo a ogni storia…

pauliusztin.medium.com

Grazie ✌🏼!

Riferimenti

[1] Consumo di energia per codice di settore DE35 da API Denmark, Denmark Energy Data Service

[2] Decoratore Docker per Task, Documentazione Airflow

[3] Operatori GCS Airflow, Documentazione Airflow