Condividi tramite


Ottimizzazione degli iperparametri (anteprima)

L'ottimizzazione degli iperparametri è il processo di ricerca dei valori ottimali per i parametri non appresi dal modello di apprendimento automatico durante il training, ma piuttosto impostato dall'utente prima dell'inizio del processo di training. Questi parametri sono comunemente denominati iperparametri ed esempi includono la frequenza di apprendimento, il numero di livelli nascosti in una rete neurale, il livello di regolarizzazione e le dimensioni del batch.

Le prestazioni di un modello di Machine Learning possono essere estremamente sensibili alla scelta degli iperparametri e il set ottimale di iperparametri può variare notevolmente a seconda del problema e del set di dati specifico. L'ottimizzazione degli iperparametri è quindi un passaggio critico nella pipeline di apprendimento automatico, in quanto può avere un impatto significativo sull'accuratezza e sulle prestazioni di generalizzazione del modello.

In Infrastruttura i data scientist possono sfruttare FLAML, una libreria Python leggera per l'automazione efficiente delle operazioni di apprendimento automatico e intelligenza artificiale, per i requisiti di ottimizzazione degli iperparametri. All'interno dei notebook di Fabric, gli utenti possono chiamare flaml.tune per ottimizzare gli iperparametri economici.

Importante

Questa funzionalità si trova nell’anteprima.

Ottimizzazione del flusso di lavoro

Esistono tre passaggi essenziali per usare flaml.tune per completare un'attività di ottimizzazione di base:

  1. Specificare l'obiettivo di ottimizzazione in relazione agli iperparametri.
  2. Specificare uno spazio di ricerca degli iperparametri.
  3. Specificare i vincoli di ottimizzazione, inclusi i vincoli sul budget delle risorse per eseguire l'ottimizzazione, i vincoli sulle configurazioni o/e i vincoli su una o più metriche specifiche.

Obiettivo di ottimizzazione

Il primo passaggio consiste nel specificare l'obiettivo di ottimizzazione. A tale scopo, è necessario innanzitutto specificare la procedura di valutazione in relazione agli iperparametri in una funzione evaluation_functiondefinita dall'utente. La funzione richiede una configurazione degli iperparametri come input. Può semplicemente restituire un valore della metrica in un valore scalare o restituire un dizionario di coppie nome metrica e valore della metrica.

Nell'esempio seguente è possibile definire una funzione di valutazione in relazione a 2 iperparametri denominati x e y.

import time

def evaluate_config(config: dict):
    """evaluate a hyperparameter configuration"""
    score = (config["x"] - 85000) ** 2 - config["x"] / config["y"]


    faked_evaluation_cost = config["x"] / 100000
    time.sleep(faked_evaluation_cost)
    # we can return a single float as a score on the input config:
    # return score
    # or, we can return a dictionary that maps metric name to metric value:
    return {"score": score, "evaluation_cost": faked_evaluation_cost, "constraint_metric": config["x"] * config["y"]}

Spazio di ricerca

Successivamente, verrà specificato lo spazio di ricerca degli iperparametri. Nello spazio di ricerca è necessario specificare valori validi per gli iperparametri e il modo in cui questi valori vengono campionati (ad esempio, da una distribuzione uniforme o da una distribuzione uniforme del log). Nell'esempio seguente è possibile fornire lo spazio di ricerca per gli iperparametri x e y. I valori validi per entrambi sono numeri interi compresi tra [1, 100.000]. Questi iperparametri vengono campionati in modo uniforme negli intervalli specificati.

from flaml import tune

# construct a search space for the hyperparameters x and y.
config_search_space = {
    "x": tune.lograndint(lower=1, upper=100000),
    "y": tune.randint(lower=1, upper=100000)
}

# provide the search space to tune.run
tune.run(..., config=config_search_space, ...)

Con FLAML, gli utenti possono personalizzare il dominio per un particolare iperparametro. In questo modo gli utenti possono specificare un tipo e un intervallo valido da cui campionare i parametri. FLAML supporta i tipi di iperparametri seguenti: float, integer e categorico. Questo esempio è riportato di seguito per i domini di uso comune:

config = {
    # Sample a float uniformly between -5.0 and -1.0
    "uniform": tune.uniform(-5, -1),

    # Sample a float uniformly between 3.2 and 5.4,
    # rounding to increments of 0.2
    "quniform": tune.quniform(3.2, 5.4, 0.2),

    # Sample a float uniformly between 0.0001 and 0.01, while
    # sampling in log space
    "loguniform": tune.loguniform(1e-4, 1e-2),

    # Sample a float uniformly between 0.0001 and 0.1, while
    # sampling in log space and rounding to increments of 0.00005
    "qloguniform": tune.qloguniform(1e-4, 1e-1, 5e-5),

    # Sample a random float from a normal distribution with
    # mean=10 and sd=2
    "randn": tune.randn(10, 2),

    # Sample a random float from a normal distribution with
    # mean=10 and sd=2, rounding to increments of 0.2
    "qrandn": tune.qrandn(10, 2, 0.2),

    # Sample a integer uniformly between -9 (inclusive) and 15 (exclusive)
    "randint": tune.randint(-9, 15),

    # Sample a random uniformly between -21 (inclusive) and 12 (inclusive (!))
    # rounding to increments of 3 (includes 12)
    "qrandint": tune.qrandint(-21, 12, 3),

    # Sample a integer uniformly between 1 (inclusive) and 10 (exclusive),
    # while sampling in log space
    "lograndint": tune.lograndint(1, 10),

    # Sample a integer uniformly between 2 (inclusive) and 10 (inclusive (!)),
    # while sampling in log space and rounding to increments of 2
    "qlograndint": tune.qlograndint(2, 10, 2),

    # Sample an option uniformly from the specified choices
    "choice": tune.choice(["a", "b", "c"]),
}

Per altre informazioni su come personalizzare i domini all'interno dello spazio di ricerca, visitare la documentazione FLAML sulla personalizzazione degli spazi di ricerca.

Vincoli di ottimizzazione

L'ultimo passaggio consiste nello specificare i vincoli dell'attività di ottimizzazione. Una proprietà rilevante di flaml.tune è che è in grado di completare il processo di ottimizzazione all'interno di un vincolo di risorsa obbligatorio. A tale scopo, un utente può fornire vincoli di risorsa in termini di tempo in tempo reale (in secondi) usando l'argomento time_budget_s, o in termini di numero di versioni di valutazione usando l'argomento num_samples.

# Set a resource constraint of 60 seconds wall-clock time for the tuning.
flaml.tune.run(..., time_budget_s=60, ...)

# Set a resource constraint of 100 trials for the tuning.
flaml.tune.run(..., num_samples=100, ...)

# Use at most 60 seconds and at most 100 trials for the tuning.
flaml.tune.run(..., time_budget_s=60, num_samples=100, ...)

Per altre informazioni sull'aggiunta dei vincoli di configurazione, è possibile visitare la documentazione FLAML per le opzioni di ottimizzazione avanzate.

Riepilogo

Dopo aver definito i criteri di ottimizzazione, è possibile eseguire la versione di valutazione dell'ottimizzazione. Per tenere traccia dei risultati della versione di valutazione, è possibile sfruttare l'assegnazione automatica di MLFlow per acquisire le metriche e i parametri per ognuna di queste esecuzioni. Questo codice acquisirà l'intera versione di valutazione dell'ottimizzazione degli iperparametri, evidenziando ognuna delle combinazioni di iperparametri esaminate da FLAML.

import mlflow
mlflow.set_experiment("flaml_tune_experiment")
mlflow.autolog(exclusive=False)

with mlflow.start_run(nested=True, run_name="Child Run: "):
    analysis = tune.run(
        evaluate_config,  # the function to evaluate a config
        config=config_search_space,  # the search space defined
        metric="score",
        mode="min",  # the optimization mode, "min" or "max"
        num_samples=-1,  # the maximal number of configs to try, -1 means infinite
        time_budget_s=10,  # the time budget in seconds
    )

Nota

Quando l'assegnazione automatica di MLflow è abilitata, le metriche, i parametri e i modelli devono essere registrati automaticamente durante l'esecuzione di MLFlow. Tuttavia, questo varia in base al framework. Le metriche e i parametri per modelli specifici potrebbero non essere registrati. Ad esempio, nessuna metrica verrà registrata per i modelli XGBoost, LightGBM, Spark e SynapseML. Per altre informazioni sulle metriche e i parametri acquisiti da ogni framework, vedere la documentazione sull'assegnazione automatica di MLFlow.

Ottimizzazione parallela con Apache Spark

La funzionalità flaml.tune supporta l'ottimizzazione sia di Apache Spark che di apprendimento a nodo singolo. Inoltre, quando si ottimizzano gli studenti a nodo singolo, ad esempio Scikit-Learn, è anche possibile parallelizzare l'ottimizzazione per velocizzare il processo di ottimizzazione impostando use_spark = True. Per i cluster Spark, per impostazione predefinita, FLAML avvierà una versione di valutazione per ogni executor. È anche possibile personalizzare il numero di versioni di valutazione simultanee usando l'argomento n_concurrent_trials.


analysis = tune.run(
    evaluate_config,  # the function to evaluate a config
    config=config_search_space,  # the search space defined
    metric="score",
    mode="min",  # the optimization mode, "min" or "max"
    num_samples=-1,  # the maximal number of configs to try, -1 means infinite
    time_budget_s=10,  # the time budget in seconds
    use_spark=True,
)
print(analysis.best_trial.last_result)  # the best trial's result
print(analysis.best_config)  # the best config

Per altre informazioni su come parallelizzare i percorsi di ottimizzazione, è possibile visitare la documentazione FLAML per i processi Spark paralleli.

Visualizzare i risultati

Il modulo flaml.visualization fornisce funzioni di utilità per tracciare il processo di ottimizzazione usando Plotly. Grazie a Plotly, gli utenti possono esplorare in modo interattivo i risultati dell'esperimento AutoML. Per usare queste funzioni di tracciato, è sufficiente fornire l'oggetto ottimizzato flaml.AutoML o flaml.tune.tune.ExperimentAnalysis come input.

È possibile usare le funzioni seguenti all'interno del notebook:

  • plot_optimization_history: tracciare la cronologia di ottimizzazione di tutte le versioni di valutazione nell'esperimento.
  • plot_feature_importance: importanza del tracciato per ogni funzionalità nel set di dati.
  • plot_parallel_coordinate: tracciare le relazioni dei parametri ad alta dimensione nell'esperimento.
  • plot_contour: tracciare la relazione di parametro come tracciato di contorno nell'esperimento.
  • plot_edf: tracciare il valore obiettivo EDF (funzione di distribuzione empirica) dell'esperimento.
  • plot_timeline: tracciare la sequenza temporale dell'esperimento.
  • plot_slice: tracciare la relazione di parametro come tracciato di sezione in uno studio.
  • plot_param_importance: tracciare l'importanza dell'iperparametro dell'esperimento.