Ottimizza il Tuo Codice di Machine Learning con MLflow e Hydra: Un Guida Completa
Red Hot Cyber
Condividi la tua difesa. Incoraggia l'eccellenza. La vera forza della cybersecurity risiede nell'effetto moltiplicatore della conoscenza.
Cerca
Enterprise BusinessLog 970x120 1
Crowdstrike 320×100
Ottimizza il Tuo Codice di Machine Learning con MLflow e Hydra: Un Guida Completa

Ottimizza il Tuo Codice di Machine Learning con MLflow e Hydra: Un Guida Completa

Marcello Politi : 23 Maggio 2024 22:22

Quando sviluppiamo modelli di Machine Learning, solitamente dobbiamo eseguire diversi esperimenti per capire quale settaggio degli iperparametri risulta essere il migliore per un determinato algoritmo. Questo può spesso portare a un codice lungo e sporco e a perdere traccia di quale risultato corrisponda a quale settaggio. Ho visto spesso sviluppatori fare “hard coding” (cioè inserire gli iperparametri direttamente nel codice) in modo rigido. Lanciavano quindi l’esperimento e annotavano il risultato in un file Excel. Sono certo che possiamo migliorare questo flusso di lavoro.

Se siete interessati nel mio ultimo articolo ho parlato di come sviliuppare da zero una pipeline con MLflow.

Oggi voglio aggiungere un altro livello di complessità e spiegare come integrare anche Hydra nel flusso. Hydra è un fantastico strumento open-source che, tra le altre cose, consente di eseguire test con diversi settaggi del modello.

MLflow e Hydra


Christmas Sale

Christmas Sale -40%
𝗖𝗵𝗿𝗶𝘀𝘁𝗺𝗮𝘀 𝗦𝗮𝗹𝗲! Sconto del 𝟰𝟬% 𝘀𝘂𝗹 𝗽𝗿𝗲𝘇𝘇𝗼 𝗱𝗶 𝗰𝗼𝗽𝗲𝗿𝘁𝗶𝗻𝗮 del Corso "Dark Web & Cyber Threat Intelligence" in modalità E-Learning sulla nostra Academy!🚀 Fino al 𝟯𝟭 𝗱𝗶 𝗗𝗶𝗰𝗲𝗺𝗯𝗿𝗲, prezzi pazzi alla Red Hot Cyber Academy. 𝗧𝘂𝘁𝘁𝗶 𝗶 𝗰𝗼𝗿𝘀𝗶 𝘀𝗰𝗼𝗻𝘁𝗮𝘁𝗶 𝗱𝗲𝗹 𝟰𝟬% 𝘀𝘂𝗹 𝗽𝗿𝗲𝘇𝘇𝗼 𝗱𝗶 𝗰𝗼𝗽𝗲𝗿𝘁𝗶𝗻𝗮.
Per beneficiare della promo sconto Christmas Sale, scrivici ad [email protected] o contattaci su Whatsapp al numero di telefono: 379 163 8765.


Supporta Red Hot Cyber attraverso: 

  1. L'acquisto del fumetto sul Cybersecurity Awareness
  2. Ascoltando i nostri Podcast
  3. Seguendo RHC su WhatsApp
  4. Seguendo RHC su Telegram
  5. Scarica gratuitamente “Byte The Silence”, il fumetto sul Cyberbullismo di Red Hot Cyber

Se ti piacciono le novità e gli articoli riportati su di Red Hot Cyber, iscriviti immediatamente alla newsletter settimanale per non perdere nessun articolo. La newsletter generalmente viene inviata ai nostri lettori ad inizio settimana, indicativamente di lunedì.

Iniziamo quindi a scrivere il primo file Python per addestrare un semplice modello di ML come il Random Forest. Possiamo utilizzare il dataset Titanic e importarlo tramite la libreria Seaborn.

Il dataset Titanic ha una licenza open-source (licenza MIT). È possibile trovarlo su GitHub a questo indirizzo.

All’interno del nostro progetto, creiamo una sottocartella che sarà un componente della pipeline MLflow e chiamiamola “random_fortest”. All’interno di questo componente generiamo lo script “run.py” e scriviamo il codice per eseguire l’addestramento del modello.

In questo pezzo di codice, implementeremo le fasi standard di preelaborazione e di addestramento che credo non necessitino di troppe spiegazioni.

import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import seaborn as sns


def go():
    titanic = sns.load_dataset("titanic")

    # Preprocess the data: drop rows with missing values and convert categorical to numerical
    titanic.dropna(subset=["age", "embarked", "deck"], inplace=True)
    titanic = pd.get_dummies(
        titanic,
        columns=["sex", "embarked", "class", "who", "deck", "embark_town", "alive"],
        drop_first=True,
    )

    # Define features and target
    X = titanic.drop(["survived"], axis=1)
    y = titanic["survived"]

    # Split the data into training and test sets
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=42
    )

    # Initialize RandomForestClassifier
    rf = RandomForestClassifier(
        n_estimators=100,
        max_depth=4,
        max_samples=20,
        min_samples_split=5,
        random_state=42,
    )

    # Train the model
    rf.fit(X_train, y_train)

    # Predict on the test set
    y_pred = rf.predict(X_test)

    # Calculate the accuracy
    accuracy = accuracy_score(y_test, y_pred)

    print(f"Accuracy of RandomForest classifier on test set: {accuracy:.2f}")

Una cosa che avrete notato è che il Random Forest accetta come input molti iperparametri, che ora ho scelto in modo casuale. Per migliorare il nostro script, possiamo prendere questi iperparametri come argomenti della funzione go(). Quindi creiamo un file YAML in cui settiamo questi iperparametri. In seguito, chiederemo all’utente di passarci da terminale il path del file YAML che desidera utilizzare e da cui leggere le impostazioni del modello.

Vediamo quindi come possiamo modificare il nostro script. Sfrutteremo la libreria argparse per consentire agli utenti di specificare alcuni parametri di input (come il file di configurazione del modello) dal terminale.

import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import seaborn as sns
import argparse
import yaml


def go(args):
    with open(args.model_config) as fp:
        model_config = yaml.safe_load(fp)

    titanic = sns.load_dataset("titanic")

    # Preprocess the data: drop rows with missing values and convert categorical to numerical
    titanic.dropna(subset=["age", "embarked", "deck"], inplace=True)
    titanic = pd.get_dummies(
        titanic,
        columns=["sex", "embarked", "class", "who", "deck", "embark_town", "alive"],
        drop_first=True,
    )

    # Define features and target
    X = titanic.drop(["survived"], axis=1)
    y = titanic["survived"]

    # Split the data into training and test sets
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=42
    )

    # Initialize RandomForestClassifier
    rf = RandomForestClassifier(**model_config["random_forest"])

    # Train the model
    rf.fit(X_train, y_train)

    # Predict on the test set
    y_pred = rf.predict(X_test)

    # Calculate the accuracy
    accuracy = accuracy_score(y_test, y_pred)

    print(f"\n\nAccuracy of RandomForest classifier on test set: {accuracy:.2f}")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Train a Random Forest",
        fromfile_prefix_chars="@",
    )

    parser.add_argument(
        "--model_config",
        type=str,
        help="Path to a YAML file containing the configuration for the random forest",
        required=True,
    )

    args = parser.parse_args()

    go(args)

Come avrete letto nel mio precedente articolo sulle pipeline con MLflow, un componente di MLflow ha bisogno anche di un conda.yaml in cui si specifica l’ambiente di sviluppo. Ecco quindi lo yaml che possiamo usare:

name: random_forest
channels:
  - conda-forge
  - defaults
dependencies:
  - pandas
  - pip
  - scikit-learn
  - matplotlib
  - plotly
  - pillow
  - mlflow
  - seaborn
  - pip:
      - omegaconf

Ma sappiamo anche che un componente MLflow ha bisogno di un file MLproject:

name: random_forest
conda_env: conda.yml
entry_points:
  main:
    parameters:
      model_config:
        description: JSON blurb containing the configuration for the decision tree
        type: str
    command: >-
      python run.py --model_config {model_config}

In questo file MLproject diamo un nome al componente, definiamo il file conda da utilizzare e inseriamo i parametri di input per la configurazione del modello.

Ora, al di fuori del componente, come main entry, creiamo altri file:

  • un altro conda.yaml per specificare l’ambiente del componente principale
  • un MLproject per definire i passaggi della pipeline MLflow
  • un file di configurazione in cui inserire gli iperparametri da utilizzare
  • uno script main.py da cui parte il nostro progetto

Ecco un immagine per chiarire la struttura del progetto.

Iniziamo definendo il file config.yaml. Ci sono molti iperparametri che si possono provare. Ne ho scelti solo alcuni, sentitevi liberi di sperimentarne altri.

random_forest:
  n_estimators: 100
  criterion: "gini"
  max_depth: null
  min_samples_split: 2
  min_samples_leaf: 1

Ora specifichiamo il file conda per questo entry point.

name: main_component
channels:
  - conda-forge
  - defaults
dependencies:
  - requests
  - pip
  - mlflow
  - hydra-core
  - pip:
      - wandb
      - hydra-joblib-launcher

Definiamo ora come deve essere scritto main.py per utilizzare facilmente Hydra.

Per usare il file di configurazione all’interno del nostro codice Python, è sufficiente utilizzare il decoratore Hydra in cui si specifica il nome del file di configurazione e Python lo leggerà automaticamente. Vediamo un esempio.

import mlflow
import os
import hydra
from omegaconf import DictConfig, OmegaConf


# This automatically reads in the configuration
@hydra.main(config_name="config")
def go(config: DictConfig):

    # You can get the path at the root of the MLflow project with this:
    root_path = hydra.utils.get_original_cwd()

    # Serialize decision tree configuration
    model_config = os.path.abspath("random_forest_config.yml")

    with open(model_config, "w+") as fp:
        fp.write(OmegaConf.to_yaml(config))

    _ = mlflow.run(
        os.path.join(
            root_path, "random_forest"
        ),  # run the subdirectory mlflow pipeline
        "main",
        parameters={
            "model_config": model_config,
        },
    )


if __name__ == "__main__":
    go()

Come potete osservare, il file di configurazione viene letto grazie al decoratore di Hydra. Dopodiché, creo on the fly un nuovo file yaml chiamato random_forest_config.yml da passare allo script run.py. Se ricordate, questo script si aspettava proprio un file yaml con la configurazione del random forest.

Perché allora creare un secondo yaml invece di passargli direttamente il config.yaml? Semplicemente perché in config.yaml potrei avere impostazioni anche per altre cose oltre che al random forest. In questo caso, non ce ne sono perché si tratta di un esempio giocattolo, quindi potremmo anche saltare questo passaggio.

Lanciamo il componente “random_forest” della pipeline con il comando mlflow.run()

Ora potremmo eseguire lo script main.py con comandi speciali derivati da Hydra per modificare alcune impostazioni del file di configurazione del modello in modo semplice e veloce.

Ma sappiamo bene che non eseguiremo il comando “python main.py” a mano, ma definiremo questo comando nel MLproject. Quindi nel MLproject dovremo dire che il comando “python main.py” può essere accompagnato da altri parametri accettati da Hydra.

name: run_pipeline
conda_env: conda.yaml

entry_points:
  main:
    parameters:
      hydra_options:
        description: Hydra parameters to override
        type: str
        default: ""
    command: >-
      python main.py $(echo {hydra_options})

Notate che il comando che eseguiamo ha ora un pezzo di codice in più:

python main.py $(echo {hydra_options}).

In questo modo, aggiungiamo piccole modifiche ai comandi che vengono accettati da Hydra. Vedremo degli esempi tra poco.

Ora, se lanciamo il tutto con “mlflow run .“, verrà avviata l’intera pipeline.

mlflow run .

Ci vorrà un po’ di tempo perché MLflow dovrà generare gli ambienti necessari che sono stati specificati nei file conda.yaml.

La seconda volta che si avvia la pipeline sarà più veloce perché MLflow è abbastanza intelligente da capire che quell’ambiente esiste già e non è necessario crearlo di nuovo.

Come si può vedere nel mio caso, tutto è andato bene senza errori e posso vedere l’accuratezza raggiunta dal modello.

Avere una pipeline di questo tipo è molto comodo perché ora posso testare un modello con iperparametri diversi semplicemente cambiando un file yaml.

Hydra Sweeps

Sfruttiamo il codice che abbiamo scritto per lanciare altri esperimenti in modo semplice. Nel MLproject, nella voce riguardo l’entry point, abbiamo detto che accettiamo parametri di input che sono utili per modificare il comportamento di Hydra.

Possiamo modificare alcuni campi del file di configurazione direttamente dal terminale. Se leggete attentamente il file MLproject vedrete che possiamo modificare un parametro chiamato “hydra_options”. Quindi definiamo all’interno di questo parametro i valori del file di configurazione che vogliamo richiamare.

Ad esempio, se voglio fare un test con un numero di stimatori pari a 30, posso eseguire la pipeline di Mlflow nel modo seguente.

mlflow run . -P hydra_options="random_forest.n_estimators=30"

Naturalmente, è possibile specificare più modifiche ai parametri contemporaneamente. Nel prossimo esempio, modifico sia n_estimator che min_sample_split:

mlflow run . -P hydra_options="random_forest.n_estimators=100 random_forest.min_samples_split=5"

Semplice, vero? Ora per lanciare vari esperimenti basta cambiare una stringa da terminale!

Tuttavia, non abbiamo visto come questo possa accelerare notevolmente la fase di hyperparameter tuning. Supponiamo che il risultato di ogni esperimento venga salvato su un file invece che stampato a console (potete provare a implementarlo voi stessi).

Possiamo dire a Hydra, tramite terminale, di provare combinazioni di valori diversi per ogni iperparametro con un solo comando. Successivamente, controlleremo i log dei risultati e sceglieremo il migliore settaggio.

Per fare questo usiamo la funzione multi-run di Hydra, perché dovranno essere lanciate molte run per ciascuna delle combinazioni di iperparametri.

I vari valori da testare essere separati da virgole. Alla fine della stringa, si aggiunge un “-m” per indicare che si tratta di una multi-run. Ecco un esempio pratico:

mlflow run . -P hydra_options="random_forest.n_estimators=10,50,100 random_forest.min_samples_split=3,5,7 -m"

Abbiamo finalmente lanciato il nostro hyperparameter tuning con Hydra! 🥳

Se vogliamo provare tutti i numeri da x1 a x2, possiamo usare la funzione range(x1,x2). Ad esempio, se per min_samples_split voglio provare tutti i numeri da 1 a 5, posso usare il comando in questo modo:

mlflow run . -P hydra_options="random_forest.n_estimators=10,50,100 random_forest.min_samples_split=range(1,5)  -m"

Conclusioni

MLflow e Hydra sono strumenti fantastici per lavorare su progetti di data science. Con un pò sforzo iniziale, ci permettono di lanciare esperimenti facilmente. In questo modo possiamo dedicarci effettivamente a capire perché alcuni esperimenti funzionano meglio di altri e trarre le nostre conclusioni senza dover andare a modificare il codice ogni volta.

In un prossimo tutorial, mostrerò anche come salvare i risultati degli esperimenti in uno strumento esterno per tenerne traccia. Di solito uso  Weight & Biases, ma anche MLflow stesso offre questa possibilità.

  • #ai
  • #dataset
  • #python
  • ia
  • Intelligenza artificiale
  • iperparametri
  • machine learning
  • MLflow
  • multi-run
  • open source
  • ottimizzazionehydra
  • Seaborn
  • YAML
Immagine del sitoMarcello Politi
Esperto di intelligenza artificiale con una grande passione per l'esplorazione spaziale. Ho avuto la fortuna di lavorare presso l'Agenzia Spaziale Europea, contribuendo a progetti di ottimizzazione del flusso di dati e di architettura del software. Attualmente, sono AI Scientist & Coach presso la PiSchool, dove mi dedico alla prototipazione rapida di prodotti basati sull'intelligenza artificiale. Mi piace scrivere articoli riguardo la data science e recentemente sono stato riconosciuto come uno dei blogger più prolifici su Towards Data Science.

Lista degli articoli

Articoli in evidenza

Immagine del sito
React Server: Nuovi bug critici portano a DoS e alla divulgazione del codice sorgente
Di Redazione RHC - 12/12/2025

La saga sulla sicurezza dei componenti di React Server continua questa settimana. Successivamente alla correzione di una vulnerabilità critica relativa all’esecuzione di codice remoto (RCE) che ha ...

Immagine del sito
700.000 record di un Registro Professionale Italiano in vendita nel Dark Web
Di Redazione RHC - 11/12/2025

Un nuovo allarme arriva dal sottobosco del cybercrime arriva poche ore fa. A segnalarlo l’azienda ParagonSec, società specializzata nel monitoraggio delle attività delle cyber gang e dei marketpla...

Immagine del sito
L’EDR è inutile! Gli hacker di DeadLock hanno trovato un “kill switch” universale
Di Redazione RHC - 11/12/2025

Cisco Talos ha identificato una nuova campagna ransomware chiamata DeadLock: gli aggressori sfruttano un driver antivirus Baidu vulnerabile (CVE-2024-51324) per disabilitare i sistemi EDR tramite la t...

Immagine del sito
DDoSia e NoName057(16): le indagini di RHC confermate dal Dipartimento di Giustizia USA
Di Redazione RHC - 11/12/2025

Quanto avevamo scritto nell’articolo “Codice Patriottico: da DDoSia e NoName057(16) al CISM, l’algoritmo che plasma la gioventù per Putin” su Red Hot Cyber il 23 luglio scorso trova oggi pien...

Immagine del sito
Supply chain: Notepad++ rafforza la sicurezza dopo un grave incidente di dirottamento del traffico
Di Redazione RHC - 11/12/2025

Notepad++ è spesso preso di mira da malintenzionati perché il software è popolare e ampiamente utilizzato. Una vulnerabilità recentemente scoperta nell’editor di testo e codice open source Notep...