Vertex AI: rozproszone dostrajanie hiperparametrów

1. Omówienie

W tym module dowiesz się, jak używać Vertex AI do dostrajania hiperparametrów i trenowania rozproszonego. Chociaż w tym module do tworzenia kodu modelu używany jest TensorFlow, podane koncepcje mają też zastosowanie do innych platform ML.

Czego się nauczysz

Poznasz takie zagadnienia jak:

  • Wytrenuj model za pomocą trenowania rozproszonego na niestandardowym kontenerze
  • Przeprowadzaj wiele prób kodu trenowania na potrzeby automatycznego dostrajania hiperparametrów

Całkowity koszt uruchomienia tego modułu w Google Cloud wynosi około 6 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. Jeśli masz jakieś uwagi, odwiedź stronę pomocy.

Vertex AI obejmuje wiele różnych usług, które obsługują kompleksowe przepływy pracy ML. Głównym tematem tego modułu są Szkolenia i Workbench.

Omówienie usługi Vertex

3. Omówienie przypadku użycia

W tym module wykorzystasz dostrajanie hiperparametrów, aby odkryć optymalne parametry dla modelu klasyfikacji obrazów wytrenowanego na zbiorze danych „konie lub ludzie” ze zbiorów danych TensorFlow.

Dostrajanie hiperparametrów

Dostrajanie hiperparametrów za pomocą Vertex AI Training polega na uruchomieniu wielu prób aplikacji treningowej z wartościami dla wybranych hiperparametrów, ustawionymi w ramach określonych przez Ciebie limitów. Vertex AI śledzi wyniki każdej próby i wprowadza korekty w kolejnych.

Aby korzystać z dostrajania hiperparametrów w Vertex AI Training, musisz wprowadzić 2 zmiany w kodzie trenowania:

  1. Zdefiniuj argument wiersza poleceń w głównym module treningowym dla każdego hiperparametru, który chcesz dostroić.
  2. Użyj wartości przekazywanej w tych argumentach, aby ustawić odpowiedni hiperparametr w kodzie aplikacji.

Zdzielone szkolenie

Jeśli masz pojedynczy procesor graficzny, TensorFlow użyje tego akceleratora, aby przyspieszyć trenowanie modelu bez dodatkowych działań z Twojej strony. Jeśli jednak chcesz uzyskać dodatkowe korzyści dzięki używaniu kilku procesorów graficznych, musisz użyć interfejsu tf.distribute, który jest modułem TensorFlow do przeprowadzania obliczeń na wielu urządzeniach.

W tym module używane są narzędzia tf.distribute.MirroredStrategy, które możesz dodać do aplikacji treningowych, wprowadzając kilka zmian w kodzie. Ta strategia tworzy kopię modelu w każdym GPU na Twoim komputerze. Kolejne aktualizacje gradientu będą odbywać się synchronicznie. Oznacza to, że każdy procesor graficzny oblicza ruch do przodu i do tyłu przez model na innym wycinku danych wejściowych. Obliczone gradienty z każdego z tych wycinków są następnie zagregowane dla wszystkich układów GPU i uśredniane w procesie znanym jako all-reduce. Parametry modelu są aktualizowane za pomocą tych uśrednionych gradientów.

Nie musisz znać szczegółów, aby ukończyć ten moduł, ale jeśli chcesz dowiedzieć się więcej o tym, jak działa rozproszone trenowanie w TensorFlow, obejrzyj film poniżej:

4. Konfigurowanie środowiska

Aby uruchomić to ćwiczenia z programowania, musisz mieć projekt Google Cloud Platform z włączonymi płatnościami. Aby utworzyć projekt, postępuj zgodnie z tymi instrukcjami.

Krok 1. Włącz Compute Engine API

Przejdź do Compute Engine i wybierz opcję Włącz, jeśli nie jest jeszcze włączona.

Krok 2. Włącz Container Registry API

Otwórz Container Registry i wybierz Włącz, jeśli nie jest jeszcze włączony. Użyjesz go do utworzenia kontenera dla niestandardowego zadania trenowania.

Krok 3. Włącz interfejs Vertex AI API

Przejdź do sekcji Vertex AI w konsoli Cloud i kliknij Włącz interfejs Vertex AI API.

Panel Vertex AI

Krok 4. Utwórz instancję Vertex AI Workbench

W sekcji Vertex AI w konsoli Cloud kliknij Workbench:

Menu Vertex AI

Włącz Notebooks API, jeśli nie jest jeszcze włączone.

Notebook_api

Po włączeniu kliknij ZARZĄDZANE NOTATKI:

Notebooks_UI

Następnie wybierz NOWY NOTATNIK.

new_notebook

Nadaj notatnikowi nazwę i kliknij Ustawienia zaawansowane.

create_notebook

W sekcji Ustawienia zaawansowane włącz wyłączanie w trybie bezczynności i ustaw liczbę minut na 60. Oznacza to, że Twój notatnik automatycznie wyłączy się, gdy nie będzie używany, aby nie ponosić niepotrzebnych kosztów.

idle_timeout

W sekcji Zabezpieczenia wybierz „Włącz terminal” , jeśli nie jest jeszcze włączona.

włącz-terminal

Inne ustawienia zaawansowane możesz pozostawić bez zmian.

Następnie kliknij Utwórz. Udostępnienie instancji zajmie kilka minut.

Po utworzeniu instancji wybierz Otwórz JupyterLab.

open_jupyterlab

Przy pierwszym użyciu nowej instancji zobaczysz prośbę o uwierzytelnienie. W tym celu postępuj zgodnie z instrukcjami w interfejsie.

uwierzytelnij

5. Pisanie kodu trenowania

Aby rozpocząć, w menu Menu z aplikacjami otwórz okno terminala w instancji notatnika:

launcher_terminal

Utwórz nowy katalog o nazwie vertex-codelab i umieść w nim dysk CD.

mkdir vertex-codelab
cd vertex-codelab

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

mkdir trainer
touch trainer/task.py

W katalogu vertex-codelab powinny znajdować się teraz te elementy:

+ trainer/
    + task.py

Następnie otwórz utworzony przed chwilą plik task.py i wklej cały poniższy kod.

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

Przyjrzyjmy się bliżej kodowi i składnikom specyficznym dla rozproszonego trenowania i dostrajania hiperparametrów.

Zdzielone szkolenie

  1. W funkcji main() tworzony jest obiekt MirroredStrategy. Następnie dodajesz zmienne modelu do zakresu strategii. Ten krok informuje TensorFlow, które zmienne powinny być powielone w GPU.
  2. Wielkość wsadu jest skalowana w górę o num_replicas_in_sync. Skalowanie rozmiaru wsadu jest sprawdzoną metodą w przypadku korzystania ze strategii synchronicznego równoległości danych w TensorFlow. Więcej informacji możesz uzyskać tutaj.

Dostrajanie hiperparametrów

  1. Skrypt importuje bibliotekę hypertune. Później podczas tworzenia obrazu kontenera musimy zainstalować tę bibliotekę.
  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 (np. ustaw learning_rate = args.learning_rate)
  3. Na końcu funkcji main() biblioteka hypertune służy do określenia danych, które chcesz optymalizować. W TensorFlow metoda Keras model.fit 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.

6. Konteneryzowanie kodu

Pierwszym krokiem w konteneryzacji kodu jest utworzenie pliku Dockerfile. W pliku Dockerfile znajdziesz wszystkie polecenia potrzebne do uruchomienia obrazu. Zainstaluje wszystkie niezbędne biblioteki i skonfiguruje punkt wejścia dla kodu trenowania.

Krok 1. Zapisz plik Dockerfile

W terminalu sprawdź, czy jesteś w katalogu vertex-codelab i utwórz pusty plik Dockerfile:

touch Dockerfile

W katalogu vertex-codelab powinny znajdować się teraz te elementy:

+ Dockerfile
+ trainer/
    + task.py

Otwórz plik Dockerfile i skopiuj do niego poniższy plik:

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

Ten plik Dockerfile wykorzystuje obraz Dockera GPU TensorFlow Enterprise 2.7. Kontenery do deep learningu w Google Cloud mają wstępnie zainstalowane wiele popularnych platform do uczenia maszynowego i platformy do badania danych. Po pobraniu tego obrazu plik Dockerfile konfiguruje punkt wejścia dla kodu trenowania.

Krok 2. 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'

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

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

Konfigurowanie Dockera

gcloud auth configure-docker

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

docker build ./ -t $IMAGE_URI

Na koniec wypchnij go do Google Container Registry:

docker push $IMAGE_URI

Krok 3. Utwórz zasobnik Cloud Storage

W zadaniu trenowania przekażemy ścieżkę do zasobnika przejściowego.

Aby utworzyć w projekcie nowy zasobnik, uruchom w terminalu to polecenie.

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

7. Uruchom zadanie dostrajania hiperparametrów

Krok 1. Utwórz niestandardowe zadanie trenowania z dostrajaniem hiperparametrów

W programie uruchamiającym otwórz nowy notatnik TensorFlow 2.

new_notebook

Zaimportuj pakiet Vertex AI Python 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": 2
    },
    "replica_count": 1,
    "container_spec": {
        "image_uri": "gcr.io/{PROJECT_ID}/horse-human-codelab: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 wybieraniu najlepszej skali 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.

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

Po zdefiniowaniu specyfikacji utworzysz CustomJob, czyli typową 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='horses-humans',
                              worker_pool_specs=worker_pool_specs,
                              staging_bucket='gs://{YOUR_BUCKET}')

Następnie utwórz i uruchom HyperparameterTuningJob.

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

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.
  • algorytm_wyszukiwania: możesz ustawić algorytm wyszukiwania na siatkę, losowo lub domyślnie (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.

Po uruchomieniu zadania jego stan będzie można śledzić na karcie ZADANIA DOstrajania SAMOCHODU w interfejsie.

HP_job

Po zakończeniu zadania możesz wyświetlić i posortować wyniki prób, aby odkryć najlepszą kombinację wartości hiperparametrów.

HP_results

🎉 Gratulacje! 🎉

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

  • Uruchamianie zadania dostrajania hiperparametrów z trenowaniem rozproszonym

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

8. 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ń.

usuń

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