Vertex AI: Verteilte Hyperparameter-Abstimmung

1. Übersicht

In diesem Lab erfahren Sie, wie Sie Vertex AI für die Hyperparameter-Abstimmung und das verteilte Training verwenden. In diesem Lab wird TensorFlow für den Modellcode verwendet, die Konzepte sind aber auch auf andere ML-Frameworks anwendbar.

Lerninhalte

Die folgenden Themen werden behandelt:

  • Modell mit verteiltem Training in einem benutzerdefinierten Container trainieren
  • Mehrere Tests Ihres Trainingscodes für die automatische Hyperparameter-Abstimmung starten

Die Gesamtkosten für die Ausführung dieses Labs in Google Cloud betragen etwa 6$.

2. Einführung in Vertex AI

In diesem Lab wird das neueste KI-Produkt von Google Cloud verwendet. Vertex AI vereint die ML-Angebote von Google Cloud in einer nahtlosen Entwicklungsumgebung. Bisher musste auf mit AutoML trainierte und benutzerdefinierte Modelle über verschiedene Dienste zugegriffen werden. Das neue Angebot kombiniert diese und weitere, neue Produkte zu einer einzigen API. Sie können auch vorhandene Projekte zu Vertex AI migrieren. Wenn Sie Feedback haben, lesen Sie die Supportseite.

Vertex AI umfasst viele verschiedene Produkte zur Unterstützung von End-to-End-ML-Workflows. In diesem Lab konzentrieren wir uns auf Training und Workbench.

Vertex-Produktübersicht

3. Anwendungsfallübersicht

In diesem Lab verwenden Sie die Hyperparameter-Abstimmung, um optimale Parameter für ein Bildklassifizierungsmodell zu ermitteln, das mit dem Dataset „horses or humans“ aus TensorFlow Datasets trainiert wurde.

Hyperparameter-Abstimmung

Bei der Hyperparameter-Abstimmung mit Vertex AI Training werden mehrere Tests Ihrer Trainingsanwendung mit Werten für Ihre gewählten Hyperparameter innerhalb von Ihnen festgelegten Limits ausgeführt. Vertex AI verfolgt die Ergebnisse jedes Versuchs und nimmt Anpassungen für nachfolgende Versuche vor.

Wenn Sie die Hyperparameter-Abstimmung mit Vertex AI Training verwenden möchten, müssen Sie zwei Änderungen an Ihrem Trainingscode vornehmen:

  1. Definieren Sie in Ihrem Haupt-Trainingsmodul für jeden abzustimmenden Hyperparameter ein Befehlszeilenargument.
  2. Verwenden Sie den Wert, der in diesen Argumenten übergeben wird, um den entsprechenden Hyperparameter im Code Ihrer Anwendung festzulegen.

Verteiltes Training

Wenn Sie eine einzelne GPU haben, verwendet TensorFlow diesen Beschleuniger, um das Modelltraining zu beschleunigen, ohne dass Sie etwas tun müssen. Wenn Sie jedoch eine zusätzliche Leistungssteigerung durch die Verwendung mehrerer GPUs erzielen möchten, müssen Sie tf.distribute verwenden. Das ist das TensorFlow-Modul zum Ausführen einer Berechnung auf mehreren Geräten.

In diesem Lab wird tf.distribute.MirroredStrategy verwendet, das Sie mit nur wenigen Codeänderungen zu Ihren Trainingsanwendungen hinzufügen können. Mit dieser Strategie wird eine Kopie des Modells auf jeder GPU auf Ihrem Computer erstellt. Die nachfolgenden Gradientenaktualisierungen erfolgen synchron. Das bedeutet, dass jede GPU die Vorwärts- und Rückwärtsdurchläufe durch das Modell auf einem anderen Teil der Eingabedaten berechnet. Die berechneten Gradienten aus jedem dieser Teile werden dann auf allen GPUs aggregiert und in einem Prozess namens All-Reduce gemittelt. Die Modellparameter werden mit diesen gemittelten Gradienten aktualisiert.

Sie müssen die Details nicht kennen, um dieses Lab abzuschließen. Wenn Sie jedoch mehr darüber erfahren möchten, wie verteiltes Training in TensorFlow funktioniert, sehen Sie sich das folgende Video an:

4. Umgebung einrichten

Für dieses Codelab benötigen Sie ein Google Cloud-Projekt, in dem die Abrechnung aktiviert ist. Eine Anleitung zum Erstellen eines Projekts finden Sie hier.

Schritt 1: Compute Engine API aktivieren

Rufen Sie Compute Engine auf und wählen Sie Aktivieren aus, falls die API noch nicht aktiviert ist.

Schritt 2: Container Registry API aktivieren

Rufen Sie die Container Registry auf und wählen Sie Aktivieren aus, falls die API noch nicht aktiviert ist. Sie verwenden sie, um einen Container für Ihren benutzerdefinierten Trainingsjob zu erstellen.

Schritt 3: Vertex AI API aktivieren

Rufen Sie den Bereich „Vertex AI“ in der Cloud Console auf und klicken Sie auf **Vertex AI API aktivieren**.

Vertex AI-Dashboard

Schritt 4: Vertex AI Workbench-Instanz erstellen

Klicken Sie im Bereich „Vertex AI“ der Cloud Console auf „Workbench“:

Vertex AI-Menü

Aktivieren Sie die Notebooks API, falls sie noch nicht aktiviert ist.

Notebook_api

Klicken Sie nach der Aktivierung auf VERWALTETE NOTEBOOKS:

Notebooks_UI

Wählen Sie dann NEUES NOTEBOOK aus.

new_notebook

Geben Sie Ihrem Notebook einen Namen und klicken Sie dann auf Erweiterte Einstellungen.

create_notebook

Aktivieren Sie unter „Erweiterte Einstellungen“ das Herunterfahren bei Inaktivität und legen Sie die Anzahl der Minuten auf 60 fest. Das bedeutet, dass Ihr Notebook automatisch heruntergefahren wird, wenn es nicht verwendet wird, damit keine unnötigen Kosten entstehen.

idle_timeout

Wählen Sie unter „Sicherheit“ die Option „Terminal aktivieren“ aus, falls sie noch nicht aktiviert ist.

enable-terminal

Sie können alle anderen erweiterten Einstellungen unverändert lassen.

Klicken Sie dann auf Erstellen. Die Bereitstellung der Instanz dauert einige Minuten.

Nachdem die Instanz erstellt wurde, klicken Sie auf JupyterLab öffnen.

open_jupyterlab

Wenn Sie eine neue Instanz zum ersten Mal verwenden, werden Sie aufgefordert, sich zu authentifizieren. Folgen Sie dazu der Anleitung auf der Benutzeroberfläche.

Authentifizieren

5. Trainingscode schreiben

Öffnen Sie im Launcher-Menü ein Terminalfenster in Ihrer Notebook-Instanz:

launcher_terminal

Erstellen Sie ein neues Verzeichnis mit dem Namen vertex-codelab und wechseln Sie zu diesem Verzeichnis.

mkdir vertex-codelab
cd vertex-codelab

Führen Sie den folgenden Befehl aus, um ein Verzeichnis für den Trainingscode und eine Python-Datei zu erstellen, in der Sie den Code hinzufügen:

mkdir trainer
touch trainer/task.py

Das Verzeichnis vertex-codelab sollte jetzt Folgendes enthalten:

+ trainer/
    + task.py

Öffnen Sie als Nächstes die gerade erstellte Datei task.py und fügen Sie den gesamten folgenden Code ein.

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

NUM_EPOCHS = 10
BATCH_SIZE = 64

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(batch_size):
  '''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(batch_size)

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

  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()

  # Create distribution strategy
  strategy = tf.distribute.MirroredStrategy()

  # Get data
  GLOBAL_BATCH_SIZE = BATCH_SIZE * strategy.num_replicas_in_sync
  train_data, validation_data = create_dataset(GLOBAL_BATCH_SIZE)

  # Wrap variable creation within strategy scope
  with strategy.scope():
    model = create_model(args.num_units, args.learning_rate, args.momentum)

  # Train model
  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()

Sehen wir uns den Code genauer an und untersuchen wir die Komponenten, die speziell für das verteilte Training und die Hyperparameter-Abstimmung verwendet werden.

Verteiltes Training

  1. In der Funktion main() wird das Objekt MirroredStrategy erstellt. Als Nächstes umschließen Sie die Erstellung Ihrer Modellvariablen mit dem Bereich der Strategie. Mit diesem Schritt wird TensorFlow mitgeteilt, welche Variablen auf den GPUs gespiegelt werden sollen.
  2. Die Batchgröße wird mit num_replicas_in_sync skaliert. Das Skalieren der Batchgröße ist eine Best Practice bei der Verwendung synchroner Datenparallelitätsstrategien in TensorFlow. Weitere Informationen hier.

Hyperparameter-Abstimmung

  1. Das Skript importiert die Bibliothek hypertune. Wenn wir später das Container-Image erstellen, müssen wir darauf achten, dass diese Bibliothek installiert ist.
  2. Die Funktion get_args() definiert ein Befehlszeilenargument für jeden abzustimmenden Hyperparameter. In diesem Beispiel werden die Lernrate, der Momentum-Wert im Optimierer und die Anzahl der Einheiten in der letzten verborgenen Schicht des Modells abgestimmt. Sie können aber auch mit anderen Hyperparametern experimentieren. Der Wert, der in diesen Argumenten übergeben wird, wird dann verwendet, um den entsprechenden Hyperparameter im Code festzulegen (z. B. learning_rate = args.learning_rate).
  3. Am Ende der Funktion main() wird die Bibliothek hypertune verwendet, um den zu optimierenden Messwert zu definieren. In TensorFlow gibt die Keras-Methode model.fit ein History-Objekt zurück. Das Attribut History.history enthält die Werte für den Trainingsverlust und die Messwerte für aufeinanderfolgende Epochen. Wenn Sie Validierungsdaten an model.fit übergeben, enthält das Attribut History.history auch die Werte für den Validierungsverlust und die Messwerte. Wenn Sie beispielsweise ein Modell für drei Epochen mit Validierungsdaten trainiert und accuracy als Messwert angegeben haben, sieht das Attribut History.history ähnlich wie das folgende Dictionary aus.
{
 "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
 ]

Wenn der Hyperparameter-Abstimmungsdienst die Werte ermitteln soll, mit denen die Validierungsgenauigkeit des Modells maximiert wird, definieren Sie den Messwert als letzten Eintrag (oder NUM_EPOCS - 1) der Liste val_accuracy. Übergeben Sie diesen Messwert dann an eine Instanz von HyperTune. Sie können für hyperparameter_metric_tag eine beliebige String verwenden. Sie müssen die String jedoch später noch einmal verwenden, wenn Sie den Hyperparameter-Abstimmungsjob starten.

6. Code in Container verpacken

Der erste Schritt zum Verpacken Ihres Codes in einen Container besteht darin, ein Dockerfile zu erstellen. Das Dockerfile enthält alle Befehle, die zum Ausführen des Images erforderlich sind. Es installiert alle erforderlichen Bibliotheken und richtet den Einstiegspunkt für den Trainingscode ein.

Schritt 1: Dockerfile schreiben

Achten Sie im Terminal darauf, dass Sie sich im Verzeichnis vertex-codelab befinden, und erstellen Sie ein leeres Dockerfile:

touch Dockerfile

Das Verzeichnis vertex-codelab sollte jetzt Folgendes enthalten:

+ Dockerfile
+ trainer/
    + task.py

Öffnen Sie das Dockerfile und kopieren Sie Folgendes hinein:

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"]

Dieses Dockerfile verwendet das Docker-Image „TensorFlow Enterprise 2.7 GPU“ von Deep Learning Containers. In den Deep Learning Containers in Google Cloud sind viele gängige ML- und Data-Science-Frameworks vorinstalliert. Nach dem Herunterladen dieses Images richtet dieses Dockerfile den Einstiegspunkt für den Trainingscode ein.

Schritt 2: Container erstellen

Führen Sie im Terminal den folgenden Befehl aus, um eine Umgebungsvariable für Ihr Projekt zu definieren. Ersetzen Sie dabei your-cloud-project durch die ID Ihres Projekts:

PROJECT_ID='your-cloud-project'

Definieren Sie eine Variable mit dem URI Ihres Container-Images in der Google Container Registry:

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

Docker konfigurieren

gcloud auth configure-docker

Erstellen Sie dann den Container, indem Sie im Stammverzeichnis von vertex-codelab den folgenden Befehl ausführen:

docker build ./ -t $IMAGE_URI

Laden Sie ihn schließlich in die Google Container Registry hoch:

docker push $IMAGE_URI

Schritt 3: Cloud Storage-Bucket erstellen

In unserem Trainingsjob übergeben wir den Pfad zu einem Staging-Bucket.

Führen Sie im Terminal den folgenden Befehl aus, um einen neuen Bucket in Ihrem Projekt zu erstellen.

BUCKET_NAME="gs://${PROJECT_ID}-hptune-bucket"
gsutil mb -l us-central1 $BUCKET_NAME

7. Hyperparameter-Abstimmungsjob starten

Schritt 1: Benutzerdefinierten Trainingsjob mit Hyperparameter-Abstimmung erstellen

Öffnen Sie im Launcher ein neues TensorFlow 2-Notebook.

new_notebook

Importieren Sie das Vertex AI Python SDK.

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

Um den Hyperparameter-Abstimmungsjob zu starten, müssen Sie zuerst die worker_pool_specs definieren, die den Maschinentyp und das Docker-Image angeben. Die folgende Spezifikation definiert eine Maschine mit zwei NVIDIA Tesla V100-GPUs.

Sie müssen {PROJECT_ID} im image_uri durch Ihr Projekt ersetzen.

# 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": 2
    },
    "replica_count": 1,
    "container_spec": {
        "image_uri": "gcr.io/{PROJECT_ID}/horse-human-codelab:latest"
    }
}]

Definieren Sie als Nächstes die parameter_spec. Das ist ein Dictionary, das die zu optimierenden Parameter angibt. Der Dictionary-Schlüssel ist die String, die Sie dem Befehlszeilenargument für jeden Hyperparameter zugewiesen haben, und der Dictionary-Wert ist die Parameterspezifikation.

Für jeden Hyperparameter müssen Sie den Typ sowie die Grenzen für die Werte definieren, die der Abstimmungsdienst testen soll. Hyperparameter können vom Typ „Double“, „Integer“, „Categorical“ oder „Discrete“ sein. Wenn Sie den Typ „Double“ oder „Integer“ auswählen, müssen Sie einen Mindest- und einen Höchstwert angeben. Wenn Sie „Categorical“ oder „Discrete“ auswählen, müssen Sie die Werte angeben. Für die Typen „Double“ und „Integer“ müssen Sie auch den Skalierungswert angeben. In diesem Video erfahren Sie mehr darüber, wie Sie die beste Skalierung auswählen.

# 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)
}

Die letzte zu definierende Spezifikation ist metric_spec. Das ist ein Dictionary, das den zu optimierenden Messwert darstellt. Der Dictionary-Schlüssel ist das hyperparameter_metric_tag, das Sie im Code Ihrer Trainingsanwendung festgelegt haben, und der Wert ist das Optimierungsziel.

# Dicionary 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'}

Sobald die Spezifikationen definiert sind, erstellen Sie einen CustomJob. Das ist die allgemeine Spezifikation, die verwendet wird, um Ihren Job für jeden der Hyperparameter-Abstimmungstests auszuführen.

Sie müssen {YOUR_BUCKET} durch den Bucket ersetzen, den Sie zuvor erstellt haben.

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

Erstellen und führen Sie dann den HyperparameterTuningJob aus.

hp_job = aiplatform.HyperparameterTuningJob(
    display_name='horses-humans',
    custom_job=my_custom_job,
    metric_spec=metric_spec,
    parameter_spec=parameter_spec,
    max_trial_count=6,
    parallel_trial_count=2,
    search_algorithm=None)

hp_job.run()

Beachten Sie einige Argumente:

  • max_trial_count: Sie müssen eine Obergrenze für die Anzahl der Tests festlegen, die der Dienst ausführt. Mehr Tests führen in der Regel zu besseren Ergebnissen, aber es gibt einen Punkt, ab dem rückläufige Ergebnisse geliefert werden und zusätzliche Tests danach wenig oder keinen Einfluss auf den Messwert haben, den Sie optimieren möchten. Es empfiehlt sich, mit einer kleineren Anzahl von Tests zu beginnen und sich ein Bild davon zu machen, wie sich die gewählten Hyperparameter auswirken, bevor Sie die Anzahl erhöhen.
  • parallel_trial_count: Wenn Sie parallele Tests verwenden, stellt der Dienst mehrere Trainingsverarbeitungscluster bereit. Wenn Sie die Anzahl der parallelen Tests erhöhen, verkürzt sich die Ausführungszeit des Hyperparameter-Abstimmungsjobs. Dies kann jedoch die Effektivität des Jobs insgesamt verringern. Das liegt daran, dass die Standardstrategie für die Abstimmung die Ergebnisse vorheriger Tests verwendet, um die Werte für nachfolgende Tests festzulegen.
  • search_algorithm: Sie können den Suchalgorithmus auf „grid“, „random“ oder „default“ (None) festlegen. Die Standardoption wendet die Bayes'sche Optimierung an, um den Bereich möglicher Hyperparameterwerte zu durchsuchen. Das ist der empfohlene Algorithmus. Weitere Informationen zu diesem Algorithmus hier.

Sobald der Job gestartet wurde, können Sie den Status auf der Benutzeroberfläche auf dem Tab HYPERPARAMETER-ABSTIMMUNGSJOBS verfolgen.

HP_job

Nach Abschluss des Jobs können Sie die Ergebnisse Ihrer Tests ansehen und sortieren, um die beste Kombination von Hyperparameter-Werten zu ermitteln.

HP_results

🎉 Das wars! 🎉

Sie haben gelernt, wie Sie Vertex AI für Folgendes verwenden:

  • Hyperparameter-Abstimmungsjob mit verteiltem Training ausführen

Weitere Informationen zu den verschiedenen Bereichen von Vertex AI finden Sie in der Dokumentation.

8. Bereinigen

Da wir das Notebook so konfiguriert haben, dass es nach 60 Minuten Inaktivität eine Zeitüberschreitung auslöst, müssen wir uns nicht um das Herunterfahren der Instanz kümmern. Wenn Sie die Instanz manuell herunterfahren möchten, klicken Sie im Bereich „Vertex AI Workbench“ der Console auf die Schaltfläche „Beenden“. Wenn Sie das Notebook vollständig löschen möchten, klicken Sie auf die Schaltfläche „Löschen“.

Löschen

Wenn Sie den Storage-Bucket löschen möchten, rufen Sie im Navigationsmenü der Cloud Console „Storage“ auf, wählen Sie den Bucket aus und klicken Sie auf „Löschen“:

Speicher löschen