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

1. Übersicht

In diesem Lab durchlaufen Sie einen vollständigen ML-Trainingsworkflow in Google Cloud und verwenden PyTorch, um Ihr Modell zu erstellen. In einer Cloud AI Platform Notebooks-Umgebung erfahren Sie, wie Sie Ihren Trainingsjob verpacken, um ihn mit Hyperparameter-Abstimmung in AI Platform Training 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 die Ausführung dieses Labs in Google Cloud betragen etwa 1$.

2. Richten Sie Ihre Umgebung ein.

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

Schritt 1: Cloud AI Platform Models API aktivieren

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

d0d38662851c6af3.png

Schritt 2: Compute Engine API aktivieren

Rufen Sie Compute Engine auf und wählen Sie Aktivieren aus, falls die API noch nicht aktiviert ist. Sie benötigen diese, um Ihre Notebook-Instanz zu erstellen.

Schritt 3: AI Platform Notebooks-Instanz erstellen

Rufen Sie in der Cloud Console den Abschnitt „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 einen benutzerdefinierten Namen ein. Klicken Sie dann auf Erstellen. Nachdem die Instanz erstellt wurde, klicken Sie auf JupyterLab öffnen:

63d2cf44801c2df5.png

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

de4c86c6c7f9438f.png

Jetzt können Sie loslegen.

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 es ausführen, indem Sie im oberen Menü auf die Rechtspfeiltaste oder auf die Tastenkombination „Befehl + Eingabetaste“ drücken:

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

Wie Sie sehen, importieren wir PyTorch hier nicht. Das liegt daran, dass wir den Trainingsjob in AI Platform Training und nicht in unserer Notebook-Instanz ausführen.

3. Paket für den Trainingsjob erstellen

Damit wir unseren Trainingsjob in AI Platform Training ausführen können, benötigen wir unseren Trainingscode, der lokal in unserer Notebooks-Instanz verpackt ist, und einen Cloud Storage-Bucket zum Speichern von Assets für unseren Job. Zuerst erstellen wir einen Storage-Bucket. Wenn Sie bereits ein Konto 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 restlichen Codelab verwenden werden. Geben Sie 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 Speicher-Bucket erstellen, auf den wir verweisen, wenn wir den Trainingsjob starten.

Führen Sie diesen 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

Damit wir einen Trainingsjob in AI Platform ausführen können, müssen wir 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 unseres Pakets (hier nennen wir es trainer/) und einer leeren __init__.py-Datei in diesem Unterverzeichnis.

Schreiben wir zuerst unsere Datei „setup.py“. Wir verwenden die iPython-Magics %%writefile, um die Datei auf 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 leere Datei init.py darin. 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 unseres Trainingsjobs beginnen.

4. Dataset als Vorschau ansehen

In diesem Lab geht es hauptsächlich um die Tools zum Trainieren von Modellen. Sehen wir uns aber kurz den Datensatz an, mit dem wir unser Modell trainieren werden. Wir verwenden das Dataset „natality“, das in BigQuery verfügbar ist. Es enthält Geburtsdaten aus den USA über mehrere Jahrzehnte hinweg. Wir verwenden einige Spalten aus dem Dataset, um das Geburtsgewicht eines Babys vorherzusagen. Das ursprüngliche Dataset ist recht groß. Wir verwenden eine Teilmenge davon, die wir Ihnen in einem Cloud Storage-Bucket zur Verfügung gestellt haben.

Schritt 1: BigQuery-Dataset „natality“ herunterladen

Laden wir die Version des Datasets, die wir Ihnen in Cloud Storage zur Verfügung gestellt haben, in einen Pandas DataFrame herunter und sehen wir uns eine Vorschau an.

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

Dieses Dataset enthält knapp 100.000 Zeilen. Wir verwenden fünf Merkmale, um das Geburtsgewicht eines Babys vorherzusagen: Alter der Mutter und des Vaters, Schwangerschaftswochen, Gewichtszunahme der Mutter in Pfund und Geschlecht des Babys (als boolescher Wert).

5. Trainingsjob mit Hyperparameter-Abstimmung definieren

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

Schritt 1: Trainingsscript erstellen

Erstellen wir zuerst die Python-Datei mit unserem Trainingsskript. Danach sehen wir uns an, was darin passiert. Wenn Sie diesen %%writefile-Befehl ausführen, 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, in denen der Großteil der Arbeit erfolgt.

  • get_args(): Hier werden die Befehlszeilenargumente geparst, die wir beim Erstellen unseres Trainingsjobs übergeben, sowie die Hyperparameter, die AI Platform optimieren soll. In diesem Beispiel enthält die Liste der Argumente nur die Hyperparameter, die wir optimieren möchten: die Lernrate, den Impuls und die Anzahl der Neuronen in der verborgenen Ebene des Modells.
  • train_model(): Hier laden wir die Daten in ein Pandas DataFrame herunter, normalisieren sie, wandeln sie in PyTorch-Tensoren um und definieren dann unser Modell. Für unser Modell verwenden wir die PyTorch-nn.Sequential-API, mit der wir unser Modell als Stapel von Layern 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 Schicht unseres Modells fest zu codieren, machen wir sie zu einem Hyperparameter, der von AI Platform für uns optimiert wird. Dazu mehr 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 Dienst zur Hyperparameter-Optimierung von Cloud AI Platform. Wenn wir unseren Trainingsjob mit Hyperparameterargumenten einrichten, verwendet AI Platform die Bayes'sche Optimierung, um die idealen Werte für die von uns 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 ausgeführt werden, verwendet AI Platform die Ergebnisse abgeschlossener Tests, um die Hyperparameter zu optimieren, die für zukünftige Tests ausgewählt werden. Um die Hyperparameter-Abstimmung zu konfigurieren, müssen wir eine Konfigurationsdatei übergeben, wenn wir unseren Trainingsjob mit einigen Daten zu den einzelnen Hyperparametern starten, die wir optimieren.

Erstellen Sie als Nächstes die 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 Wertebereich, in dem wir suchen möchten, und die Skala an, auf der der Wert in den verschiedenen Tests erhöht werden soll.

Zu Beginn des Jobs geben wir auch den Messwert an, für den wir optimieren. Beachten Sie, dass wir am Ende der train_model()-Funktion oben diesen Messwert jedes Mal an AI Platform melden, wenn ein Test abgeschlossen ist. Hier minimieren wir den mittleren quadratischen Fehler unseres Modells. Wir möchten also die Hyperparameter verwenden, die den niedrigsten mittleren quadratischen Fehler für unser Modell ergeben. Der Name dieses Messwerts (val_mse) entspricht dem Namen, den wir verwenden, um ihn zu melden, wenn wir report_hyperparameter_tuning_metric() am Ende eines Testzeitraums aufrufen.

6. Trainingsjob in AI Platform ausführen

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

Schritt 1: Umgebungsvariablen definieren

Zuerst definieren wir einige Umgebungsvariablen, die wir zum Starten unseres Trainingsjobs verwenden. Wenn Sie den Job in einer anderen Region ausführen möchten, aktualisieren Sie die REGION-Variable unten:

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 eine Variable für den Namen Ihres Jobs mit einem Zeitstempel zu definieren:

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

Schritt 2: Trainingsjob starten

Wir erstellen unseren 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 Ihr Job korrekt erstellt wurde, können Sie die Logs in der AI Platform Console im Abschnitt „Jobs“ überwachen.

Schritt 3: Job überwachen

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

c184167641bb7ed7.png

Sobald die erste Testrunde beginnt, können Sie die für die einzelnen Tests ausgewählten Hyperparameterwerte sehen:

787c053ef9110e6b.png

Nach Abschluss der Testläufe wird der resultierende Wert des Optimierungs-Messwerts (in diesem Fall val_mse) hier protokolliert. Die Ausführung des Jobs dauert etwa 15 bis 20 Minuten. Das Dashboard sollte nach Abschluss des Jobs in etwa so aussehen (die genauen Werte können variieren):

47ef6b9b4ecb532c.png

Wenn Sie potenzielle Probleme beheben und Ihren Job genauer überwachen 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 print-Anweisungen hinzu und starten Sie einen neuen Trainingsjob.

Suchen Sie nach Abschluss des Trainingsjobs nach den Hyperparametern, die den niedrigsten „val_mse“-Wert ergeben haben. Sie können diese entweder verwenden, um eine endgültige Version Ihres Modells zu trainieren und zu exportieren, oder als Orientierungshilfe, um einen weiteren Trainingsjob mit zusätzlichen Hyperparameter-Abstimmungstests zu starten.

7. Bereinigen

Wenn Sie dieses Notebook weiterhin verwenden möchten, empfehlen wir, es auszuschalten, wenn Sie es nicht verwenden. Wählen Sie in der Notebooks-Benutzeroberfläche 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 Notebook-Instanz, anstatt sie zu beenden.

Rufen Sie in der Cloud Console über das Navigationsmenü „Storage“ auf und löschen Sie die beiden Buckets, die Sie zum Speichern Ihrer Modell-Assets erstellt haben.