Training und Hyperparameter-Abstimmung eines PyTorch-Modells in der Cloud AI Platform

1. Übersicht

In diesem Lab gehen Sie einen vollständigen ML-Trainingsworkflow in Google Cloud durch. Dabei verwenden Sie PyTorch, um Ihr Modell zu erstellen. In einer Cloud AI Platform Notebooks-Umgebung erfahren Sie, wie Sie Ihren Trainingsjob verpacken, um ihn in AI Platform Training mit Hyperparameter-Abstimmung auszuführen.

Lerninhalte

Die folgenden Themen werden behandelt:

  • AI Platform Notebooks-Instanz erstellen
  • PyTorch-Modell erstellen
  • Modell mit Hyperparameter-Abstimmung in AI Platform Training trainieren

Die Gesamtkosten für das Lab in Google Cloud belaufen sich auf 1$.

2. Richten Sie Ihre Umgebung ein.

Sie benötigen ein Google Cloud Platform-Projekt mit aktivierter Abrechnung, um dieses Codelab auszuführen. Folgen Sie dieser Anleitung, um ein Projekt zu erstellen.

Schritt 1: Cloud AI Platform Models API aktivieren

Rufen Sie in der Cloud Console den Bereich AI Platform-Modelle auf und klicken Sie auf „Aktivieren“, falls die Option noch nicht aktiviert ist.

d0d38662851c6af3.png

Schritt 2: Compute Engine API aktivieren

Gehen Sie zu Compute Engine und wählen Sie Aktivieren aus, falls dies noch nicht geschehen ist. Sie benötigen dies zum Erstellen Ihrer Notebookinstanz.

Schritt 3: AI Platform Notebooks-Instanz erstellen

Rufen Sie in der Cloud Console den Bereich AI Platform Notebooks auf und klicken Sie auf Neue Instanz. Wählen Sie dann den neuesten PyTorch-Instanztyp (ohne GPUs) aus:

892b7588f940d145.png

Verwenden Sie die Standardoptionen oder geben Sie ihr einen benutzerdefinierten Namen. Klicken Sie dann auf Erstellen. Nachdem die Instanz erstellt wurde, wählen Sie JupyterLab öffnen aus:

63d2cf44801c2df5.png

Öffnen Sie als Nächstes eine Python 3-Notebook-Instanz über den Launcher:

de4c86c6c7f9438f.png

Jetzt kann es losgehen!

Schritt 5: Python-Pakete importieren

Fügen Sie in der ersten Zelle Ihres Notebooks die folgenden Importe hinzu und führen Sie die Zelle aus. Sie können ihn ausführen, indem Sie im oberen Menü den Rechtspfeil oder Befehlstaste + Eingabetaste drücken:

import datetime
import numpy as np
import os
import pandas as pd
import time

Sie werden feststellen, dass PyTorch hier nicht importiert wird. Das liegt daran, dass der Trainingsjob in AI Platform Training und nicht in unserer Notebook-Instanz ausgeführt wird.

3. Paket für den Trainingsjob erstellen

Zum Ausführen unseres Trainingsjobs in AI Platform Training benötigen wir den Trainingscode, der lokal in unserer Notebooks-Instanz verpackt ist, sowie einen Cloud Storage-Bucket, um Assets für den Job zu speichern. Zuerst erstellen wir einen Storage-Bucket. Wenn Sie bereits einen haben, können Sie diesen Schritt überspringen.

Schritt 1: Cloud Storage-Bucket für das Modell erstellen

Definieren wir zuerst einige Umgebungsvariablen, die wir im weiteren Codelab verwenden. Geben Sie in die Werte unten den Namen Ihres Google Cloud-Projekts und den Namen des Cloud Storage-Buckets ein, den Sie erstellen möchten (muss global eindeutig sein):

# Update these to your own GCP project, model, and version names
GCP_PROJECT = 'your-gcp-project'
BOCKET_URL = 'gs://storage_bucket_name'

Jetzt können wir einen Storage-Bucket erstellen, auf den wir verweisen, wenn wir den Trainingsjob starten.

Führen Sie den folgenden gsutil-Befehl in Ihrem Notebook aus, um einen Bucket zu erstellen:

!gsutil mb $BUCKET_URL

Schritt 2: Erste Dateien für das Python-Paket erstellen

Zum Ausführen eines Trainingsjobs in AI Platform müssen Sie unseren Code als Python-Paket konfigurieren. Es besteht aus einer setup.py-Datei in unserem Stammverzeichnis, in der alle externen Paketabhängigkeiten angegeben sind, einem Unterverzeichnis mit dem Namen des Pakets (hier trainer/ nennen) und einer leeren __init__.py-Datei in diesem Unterverzeichnis.

Schreiben wir zunächst unsere Datei „setup.py“. Wir verwenden die Magien %%writefile von iPython, um die Datei in unserer Instanz zu speichern. Hier haben wir drei externe Bibliotheken angegeben, die wir in unserem Trainingscode verwenden werden: PyTorch, Scikit-learn und Pandas:

%%writefile setup.py
from setuptools import find_packages
from setuptools import setup

REQUIRED_PACKAGES = ['torch>=1.5', 'scikit-learn>=0.20', 'pandas>=1.0']

setup(
    name='trainer',
    version='0.1',
    install_requires=REQUIRED_PACKAGES,
    packages=find_packages(),
    include_package_data=True,
    description='My training application package.'
)

Als Nächstes erstellen wir das Verzeichnis trainer/ und die darin enthaltene leere init.py-Datei. Python verwendet diese Datei, um zu erkennen, dass es sich um ein Paket handelt:

!mkdir trainer
!touch trainer/__init__.py

Jetzt können wir mit dem Erstellen des Trainingsjobs beginnen.

4. Dataset als Vorschau ansehen

Der Schwerpunkt dieses Labs liegt auf den Tools zum Trainieren von Modellen. Werfen wir jedoch einen kurzen Blick auf das Dataset, mit dem wir das Modell trainieren. Wir verwenden das in BigQuery verfügbare Dataset zur Geburtenrate. Es enthält Geburten aus den USA über mehrere Jahrzehnte. Wir verwenden einige Spalten aus dem Dataset, um das Geburtsgewicht eines Babys vorherzusagen. Das ursprüngliche Dataset ist ziemlich groß und wir werden einen Teil davon verwenden, den wir Ihnen in einem Cloud Storage-Bucket zur Verfügung gestellt haben.

Schritt 1: BigQuery-Dataset zur Geburtenrate herunterladen

Lassen Sie uns die Version des Datasets, das wir Ihnen in Cloud Storage zur Verfügung gestellt haben, in einen Pandas-DataFrame herunterladen und eine Vorschau anzeigen.

natality = pd.read_csv('https://storage.googleapis.com/ml-design-patterns/natality.csv')
natality.head()

Dieses Dataset hat knapp 100.000 Zeilen. Wir verwenden fünf Funktionen, um das Geburtsgewicht eines Babys vorherzusagen: das Alter von Mutter und Vater, Schwangerschaftswochen, Gewichtszunahme der Mutter in Pfund und das Geschlecht des Babys als boolescher Wert.

5. Trainingsjob mit Hyperparameter-Abstimmung definieren

Wir schreiben unser Trainingsskript in eine Datei namens model.py im Unterverzeichnis „trainer/“, das wir zuvor erstellt haben. Unser Trainingsjob wird in AI Platform Training ausgeführt und nutzt außerdem den Hyperparameter-Abstimmungsdienst von AI Platform, um mithilfe der Bayes'schen Optimierung die optimalen Hyperparameter für unser Modell zu finden.

Schritt 1: Trainingsskript erstellen

Zuerst erstellen wir die Python-Datei mit unserem Trainingsskript. Dann analysieren wir, was darin passiert. Durch das Ausführen dieses %%writefile -Befehls wird der Modellcode in eine lokale Python-Datei geschrieben:

%%writefile trainer/model.py
import argparse
import hypertune
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim

from sklearn.utils import shuffle
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import normalize

def get_args():
    """Argument parser.
    Returns:
        Dictionary of arguments.
    """
    parser = argparse.ArgumentParser(description='PyTorch MNIST')
    parser.add_argument('--job-dir',  # handled automatically by AI Platform
                        help='GCS location to write checkpoints and export ' \
                             'models')
    parser.add_argument('--lr',  # Specified in the config file
                        type=float,
                        default=0.01,
                        help='learning rate (default: 0.01)')
    parser.add_argument('--momentum',  # Specified in the config file
                        type=float,
                        default=0.5,
                        help='SGD momentum (default: 0.5)')
    parser.add_argument('--hidden-layer-size',  # Specified in the config file
                        type=int,
                        default=8,
                        help='hidden layer size')
    args = parser.parse_args()
    return args

def train_model(args):
    # Get the data
    natality = pd.read_csv('https://storage.googleapis.com/ml-design-patterns/natality.csv')
    natality = natality.dropna()
    natality = shuffle(natality, random_state = 2)
    natality.head()

    natality_labels = natality['weight_pounds']
    natality = natality.drop(columns=['weight_pounds'])


    train_size = int(len(natality) * 0.8)
    traindata_natality = natality[:train_size]
    trainlabels_natality = natality_labels[:train_size]

    testdata_natality = natality[train_size:]
    testlabels_natality = natality_labels[train_size:]

    # Normalize and convert to PT tensors
    normalized_train = normalize(np.array(traindata_natality.values), axis=0)
    normalized_test = normalize(np.array(testdata_natality.values), axis=0)

    train_x = torch.Tensor(normalized_train)
    train_y = torch.Tensor(np.array(trainlabels_natality))

    test_x = torch.Tensor(normalized_test)
    test_y = torch.Tensor(np.array(testlabels_natality))

    # Define our data loaders
    train_dataset = torch.utils.data.TensorDataset(train_x, train_y)
    train_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=128, shuffle=True)

    test_dataset = torch.utils.data.TensorDataset(test_x, test_y)
    test_dataloader = torch.utils.data.DataLoader(test_dataset, batch_size=128, shuffle=False)

    # Define the model, while tuning the size of our hidden layer
    model = nn.Sequential(nn.Linear(len(train_x[0]), args.hidden_layer_size),
                          nn.ReLU(),
                          nn.Linear(args.hidden_layer_size, 1))
    criterion = nn.MSELoss()

    # Tune hyperparameters in our optimizer
    optimizer = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum)
    epochs = 20
    for e in range(epochs):
        for batch_id, (data, label) in enumerate(train_dataloader):
            optimizer.zero_grad()
            y_pred = model(data)
            label = label.view(-1,1)
            loss = criterion(y_pred, label)
            
            loss.backward()
            optimizer.step()


    val_mse = 0
    num_batches = 0
    # Evaluate accuracy on our test set
    with torch.no_grad():
        for i, (data, label) in enumerate(test_dataloader):
            num_batches += 1
            y_pred = model(data)
            mse = criterion(y_pred, label.view(-1,1))
            val_mse += mse.item()


    avg_val_mse = (val_mse / num_batches)

    # Report the metric we're optimizing for to AI Platform's HyperTune service
    # In this example, we're mimizing error on our test set
    hpt = hypertune.HyperTune()
    hpt.report_hyperparameter_tuning_metric(
        hyperparameter_metric_tag='val_mse',
        metric_value=avg_val_mse,
        global_step=epochs        
    )

def main():
    args = get_args()
    print('in main', args)
    train_model(args)

if __name__ == '__main__':
    main()

Der Trainingsjob besteht aus zwei Funktionen, bei denen der Großteil der Arbeit stattfindet.

  • get_args(): Damit werden die Befehlszeilenargumente analysiert, die beim Erstellen des Trainingsjobs übergeben werden, sowie die Hyperparameter, die AI Platform optimieren soll. In diesem Beispiel enthält unsere Liste von Argumenten nur die Hyperparameter, die wir optimieren: die Lernrate unseres Modells, den Impuls und die Anzahl der Neuronen in unserer verborgenen Schicht.
  • train_model(): Hier laden wir die Daten in einen Pandas DataFrame herunter, normalisieren sie, konvertieren sie in PyTorch Tensors und definieren dann unser Modell. Zum Erstellen unseres Modells verwenden wir die PyTorch API von nn.Sequential, mit der wir das Modell als Ebenenstapel definieren können:
model = nn.Sequential(nn.Linear(len(train_x[0]), args.hidden_layer_size),
                      nn.ReLU(),
                      nn.Linear(args.hidden_layer_size, 1))

Anstatt die Größe der verborgenen Ebene unseres Modells hartzucodieren, machen wir diesen Hyperparameter, den AI Platform für uns abstimmt. Mehr dazu im nächsten Abschnitt.

Schritt 2: Hyperparameter-Abstimmungsdienst von AI Platform verwenden

Anstatt verschiedene Hyperparameter-Werte manuell auszuprobieren und unser Modell jedes Mal neu zu trainieren, verwenden wir den Hyperparameter-Optimierungsdienst der Cloud AI Platform. Wenn wir unseren Trainingsjob mit Hyperparameter-Argumenten einrichten, verwendet AI Platform die Bayes'sche Optimierung, um die idealen Werte für die angegebenen Hyperparameter zu finden.

Bei der Hyperparameter-Abstimmung besteht ein einzelner Test aus einem Trainingslauf unseres Modells mit einer bestimmten Kombination von Hyperparameter-Werten. Je nachdem, wie viele Tests wir ausführen, verwendet AI Platform die Ergebnisse abgeschlossener Tests, um die Hyperparameter für zukünftige Tests zu optimieren. Um die Hyperparameter-Abstimmung zu konfigurieren, müssen wir eine Konfigurationsdatei übergeben, wenn wir unseren Trainingsjob mit einigen Daten zu jedem der zu optimierenden Hyperparameter starten.

Erstellen Sie als Nächstes diese Konfigurationsdatei lokal:

%%writefile config.yaml
trainingInput:
  hyperparameters:
    goal: MINIMIZE
    maxTrials: 10
    maxParallelTrials: 5
    hyperparameterMetricTag: val_mse
    enableTrialEarlyStopping: TRUE
    params:
    - parameterName: lr
      type: DOUBLE
      minValue: 0.0001
      maxValue: 0.1
      scaleType: UNIT_LINEAR_SCALE
    - parameterName: momentum
      type: DOUBLE
      minValue: 0.0
      maxValue: 1.0
      scaleType: UNIT_LINEAR_SCALE
    - parameterName: hidden-layer-size
      type: INTEGER
      minValue: 8
      maxValue: 32
      scaleType: UNIT_LINEAR_SCALE

Für jeden Hyperparameter geben wir den Typ, den zu suchenden Wertebereich und die Skala an, auf der der Wert in verschiedenen Tests erhöht werden soll.

Zu Beginn des Jobs geben wir auch die Metrik an, für die wir optimieren. Am Ende der train_model()-Funktion oben melden wir diesen Messwert bei jedem Ende eines Tests an AI Platform. Hier minimieren wir den mittleren quadratischen Fehler unseres Modells und verwenden daher die Hyperparameter, die den niedrigsten mittleren quadratischen Fehler für unser Modell liefern. Der Name dieses Messwerts (val_mse) stimmt mit dem Namen überein, mit dem wir ihn melden, wenn wir am Ende eines Tests report_hyperparameter_tuning_metric() aufrufen.

6. Trainingsjob in AI Platform ausführen

In diesem Abschnitt starten wir unseren Modelltrainingsjob mit Hyperparameter-Abstimmung in AI Platform.

Schritt 1: Einige Umgebungsvariablen definieren

Lassen Sie uns zuerst einige Umgebungsvariablen definieren, mit denen wir unseren Trainingsjob starten. Wenn Sie den Job in einer anderen Region ausführen möchten, aktualisieren Sie die folgende REGION-Variable:

MAIN_TRAINER_MODULE = "trainer.model"
TRAIN_DIR = os.getcwd() + '/trainer'
JOB_DIR = BUCKET_URL + '/output'
REGION = "us-central1"

Jeder Trainingsjob in AI Platform sollte einen eindeutigen Namen haben. Führen Sie den folgenden Befehl aus, um mithilfe eines Zeitstempels eine Variable für den Namen Ihres Jobs zu definieren:

timestamp = str(datetime.datetime.now().time())
JOB_NAME = 'caip_training_' + str(int(time.time()))

Schritt 2: Trainingsjob starten

Wir erstellen den Trainingsjob mit gcloud, der Google Cloud CLI. Wir können diesen Befehl direkt in unserem Notebook ausführen und dabei auf die oben definierten Variablen verweisen:

!gcloud ai-platform jobs submit training $JOB_NAME \
        --scale-tier basic \
        --package-path $TRAIN_DIR \
        --module-name $MAIN_TRAINER_MODULE \
        --job-dir $JOB_DIR \
        --region $REGION \
        --runtime-version 2.1 \
        --python-version 3.7 \
        --config config.yaml

Wenn der Job korrekt erstellt wurde, rufen Sie in der AI Platform Console den Abschnitt Jobs auf, um die Logs zu überwachen.

Schritt 3: Job überwachen

Klicken Sie im Bereich „Jobs“ der Console auf den Job, den Sie gerade gestartet haben, um Details aufzurufen:

c184167641bb7ed7.png

Zu Beginn Ihrer ersten Testrunde können Sie die für jeden Test ausgewählten Hyperparameter-Werte sehen:

787c053ef9110e6b.png

Nach Abschluss von Tests wird der Wert Ihres Optimierungsmesswerts (in diesem Fall val_mse) hier protokolliert. Die Ausführung des Jobs sollte 15 bis 20 Minuten dauern und das Dashboard sieht in etwa so aus, wenn der Job abgeschlossen ist (die genauen Werte variieren):

47ef6b9b4ecb532c.png

Wenn Sie potenzielle Probleme beheben und den Job genauer beobachten möchten, klicken Sie auf der Seite mit den Jobdetails auf Logs ansehen:

18c32dcd36351930.png

Jede print()-Anweisung in Ihrem Modelltrainingscode wird hier angezeigt. Wenn Probleme auftreten, fügen Sie weitere Druckanweisungen hinzu und starten Sie einen neuen Trainingsjob.

Suchen Sie nach Abschluss des Trainingsjobs die Hyperparameter, die den niedrigsten Wert für „val_mse“ erzielt haben. Sie können diese entweder verwenden, um eine endgültige Version Ihres Modells zu trainieren und zu exportieren, oder sie als Anhaltspunkt für einen weiteren Trainingsjob mit zusätzlichen Tests zur Hyperparameter-Abstimmung verwenden.

7. Bereinigen

Wenn Sie dieses Notebook weiterverwenden möchten, empfehlen wir Ihnen, es bei Nichtgebrauch auszuschalten. Wählen Sie in der Notebooks-UI in der Cloud Console das Notebook und dann Beenden aus:

879147427150b6c7.png

Wenn Sie alle Ressourcen löschen möchten, die Sie in diesem Lab erstellt haben, löschen Sie einfach die Notebookinstanz, anstatt sie zu beenden.

Gehen Sie im Navigationsmenü der Cloud Console zu „Storage“ und löschen Sie beide Buckets, die Sie zum Speichern Ihrer Modell-Assets erstellt haben.