Vertex AI: Verteilte Hyperparameter-Abstimmung

1. Übersicht

In diesem Lab erfahren Sie, wie Sie Vertex AI für die Hyperparameter-Abstimmung und verteiltes Training verwenden. In diesem Lab wird TensorFlow für den Modellcode verwendet. Die Konzepte gelten jedoch auch für andere ML-Frameworks.

Lerninhalte

Die folgenden Themen werden behandelt:

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

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

2. Einführung in Vertex AI

In diesem Lab wird das neueste KI-Produktangebot von Google Cloud verwendet. Vertex AI integriert die ML-Angebote in Google Cloud für eine nahtlose Entwicklung. Bisher konnten auf mit AutoML trainierte Modelle und benutzerdefinierte Modelle über separate Dienste zugegriffen werden. Das neue Angebot kombiniert beide zusammen mit anderen neuen Produkten in einer einzigen API. Sie können auch vorhandene Projekte zu Vertex AI migrieren. Wenn du Feedback hast, sieh auf der Supportseite nach.

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 ermitteln Sie mithilfe der Hyperparameter-Abstimmung optimale Parameter für ein Bildklassifizierungsmodell, das mit dem Pferde- oder Mensch-Dataset 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 Tests und nimmt Anpassungen für nachfolgende Tests 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 mit diesen Argumenten übergebenen Wert, um den entsprechenden Hyperparameter im Code Ihrer Anwendung festzulegen.

Verteiltes Training

Wenn Sie eine einzelne GPU haben, beschleunigt TensorFlow das Modelltraining mit diesem Beschleuniger, ohne dass Sie etwas tun müssen. Wenn Sie jedoch durch die Verwendung mehrerer GPUs zusätzliche Leistung erzielen möchten, müssen Sie tf.distribute verwenden. Das Modul von TensorFlow ist das Modul zur Ausführung einer Berechnung auf mehreren Geräten.

In diesem Lab wird tf.distribute.MirroredStrategy verwendet, das Sie Ihren Trainingsanwendungen mit nur wenigen Codeänderungen hinzufügen können. Bei dieser Strategie wird auf jeder GPU auf Ihrem Computer eine Kopie des Modells erstellt. Die nachfolgenden Farbverlaufsaktualisierungen erfolgen synchron. Das bedeutet, dass jede GPU die Vorwärts- und Rückwärtsdurchläufe durch das Modell für einen anderen Ausschnitt der Eingabedaten berechnet. Die berechneten Gradienten aus den einzelnen Segmenten werden dann über alle GPUs aggregiert und in einem als All-Reduce bezeichneten Prozess gemittelt. Die Modellparameter werden anhand dieser 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 Video unten an:

4. Umgebung einrichten

Sie benötigen ein Google Cloud-Projekt mit aktivierter Abrechnung, um dieses Codelab ausführen zu können. 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 Option noch nicht aktiviert ist.

Schritt 2: Container Registry API aktivieren

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

Schritt 3: Vertex AI API aktivieren

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

Vertex AI-Dashboard

Schritt 4: Vertex AI Workbench-Instanz erstellen

Klicken Sie in der Cloud Console im Bereich „Vertex AI“ 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 NOTIZBUCH aus.

new_notebook

Geben Sie einen Namen für das Notebook ein 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 Notebook wird also automatisch heruntergefahren, wenn es nicht verwendet wird, sodass keine unnötigen Kosten entstehen.

idle_timeout

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

Terminal aktivieren

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

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

Wählen Sie nach dem Erstellen der Instanz JupyterLab öffnen aus.

open_jupyterlab

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

Authentifizieren

5. Trainingscode schreiben

Öffnen Sie zuerst über das Launcher-Menü ein Terminalfenster in Ihrer Notebook-Instanz:

launcher_terminal

Erstellen Sie ein neues Verzeichnis mit dem Namen vertex-codelab und fügen Sie es mit cd ein.

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 die Sie den Code einfügen:

mkdir trainer
touch trainer/task.py

Ihr vertex-codelab-Verzeichnis sollte jetzt so aussehen:

+ trainer/
    + task.py

Öffnen Sie als Nächstes die Datei task.py, die Sie gerade erstellt haben, und fügen Sie den gesamten Code unten 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 die Komponenten, die speziell für die verteilte Trainings- und Hyperparameterabstimmung verwendet werden.

Verteiltes Training

  1. In der Funktion main() wird das MirroredStrategy-Objekt erstellt. Als Nächstes legen Sie fest, dass die Modellvariablen im Geltungsbereich der Strategie erstellt werden. In diesem Schritt wird TensorFlow mitgeteilt, welche Variablen in den GPUs gespiegelt werden sollen.
  2. Die Batchgröße wird um den Wert von num_replicas_in_sync skaliert. Das Skalieren der Batchgröße ist eine Best Practice bei der Verwendung von Strategien zur synchronen Datenparallelität in TensorFlow. Weitere Informationen

Hyperparameter-Abstimmung

  1. Das Script importiert die hypertune-Bibliothek. Später, wenn wir das Container-Image erstellen, müssen wir diese Bibliothek installieren.
  2. Die Funktion get_args() definiert ein Befehlszeilenargument für jeden Hyperparameter, den Sie abstimmen möchten. In diesem Beispiel werden die Hyperparameter Lernrate, der Momentum-Wert im Optimierer und die Anzahl der Einheiten in der letzten verborgenen Schicht des Modells angepasst. Sie können aber auch mit anderen experimentieren. Der in diesen Argumenten übergebene Wert wird dann verwendet, um den entsprechenden Hyperparameter im Code festzulegen (z. B. learning_rate = args.learning_rate festlegen).
  3. Am Ende der Funktion main() wird die Bibliothek hypertune verwendet, um den Messwert zu definieren, den Sie optimieren möchten. In TensorFlow gibt die Keras-Methode model.fit ein History-Objekt zurück. Das Attribut History.history enthält Werte für den Trainingsverlust und Messwerte für aufeinanderfolgende Epochen. Wenn Sie Validierungsdaten an model.fit übergeben, enthält das History.history-Attribut auch Werte für Validierungsverluste und Messwerte. Wenn Sie beispielsweise ein Modell mit Validierungsdaten für drei Epochen trainiert und accuracy als Messwert angegeben haben, sieht das Attribut History.history in etwa so aus wie das folgende Wörterbuch.
{
 "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 Dienst zur Hyperparameter-Abstimmung 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 einen beliebigen String für hyperparameter_metric_tag auswählen. Sie müssen ihn jedoch später noch einmal verwenden, wenn Sie den Job zur Hyperparameter-Abstimmung starten.

6. Code containerisieren

Der erste Schritt bei der Containerisierung Ihres Codes besteht darin, ein Dockerfile zu erstellen. Im Dockerfile geben Sie alle Befehle an, die zum Ausführen des Images erforderlich sind. Dadurch werden alle erforderlichen Bibliotheken installiert und der Einstiegspunkt für den Trainingscode eingerichtet.

Schritt 1: Dockerfile schreiben

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

touch Dockerfile

In Ihrem vertex-codelab-Verzeichnis sollte nun Folgendes vorhanden sein:

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

In diesem Dockerfile wird das Deep Learning Container TensorFlow Enterprise 2.7 GPU Docker-Image verwendet. Die Deep Learning Container in Google Cloud sind mit vielen gängigen ML- und Data-Science-Frameworks vorinstalliert. Nach dem Herunterladen dieses Images wird in diesem Dockerfile der Einstiegspunkt für den Trainingscode eingerichtet.

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 Ihres vertex-codelab-Verzeichnisses Folgendes ausführen:

docker build ./ -t $IMAGE_URI

Übertragen Sie die Datei anschließend per Push in Google Container Registry:

docker push $IMAGE_URI

Schritt 3: Cloud Storage-Bucket erstellen

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

Führen Sie im Terminal 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 über den 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, in der der Maschinentyp und das Docker-Image angegeben werden. In der folgenden Spezifikation wird eine Maschine mit zwei NVIDIA Tesla V100-GPUs definiert.

Ersetzen Sie {PROJECT_ID} in image_uri durch Ihr Projekt.

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

Als Nächstes definieren Sie parameter_spec. Das ist ein Dictionary, in dem die Parameter angegeben werden, die Sie optimieren möchten. Der Dictionary-Schlüssel ist der String, den Sie dem Befehlszeilenargument für jeden Hyperparameter zugewiesen haben. Der Dictionary-Wert ist die Parameterspezifikation.

Für jeden Hyperparameter müssen Sie den Typ sowie die Grenzen für die Werte definieren, die vom Abgleichsdienst ausprobiert werden sollen. Hyperparameter können vom Typ „Dezimalzahl“, „Ganzzahl“, „Kategorisch“ oder „Diskret“ sein. Wenn Sie den Typ „Dezimalzahl“ oder „Ganzzahl“ auswählen, müssen Sie einen Mindest- und einen Höchstwert angeben. Wenn Sie „Kategorisch“ oder „Diskret“ auswählen, müssen Sie die Werte angeben. Für die Typen Double und Integer müssen Sie außerdem den Skalierungswert angeben. In diesem Video erfahren Sie mehr darüber, wie Sie die richtige 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 Spezifikation, die Sie definieren müssen, ist metric_spec. Das ist ein Wörterbuch, das den zu optimierenden Messwert darstellt. Der Wörterbuchschlüssel ist die hyperparameter_metric_tag, die Sie im Code Ihrer Trainingsanwendung festgelegt haben, und der Wert ist das Zielvorhaben für die Optimierung.

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

Nachdem Sie die Spezifikationen definiert haben, erstellen Sie eine CustomJob. Dies ist die gemeinsame Spezifikation, mit der Ihr Job in jedem der Hyperparameter-Abstimmungstests ausgeführt wird.

Ersetzen Sie {YOUR_BUCKET} durch den zuvor erstellten Bucket.

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

Hier sind einige Argumente:

  • max_trial_count: Sie müssen eine Obergrenze für die Anzahl der Tests festlegen, die der Dienst ausführen soll. Mehr Tests führen in der Regel zu besseren Ergebnissen. Es gibt jedoch einen Punkt, ab dem zusätzliche Tests wenig oder gar 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 ausgewä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 beeinträchtigen. Das liegt daran, dass die Standardabstimmungsstrategie die Ergebnisse vorheriger Tests verwendet, um die Werte in nachfolgenden Tests zuzuweisen.
  • search_algorithm: Sie können den Suchalgorithmus auf die Raster-, die Zufalls- oder die Standardeinstellung (Keiner) einstellen. Bei der Standardoption wird die Bayes'sche Optimierung angewendet, um den Bereich möglicher Hyperparameterwerte zu durchsuchen. Dies ist der empfohlene Algorithmus. Weitere Informationen zu diesem Algorithmus

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

HP_job

Sobald der Job abgeschlossen ist, können Sie die Ergebnisse Ihrer Tests aufrufen und sortieren, um die beste Kombination von Hyperparameterwerten zu ermitteln.

HP_results

🎉 Glückwunsch! 🎉

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

  • Hyperparameter-Abstimmungsjob mit verteiltem Training ausführen

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

8. Bereinigen

Da wir das Notebook so konfiguriert haben, dass nach 60 Minuten Inaktivität ein Zeitlimit erreicht wird, müssen wir uns keine Gedanken über das Herunterfahren der Instanz machen. Wenn Sie die Instanz manuell herunterfahren möchten, klicken Sie in der Console im Bereich „Vertex AI Workbench“ auf die Schaltfläche „Beenden“. Wenn Sie das Notizbuch vollständig löschen möchten, klicken Sie auf Löschen.

Löschen

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

Speicher löschen