Vertex AI: ottimizzazione degli iperparametri

1. Panoramica

In questo lab utilizzerai Vertex AI per eseguire un job di ottimizzazione degli iperparametri per un modello TensorFlow. Sebbene questo lab utilizzi TensorFlow per il codice del modello, i concetti sono applicabili anche ad altri framework ML.

Cosa imparerai

Imparerai a:

  • Modifica il codice dell'applicazione di addestramento per l'ottimizzazione automatica degli iperparametri
  • Configura e avvia un job di ottimizzazione degli iperparametri dall'interfaccia utente di Vertex AI
  • Configura e avvia un job di ottimizzazione degli iperparametri con l'SDK Vertex AI per Python

Il costo totale per l'esecuzione di questo lab su Google Cloud è di circa 3$.

2. Introduzione a Vertex AI

Questo lab utilizza la più recente offerta di prodotti AI disponibile su Google Cloud. Vertex AI integra le offerte ML di Google Cloud in un'esperienza di sviluppo fluida. In precedenza, i modelli addestrati con AutoML e i modelli personalizzati erano accessibili tramite servizi separati. La nuova offerta combina entrambi in un'unica API, insieme ad altri nuovi prodotti. Puoi anche migrare progetti esistenti su Vertex AI. In caso di feedback, consulta la pagina di supporto.

Vertex AI include molti prodotti diversi per supportare i flussi di lavoro ML end-to-end. Questo lab è incentrato sui prodotti evidenziati di seguito: Formazione e Workbench.

Panoramica del prodotto Vertex

3. Configura l'ambiente

Per eseguire questo codelab, è necessario un progetto Google Cloud con fatturazione abilitata. Per creare un progetto, segui le istruzioni riportate qui.

Passaggio 1: abilita l'API Compute Engine

Passa a Compute Engine e seleziona Abilita se non è già abilitato. Ne avrai bisogno per creare la tua istanza di blocco note.

Passaggio 2: attiva l'API Container Registry

Vai a Container Registry e seleziona Abilita, se non è già selezionato. Lo utilizzerai per creare un container per il tuo job di addestramento personalizzato.

Passaggio 3: abilita l'API Vertex AI

Vai alla sezione Vertex AI della tua console Cloud e fai clic su Abilita API Vertex AI.

Dashboard di Vertex AI

Passaggio 4: crea un'istanza di Vertex AI Workbench

Dalla sezione Vertex AI della console Cloud, fai clic su Workbench:

Menu Vertex AI

Abilita l'API Notebooks, se non lo è già.

Notebook_api

Dopo averla attivata, fai clic su NOTEBOOK GESTITI:

Notebooks_UI

Quindi seleziona NUOVO Blocco note.

new_notebook

Assegna un nome al blocco note, quindi fai clic su Impostazioni avanzate.

create_notebook

In Impostazioni avanzate, abilita l'arresto per inattività e imposta il numero di minuti su 60. Ciò significa che il blocco note si arresterà automaticamente quando non è in uso, per evitare costi inutili.

idle_timeout

In Sicurezza, seleziona "Attiva terminale". se non è già abilitato.

enable_terminal

Puoi lasciare invariate tutte le altre impostazioni avanzate.

Quindi, fai clic su Crea. Il provisioning dell'istanza richiederà un paio di minuti.

Una volta creata l'istanza, seleziona Apri JupyterLab.

open_jupyterlab

La prima volta che utilizzi una nuova istanza, ti verrà chiesto di autenticarti. Per farlo, segui i passaggi nell'interfaccia utente.

autenticare

4. containerizza il codice dell'applicazione di addestramento

Il modello che addestrerai e ottimizzerai in questo lab è un modello di classificazione delle immagini addestrato sul set di dati di cavalli o esseri umani dai set di dati TensorFlow.

Invierai questo job di ottimizzazione degli iperparametri a Vertex AI inserendo il codice dell'applicazione di addestramento in un container Docker ed eseguendo il push di questo container a Google Container Registry. Utilizzando questo approccio, puoi ottimizzare gli iperparametri per un modello creato con qualsiasi framework.

Per iniziare, dal menu Avvio app, apri una finestra Terminale nell'istanza del blocco note:

Apri terminale nel blocco note

Crea una nuova directory chiamata horses_or_humans e accedi tramite cd:

mkdir horses_or_humans
cd horses_or_humans

Passaggio 1: crea un Dockerfile

Il primo passaggio per containerizzare il codice è creare un Dockerfile. Nel Dockerfile includerai tutti i comandi necessari per eseguire l'immagine. Installerà tutte le librerie necessarie, inclusa la libreria CloudML Hypertune, e configurerà il punto di accesso per il codice di addestramento.

Dal terminale, crea un Dockerfile vuoto:

touch Dockerfile

Apri il Dockerfile e copia al suo interno quanto segue:

FROM gcr.io/deeplearning-platform-release/tf2-gpu.2-7

WORKDIR /

# Installs hypertune library
RUN pip install cloudml-hypertune

# Copies the trainer code to the docker image.
COPY trainer /trainer

# Sets up the entry point to invoke the trainer.
ENTRYPOINT ["python", "-m", "trainer.task"]

Questo Dockerfile utilizza l'immagine Docker GPU TensorFlow Enterprise 2.7 del container deep learning. Deep Learning Containers su Google Cloud è dotato di molti framework comuni di ML e data science preinstallati. Dopo aver scaricato l'immagine, questo Dockerfile configura il punto di accesso per il codice di addestramento. Non hai ancora creato questi file: nel passaggio successivo aggiungerai il codice per l'addestramento e la messa a punto del modello.

Passaggio 2: aggiungi il codice di addestramento del modello

Dal tuo terminale, esegui quanto segue per creare una directory per il codice di addestramento e un file Python in cui aggiungerai il codice:

mkdir trainer
touch trainer/task.py

Ora dovresti avere quanto segue nella directory horses_or_humans/:

+ Dockerfile
+ trainer/
    + task.py

Successivamente, apri il file task.py che hai appena creato e copia il codice seguente.

import tensorflow as tf
import tensorflow_datasets as tfds
import argparse
import hypertune

NUM_EPOCHS = 10


def get_args():
  '''Parses args. Must include all hyperparameters you want to tune.'''

  parser = argparse.ArgumentParser()
  parser.add_argument(
      '--learning_rate',
      required=True,
      type=float,
      help='learning rate')
  parser.add_argument(
      '--momentum',
      required=True,
      type=float,
      help='SGD momentum value')
  parser.add_argument(
      '--num_units',
      required=True,
      type=int,
      help='number of units in last hidden layer')
  args = parser.parse_args()
  return args


def preprocess_data(image, label):
  '''Resizes and scales images.'''

  image = tf.image.resize(image, (150,150))
  return tf.cast(image, tf.float32) / 255., label


def create_dataset():
  '''Loads Horses Or Humans dataset and preprocesses data.'''

  data, info = tfds.load(name='horses_or_humans', as_supervised=True, with_info=True)

  # Create train dataset
  train_data = data['train'].map(preprocess_data)
  train_data  = train_data.shuffle(1000)
  train_data  = train_data.batch(64)

  # Create validation dataset
  validation_data = data['test'].map(preprocess_data)
  validation_data  = validation_data.batch(64)

  return train_data, validation_data


def create_model(num_units, learning_rate, momentum):
  '''Defines and compiles model.'''

  inputs = tf.keras.Input(shape=(150, 150, 3))
  x = tf.keras.layers.Conv2D(16, (3, 3), activation='relu')(inputs)
  x = tf.keras.layers.MaxPooling2D((2, 2))(x)
  x = tf.keras.layers.Conv2D(32, (3, 3), activation='relu')(x)
  x = tf.keras.layers.MaxPooling2D((2, 2))(x)
  x = tf.keras.layers.Conv2D(64, (3, 3), activation='relu')(x)
  x = tf.keras.layers.MaxPooling2D((2, 2))(x)
  x = tf.keras.layers.Flatten()(x)
  x = tf.keras.layers.Dense(num_units, activation='relu')(x)
  outputs = tf.keras.layers.Dense(1, activation='sigmoid')(x)
  model = tf.keras.Model(inputs, outputs)
  model.compile(
      loss='binary_crossentropy',
      optimizer=tf.keras.optimizers.SGD(learning_rate=learning_rate, momentum=momentum),
      metrics=['accuracy'])
  return model


def main():
  args = get_args()
  train_data, validation_data = create_dataset()
  model = create_model(args.num_units, args.learning_rate, args.momentum)
  history = model.fit(train_data, epochs=NUM_EPOCHS, validation_data=validation_data)

  # DEFINE METRIC
  hp_metric = history.history['val_accuracy'][-1]

  hpt = hypertune.HyperTune()
  hpt.report_hyperparameter_tuning_metric(
      hyperparameter_metric_tag='accuracy',
      metric_value=hp_metric,
      global_step=NUM_EPOCHS)


if __name__ == "__main__":
    main()

Prima di creare il container, diamo uno sguardo più approfondito al codice. Esistono alcuni componenti specifici dell'utilizzo del servizio di ottimizzazione degli iperparametri.

  1. Lo script importa la libreria hypertune. Tieni presente che il Dockerfile del passaggio 1 includeva istruzioni per installare tramite pip questa libreria.
  2. La funzione get_args() definisce un argomento della riga di comando per ogni iperparametro da ottimizzare. In questo esempio, gli iperparametri che verranno ottimizzati sono il tasso di apprendimento, il valore momentum nell'ottimizzatore e il numero di unità nell'ultimo strato nascosto del modello, ma sperimenta pure con altri iperparametri. Il valore passato in questi argomenti viene quindi utilizzato per impostare l'iperparametro corrispondente nel codice.
  3. Alla fine della funzione main(), la libreria hypertune viene utilizzata per definire la metrica che vuoi ottimizzare. In TensorFlow, il metodo model.fit keras restituisce un oggetto History. L'attributo History.history è un record dei valori di perdita dell'addestramento e dei valori delle metriche in epoche successive. Se passi i dati di convalida a model.fit, l'attributo History.history includerà anche la perdita di convalida e i valori delle metriche. Ad esempio, se addestrassi un modello per tre epoche con dati di convalida e fornissi accuracy come metrica, l'attributo History.history sarebbe simile al seguente dizionario.
{
 "accuracy": [
   0.7795261740684509,
   0.9471358060836792,
   0.9870933294296265
 ],
 "loss": [
   0.6340447664260864,
   0.16712145507335663,
   0.04546636343002319
 ],
 "val_accuracy": [
   0.3795261740684509,
   0.4471358060836792,
   0.4870933294296265
 ],
 "val_loss": [
   2.044623374938965,
   4.100203514099121,
   3.0728273391723633
 ]

Se vuoi che il servizio di ottimizzazione degli iperparametri scopra i valori che massimizzano l'accuratezza di convalida del modello, definisci la metrica come l'ultima voce (o NUM_EPOCS - 1) dell'elenco val_accuracy. Quindi, passa questa metrica a un'istanza di HyperTune. Puoi scegliere la stringa che preferisci per hyperparameter_metric_tag, ma dovrai utilizzare nuovamente la stringa in seguito quando avvierai il job di ottimizzazione degli iperparametri.

Passaggio 3: crea il container

Dal tuo terminale, esegui quanto segue per definire una variabile env per il tuo progetto, assicurandoti di sostituire your-cloud-project con l'ID del tuo progetto:

PROJECT_ID='your-cloud-project'

Definisci una variabile con l'URI dell'immagine container in Google Container Registry:

IMAGE_URI="gcr.io/$PROJECT_ID/horse-human:hypertune"

Configura Docker

gcloud auth configure-docker

Quindi, crea il container eseguendo quanto segue dalla radice della directory horses_or_humans:

docker build ./ -t $IMAGE_URI

Infine, invialo a Google Container Registry:

docker push $IMAGE_URI

Con il push del container in Container Registry, ora puoi avviare un job di ottimizzazione degli iperparametri del modello personalizzato.

5. Esegui un job di ottimizzazione degli iperparametri su Vertex AI

Questo lab utilizza l'addestramento personalizzato tramite un container personalizzato su Google Container Registry, ma puoi anche eseguire un job di ottimizzazione degli iperparametri con un container predefinito Vertex AI.

Per iniziare, vai alla sezione Addestramento nella sezione Vertex della console Cloud:

Menu uCAIP

Passaggio 1: configura il job di addestramento

Fai clic su Crea per inserire i parametri per il tuo job di ottimizzazione degli iperparametri.

  • In Set di dati, seleziona Nessun set di dati gestito
  • Quindi seleziona Addestramento personalizzato (avanzato) come metodo di addestramento e fai clic su Continua.
  • Inserisci horses-humans-hyptertune (o un nome che preferisci) in Nome modello
  • Fai clic su Continua.

Nel passaggio Impostazioni contenitore, seleziona Container personalizzato:

Opzione container personalizzato

Nella prima casella (Immagine container), inserisci il valore della variabile IMAGE_URI della sezione precedente. Dovrebbe essere: gcr.io/your-cloud-project/horse-human:hypertune, con il nome del tuo progetto. Lascia vuoti gli altri campi e fai clic su Continua.

Passaggio 2: configura il job di ottimizzazione degli iperparametri

Seleziona Abilita ottimizzazione degli iperparametri.

Iperparametri

Configura gli iperparametri

Successivamente, dovrai aggiungere gli iperparametri che hai impostato come argomenti della riga di comando nel codice dell'applicazione di addestramento. Quando aggiungi un iperparametro, devi prima fornire il nome. Dovrebbe corrispondere al nome dell'argomento che hai passato a argparse.

learning_rate_name

Quindi, seleziona il tipo e i limiti per i valori che il servizio di ottimizzazione proverà. Se selezioni il tipo Doppio o Numero intero, devi fornire un valore minimo e massimo. Se selezioni Categorico o Discreto, dovrai fornire i valori.

learning_rate_typelearning_rate_name

Per i tipi Doppio e Numero intero, devi specificare anche il valore di scalabilità.

learning_rate_scale

Dopo aver aggiunto l'iperparametro learning_rate, aggiungi i parametri per momentum e num_units.

momentum_config

numneruons_config

Configura la metrica

Dopo aver aggiunto gli iperparametri, fornirai la metrica che vuoi ottimizzare e l'obiettivo. Deve essere uguale al valore hyperparameter_metric_tag impostato nell'applicazione di addestramento.

metric_config

Il servizio di ottimizzazione degli iperparametri Vertex AI eseguirà più prove dell'applicazione di addestramento con i valori configurati nei passaggi precedenti. Dovrai porre un limite superiore al numero di prove che il servizio eseguirà. Un numero maggiore di prove generalmente porta a risultati migliori, ma ci sarà un punto in cui i risultati diminuiranno dopo il quale ulteriori prove avranno poco o nessun effetto sulla metrica che stai cercando di ottimizzare. Una best practice è iniziare con un numero inferiore di prove e farsi un'idea dell'impatto degli iperparametri scelti prima dello scale up a un numero elevato di prove.

Dovrai inoltre impostare un limite superiore al numero di prove parallele. L'aumento del numero di prove parallele ridurrà la quantità di tempo necessaria per l'esecuzione del job di ottimizzazione degli iperparametri; tuttavia, può ridurre l'efficacia del job nel complesso. Questo perché la strategia di ottimizzazione predefinita utilizza i risultati delle prove precedenti per informare l'assegnazione dei valori nelle prove successive. Se si eseguono troppe prove in parallelo, ci saranno prove che inizieranno senza il beneficio del risultato delle prove ancora in esecuzione.

A scopo dimostrativo, puoi impostare il numero di prove su 15 e il numero massimo di prove parallele su 3. Puoi sperimentare numeri diversi, ma ciò può comportare tempi di ottimizzazione più lunghi e costi più elevati.

trial_config

L'ultimo passaggio consiste nel selezionare Predefinito come algoritmo di ricerca, che utilizzerà Google Vizier per eseguire l'ottimizzazione bayesiana per l'ottimizzazione degli iperparametri. Puoi scoprire di più su questo algoritmo qui.

algorithm_config

Fai clic su Continua.

Passaggio 3: configura il calcolo

In Compute e prezzi, lascia la regione selezionata così com'è e configura Pool di worker 0 come segue.

Tipo di macchina

Fai clic su Inizia addestramento per dare il via al job di ottimizzazione degli iperparametri. Nella sezione Addestramento della console, nella scheda JOB DI OTTIMIZZAZIONE DEGLI IPERPARAMETRI vedrai qualcosa di simile a questo:

Job iperparametri

Al termine, potrai fare clic sul nome del job e vedere i risultati delle prove di ottimizzazione.

Output iperparametri

🎉 Complimenti! 🎉

Hai imparato come utilizzare Vertex AI per:

  • Avviare un processo di ottimizzazione degli iperparametri per il codice di addestramento fornito in un container personalizzato. In questo esempio hai utilizzato un modello TensorFlow, ma puoi addestrare un modello creato con qualsiasi framework utilizzando container personalizzati.

Per scoprire di più sulle diverse parti di Vertex, consulta la documentazione.

6. [Facoltativo] Utilizza l'SDK Vertex

La sezione precedente ha mostrato come avviare il job di ottimizzazione degli iperparametri tramite l'interfaccia utente. In questa sezione viene visualizzato un modo alternativo per inviare il job di ottimizzazione degli iperparametri utilizzando l'API Vertex Python.

Da Avvio app, crea un blocco note TensorFlow 2.

new_notebook

Importa l'SDK Vertex AI.

from google.cloud import aiplatform
from google.cloud.aiplatform import hyperparameter_tuning as hpt

Per avviare il job di ottimizzazione degli iperparametri, devi prima definire le seguenti specifiche. Dovrai sostituire {PROJECT_ID} in image_uri con il tuo progetto.

# The spec of the worker pools including machine type and Docker image
# Be sure to replace PROJECT_ID in the `image_uri` with your project.

worker_pool_specs = [{
    "machine_spec": {
        "machine_type": "n1-standard-4",
        "accelerator_type": "NVIDIA_TESLA_V100",
        "accelerator_count": 1
    },
    "replica_count": 1,
    "container_spec": {
        "image_uri": "gcr.io/{PROJECT_ID}/horse-human:hypertune"
    }
}]


# Dictionary representing metrics to optimize.
# The dictionary key is the metric_id, which is reported by your training job,
# And the dictionary value is the optimization goal of the metric.
metric_spec={'accuracy':'maximize'}

# Dictionary representing parameters to optimize.
# The dictionary key is the parameter_id, which is passed into your training
# job as a command line argument,
# And the dictionary value is the parameter specification of the metric.
parameter_spec = {
    "learning_rate": hpt.DoubleParameterSpec(min=0.001, max=1, scale="log"),
    "momentum": hpt.DoubleParameterSpec(min=0, max=1, scale="linear"),
    "num_units": hpt.DiscreteParameterSpec(values=[64, 128, 512], scale=None)
}

Ora crea un CustomJob. Per la gestione temporanea, devi sostituire {YOUR_BUCKET} con un bucket nel tuo progetto.

# Replace YOUR_BUCKET
my_custom_job = aiplatform.CustomJob(display_name='horses-humans-sdk-job',
                              worker_pool_specs=worker_pool_specs,
                              staging_bucket='gs://{YOUR_BUCKET}')

Poi, crea ed esegui HyperparameterTuningJob.

hp_job = aiplatform.HyperparameterTuningJob(
    display_name='horses-humans-sdk-job',
    custom_job=my_custom_job,
    metric_spec=metric_spec,
    parameter_spec=parameter_spec,
    max_trial_count=15,
    parallel_trial_count=3)

hp_job.run()

7. Esegui la pulizia

Poiché abbiamo configurato il timeout del blocco note dopo 60 minuti di inattività, non dobbiamo preoccuparci di arrestare l'istanza. Se vuoi arrestare manualmente l'istanza, fai clic sul pulsante Arresta nella sezione Vertex AI Workbench della console. Per eliminare completamente il blocco note, fai clic sul pulsante Elimina.

Arresta istanza

Per eliminare il bucket di archiviazione, utilizzando il menu di navigazione nella console Cloud, vai a Storage, seleziona il bucket e fai clic su Elimina:

Elimina spazio di archiviazione