Prototyp w środowisku produkcyjnym: dostrajanie hiperparametrów

1. Omówienie

W tym module użyjesz Vertex AI, aby uruchomić zadanie dostrajania hiperparametrów w Vertex AI Training.

Ten moduł jest częścią serii filmów Prototype to Production. Zanim rozpoczniesz ten moduł, ukończ poprzedni moduł. Aby dowiedzieć się więcej, możesz obejrzeć serię filmów towarzyszących:

.

Czego się nauczysz

Poznasz takie zagadnienia jak:

  • Zmodyfikuj kod aplikacji treningowej na potrzeby automatycznego dostrajania hiperparametrów
  • Skonfiguruj i uruchom zadanie dostrajania hiperparametrów za pomocą pakietu SDK Vertex AI w języku Python.

Całkowity koszt uruchomienia tego modułu w Google Cloud wynosi około 1 USD.

2. Wprowadzenie do Vertex AI

W tym module wykorzystano najnowszą ofertę usług AI dostępną w Google Cloud. Vertex AI integruje ofertę systemów uczących się z całego Google Cloud, tworząc bezproblemowe środowisko programistyczne. Wcześniej modele wytrenowane z użyciem AutoML i modele niestandardowe były dostępne w oddzielnych usługach. Nowa oferta jest łączona w 1 interfejs API wraz z innymi nowymi usługami. Możesz też przenieść istniejące projekty do Vertex AI.

Vertex AI obejmuje wiele różnych usług, które obsługują kompleksowe przepływy pracy ML. W tym module skupimy się na wymienionych poniżej usługach: Szkolenia i Workbench.

Omówienie usługi Vertex

3. Konfigurowanie środowiska

Wykonaj czynności opisane w module Trenowanie modeli niestandardowych przy użyciu Vertex AI, aby skonfigurować środowisko.

4. Konteneryzowanie kodu aplikacji do trenowania

Prześlesz to zadanie treningowe do Vertex AI, umieszczając kod aplikacji treningowej w kontenerze Dockera i przenosząc ten kontener do Google Artifact Registry. Korzystając z tej metody, możesz wytrenować i dostroić model utworzony na podstawie dowolnej platformy.

Aby rozpocząć, w menu Menu z aplikacjami w notatniku Workbench utworzonym w poprzednich modułach otwórz okno terminala.

Otwórz terminal w notatniku

Krok 1. Napisz kod trenowania

Utwórz nowy katalog o nazwie flowers-hptune i znajdź do niego dysk CD:

mkdir flowers-hptune
cd flowers-hptune

Uruchom następujące polecenie, aby utworzyć katalog na kod trenowania i plik Pythona, w którym umieścisz poniższy kod.

mkdir trainer
touch trainer/task.py

W katalogu flowers-hptune/ powinny znajdować się teraz te elementy:

+ trainer/
    + task.py

Następnie otwórz utworzony przed chwilą plik task.py i skopiuj poniższy kod.

Musisz zastąpić zmienną {your-gcs-bucket} w elemencie BUCKET_ROOT zasobnikiem Cloud Storage, w którym w module 1 był przechowywany zbiór danych kwiatów.

import tensorflow as tf
import numpy as np
import os
import hypertune
import argparse

## Replace {your-gcs-bucket} !!
BUCKET_ROOT='/gcs/{your-gcs-bucket}'

# Define variables
NUM_CLASSES = 5
EPOCHS=10
BATCH_SIZE = 32

IMG_HEIGHT = 180
IMG_WIDTH = 180

DATA_DIR = f'{BUCKET_ROOT}/flower_photos'

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 create_datasets(data_dir, batch_size):
  '''Creates train and validation datasets.'''

  train_dataset = tf.keras.utils.image_dataset_from_directory(
    data_dir,
    validation_split=0.2,
    subset="training",
    seed=123,
    image_size=(IMG_HEIGHT, IMG_WIDTH),
    batch_size=batch_size)

  validation_dataset = tf.keras.utils.image_dataset_from_directory(
    data_dir,
    validation_split=0.2,
    subset="validation",
    seed=123,
    image_size=(IMG_HEIGHT, IMG_WIDTH),
    batch_size=batch_size)

  train_dataset = train_dataset.cache().shuffle(1000).prefetch(buffer_size=tf.data.AUTOTUNE)
  validation_dataset = validation_dataset.cache().prefetch(buffer_size=tf.data.AUTOTUNE)

  return train_dataset, validation_dataset


def create_model(num_units, learning_rate, momentum):
  '''Creates model.'''

  model = tf.keras.Sequential([
    tf.keras.layers.Resizing(IMG_HEIGHT, IMG_WIDTH),
    tf.keras.layers.Rescaling(1./255, input_shape=(IMG_HEIGHT, IMG_WIDTH, 3)),
    tf.keras.layers.Conv2D(16, 3, padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(32, 3, padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(num_units, activation='relu'),
    tf.keras.layers.Dense(NUM_CLASSES, activation='softmax')
  ])

  model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=learning_rate, momentum=momentum),
              loss=tf.keras.losses.SparseCategoricalCrossentropy(),
              metrics=['accuracy'])
  
  return model

def main():
  args = get_args()
  train_dataset, validation_dataset = create_datasets(DATA_DIR, BATCH_SIZE)
  model = create_model(args.num_units, args.learning_rate, args.momentum)
  history = model.fit(train_dataset, validation_data=validation_dataset, epochs=EPOCHS)

  # 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=EPOCHS)


if __name__ == "__main__":
    main()

Zanim utworzysz kontener, przyjrzyjmy się kodowi. Usługa dostrajania hiperparametrów ma kilka charakterystycznych elementów.

  1. Skrypt importuje bibliotekę hypertune.
  2. Funkcja get_args() definiuje argument wiersza poleceń dla każdego hiperparametru, który chcesz dostroić. W tym przykładzie dostrojone hiperparametry to szybkość uczenia się, wartość pędu w optymalizatorze i liczba jednostek w ostatniej ukrytej warstwie modelu. Możesz też eksperymentować z innymi. Wartość przekazywana w tych argumentach jest następnie używana do ustawienia odpowiedniego hiperparametru w kodzie.
  3. Na końcu funkcji main() biblioteka hypertune służy do określenia danych, które chcesz optymalizować. W TensorFlow metoda model.fit Kera zwraca obiekt History. Atrybut History.history zawiera zapis wartości strat trenowania i wartości wskaźników w kolejnych epokach. Jeśli przekażesz dane weryfikacyjne do model.fit, atrybut History.history będzie też zawierać wartości utracone podczas weryfikacji i wskaźniki. Jeśli na przykład wytrenujesz model z 3 epokami z danymi do weryfikacji i podasz accuracy jako wskaźnik, atrybut History.history będzie wyglądać podobnie do tego słownika.
{
 "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
 ]

Jeśli chcesz, aby usługa dostrajania hiperparametrów wykrywała wartości maksymalizujące dokładność walidacji modelu, zdefiniuj wskaźnik jako ostatnią pozycję (lub NUM_EPOCS - 1) na liście val_accuracy. Następnie przekaż ten wskaźnik do instancji HyperTune. Możesz wybrać dowolny ciąg dla hyperparameter_metric_tag, ale trzeba będzie użyć go później, gdy uruchomisz zadanie dostrajania hiperparametrów.

Krok 2. Utwórz plik Dockerfile

Aby skonteneryzować kod, musisz utworzyć plik Dockerfile. W pliku Dockerfile znajdziesz wszystkie polecenia potrzebne do uruchomienia obrazu. Zainstaluje wszystkie niezbędne biblioteki i skonfiguruje punkt wejścia dla kodu trenowania.

W terminalu utwórz pusty plik Dockerfile w katalogu flowers-hptune:

touch Dockerfile

W katalogu flowers-hptune/ powinny znajdować się teraz te elementy:

+ Dockerfile
+ trainer/
    + task.py

Otwórz plik Dockerfile i skopiuj do niego poniższy plik. Zauważysz, że jest on prawie taki sam jak plik Dockerfile, którego użyto w pierwszym module, ale teraz instalujemy bibliotekę cloudml-hypertune.

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

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

Krok 3. Utwórz kontener

W terminalu uruchom to polecenie, aby zdefiniować zmienną env dla swojego projektu, pamiętając o zastąpieniu your-cloud-project identyfikatorem projektu:

PROJECT_ID='your-cloud-project'

zdefiniować repozytorium w Artifact Registry. Użyjemy repozytorium utworzonego w pierwszym module.

REPO_NAME='flower-app'

Zdefiniuj zmienną za pomocą identyfikatora URI obrazu kontenera w Google Artifact Registry:

IMAGE_URI=us-central1-docker.pkg.dev/$PROJECT_ID/$REPO_NAME/flower_image_hptune:latest

Skonfiguruj Dockera

gcloud auth configure-docker \
    us-central1-docker.pkg.dev

Następnie utwórz kontener, uruchamiając to polecenie z poziomu głównego katalogu flower-hptune:

docker build ./ -t $IMAGE_URI

Na koniec przekaż go do Artifact Registry:

docker push $IMAGE_URI

Po przeniesieniu kontenera do Artifact Registry możesz rozpocząć zadanie trenowania.

5. Uruchamianie zadania dostrajania hiperparametrów za pomocą pakietu SDK

W tej sekcji dowiesz się, jak skonfigurować i przesłać zadanie dostrajania hiperparametrów za pomocą interfejsu Vertex Python API.

W Menu z aplikacjami utwórz notatnik TensorFlow 2.

new_notebook

Zaimportuj pakiet Vertex AI SDK.

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

Aby uruchomić zadanie dostrajania hiperparametrów, musisz najpierw zdefiniować element worker_pool_specs, który określa typ maszyny i obraz Dockera. Poniższa specyfikacja definiuje jedną maszynę z 2 procesorami graficznymi NVIDIA Tesla V100.

Musisz zastąpić zmienną {PROJECT_ID} w image_uri swoim projektem.

# 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": "us-central1-docker.pkg.dev/{PROJECT_ID}/flower-app/flower_image_hptune:latest"
    }
}]

Następnie zdefiniuj parameter_spec, czyli słownik określający parametry, które chcesz zoptymalizować. Klucz słownika to ciąg znaków przypisany do argumentu wiersza poleceń dla każdego hiperparametru, a wartość słownika to specyfikacja parametru.

W przypadku każdego hiperparametru musisz zdefiniować typ oraz granice wartości, które będzie próbować dostrajać usługa dostrajania. Hiperparametry mogą być typu liczba całkowita, liczba całkowita, kategorialne lub dyskretne. Jeśli wybierzesz typ Liczba podwójna lub Liczba całkowita, musisz podać wartość minimalną i maksymalną. Jeśli wybierzesz Kategorialne lub Dyskretne, musisz podać wartości. W przypadku typów Podwójna liczba i Liczba całkowita musisz też podać wartość Skalowanie. Więcej informacji o wyborze najlepszej wagi znajdziesz w tym filmie.

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

Końcowa specyfikacja do zdefiniowania to metric_spec, czyli słownik reprezentujący wskaźnik do optymalizacji. Kluczem słownika jest identyfikator hyperparameter_metric_tag ustawiony w kodzie aplikacji treningowej, a wartością jest cel optymalizacji.

# Dictionary representing metric 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'}

Po zdefiniowaniu specyfikacji utworzysz zadanie niestandardowe, które jest wspólną specyfikacją, która będzie używana do uruchamiania zadania w ramach każdej próby dostrajania hiperparametrów.

Musisz zastąpić zmienną {YOUR_BUCKET} zasobnikiem utworzonym wcześniej.

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

Następnie utwórz i uruchom HyperparameterTuningJob.

hp_job = aiplatform.HyperparameterTuningJob(
    display_name='flowers-hptune-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()

Pamiętaj o kilku argumentach:

  • max_trial_count: musisz wyznaczyć górną granicę liczby wersji próbnych, w których usługa będzie działać. Większa liczba prób oznacza zwykle lepsze wyniki, ale występuje wtedy spadek zysków, po których dodatkowe testy mają znikomy wpływ na dane, które próbujesz zoptymalizować, lub nie mają ich wcale. Przed skalowaniem w górę najlepiej jest rozpocząć od mniejszej liczby prób i poznać wpływ wybranych hiperparametrów.
  • parallel_trial_count: jeśli korzystasz z równoległych prób, usługa udostępnia wiele klastrów przetwarzania trenowania. Zwiększenie liczby równoległych prób pozwala skrócić czas wykonywania zadania dostrajania hiperparametrów; ale może też zmniejszyć ogólną skuteczność zadania. Dzieje się tak, ponieważ domyślna strategia dostrajania wykorzystuje wyniki poprzednich prób do przypisania wartości w kolejnych próbach.
  • search_algorithm: możesz ustawić algorytm wyszukiwania jako siatka, losowe lub domyślne (Brak). Opcja domyślna stosuje optymalizację Bayesa do wyszukiwania przestrzeni możliwych wartości hiperparametrów i jest zalecanym algorytmem. Więcej informacji o tym algorytmie znajdziesz tutaj.

W konsoli możesz zobaczyć postęp zadania.

hp_job

Po zakończeniu każdej z nich możesz sprawdzić wyniki i zestaw wartości o najlepszej skuteczności.

hp_results

🎉 Gratulacje! 🎉

Wiesz już, jak używać Vertex AI do:

  • Uruchamianie automatycznego dostrajania hiperparametrów

Więcej informacji o różnych częściach Vertex znajdziesz w dokumentacji.

6. Czyszczenie

Skonfigurowaliśmy notatnik tak, aby przekraczał limit czasu po 60 minutach bezczynności, więc nie musimy się martwić wyłączeniem instancji. Jeśli chcesz ręcznie wyłączyć instancję, kliknij przycisk Zatrzymaj w sekcji Vertex AI Workbench w konsoli. Jeśli chcesz całkowicie usunąć notatnik, kliknij przycisk Usuń.

Zatrzymaj instancję

Aby usunąć zasobnik na dane, w menu nawigacyjnym w konsoli Google Cloud otwórz Cloud Storage, wybierz swój zasobnik i kliknij Usuń:

Usuń miejsce na dane