Vertex AI: rozproszone dostrajanie hiperparametrów

1. Omówienie

W tym module dowiesz się, jak używać Vertex AI do dostrajania parametrów i rozproszonego trenowania. W tym laboratorium do kodu modelu używany jest TensorFlow, ale omawiane tu zagadnienia można zastosować również do innych platform ML.

Czego się nauczysz

Poznasz takie zagadnienia jak:

  • Trenowanie modelu przy użyciu trenowania rozproszonego w kontenerze niestandardowym
  • Uruchom kilka prób treningu kodu w celu automatycznego dostrajania hiperparametrów

Łączny koszt przeprowadzenia tego laboratorium w Google Cloud wynosi około 6 USD.

2. Wprowadzenie do Vertex AI

Ten moduł wykorzystuje 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 za pomocą AutoML i modele niestandardowe były dostępne za pomocą oddzielnych usług. Nowa oferta łączy oba te interfejsy API z innymi nowymi usługami. Możesz też przenieść istniejące projekty do Vertex AI. Jeśli chcesz podzielić się opinią, odwiedź stronę pomocy.

Vertex AI zawiera wiele różnych usług, które obsługują kompleksowe przepływy pracy związane z systemami uczącymi się. W tym module omówimy trenowanieWorkbench.

Omówienie usługi Vertex

3. Omówienie przypadku użycia

W tym module użyjesz dostrajania hiperparametrów, aby znaleźć optymalne parametry modelu klasyfikacji obrazów wytrenowanego na zbiorze danych horses or humanszbiorów danych TensorFlow.

Dostrajanie hiperparametrów

Dostosowywanie hiperparametrów za pomocą funkcji Vertex AI Training polega na uruchamianiu wielu prób aplikacji treningowej z wartościami wybranych hiperparametrów ustawionymi w określonych przez Ciebie granicach. Vertex AI śledzi wyniki każdej próby i wprowadza korekty w przypadku kolejnych prób.

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

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

Trenowanie rozproszone

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ć dodatkową wydajność dzięki użyciu wielu procesorów graficznych, musisz użyć tf.distribute, czyli modułu TensorFlow do wykonywania obliczeń na wielu urządzeniach.

W tym laboratorium używamy tf.distribute.MirroredStrategy, który można dodać do aplikacji do trenowania, wprowadzając tylko kilka zmian w kodzie. Ta strategia tworzy kopię modelu na każdym GPU na komputerze. Kolejne aktualizacje gradientu będą przeprowadzane 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 kawałków są następnie agregowane na wszystkich procesorach graficznych i uśredniane w ramach procesu zwanego all-reduce. Parametry modelu są aktualizowane za pomocą tych średnich 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 ćwiczenie, musisz mieć projekt Google Cloud Platform z włączonym rozliczeniem. Aby utworzyć projekt, wykonaj te instrukcje.

Krok 1. Włącz Compute Engine API

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

Krok 2. Włącz interfejs 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 interfejs Notebooks API, jeśli nie jest jeszcze włączony.

Notebook_api

Po włączeniu tej opcji 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 funkcję wyłączania 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łączony.

włącz-terminal

Pozostałe ustawienia zaawansowane możesz pozostawić bez zmian.

Następnie kliknij Utwórz. Przygotowanie instancji może potrwać kilka minut.

Po utworzeniu instancji kliknij Otwórz JupyterLab.

open_jupyterlab

Gdy po raz pierwszy użyjesz nowego wystąpienia, zostaniesz poproszony o uwierzytelnienie. W tym celu postępuj zgodnie z instrukcjami w interfejsie.

uwierzytelnij

5. Pisanie kodu trenowania

Aby rozpocząć, w menu Launcher otwórz okno Terminala w instancji notatek:

launcher_terminal

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

mkdir vertex-codelab
cd vertex-codelab

Aby utworzyć katalog dla kodu szkoleniowego i plik Pythona, w którym dodasz kod, wykonaj te czynności:

mkdir trainer
touch trainer/task.py

W katalogu vertex-codelab powinien teraz znajdować się ten plik:

+ trainer/
    + task.py

Następnie otwórz utworzony plik task.py i wklej cały kod podany poniżej.

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 kodom i składnikom związanym z rozproszonym trenowaniem i dostrajaniem hiperparametrów.

Trenowanie rozproszone

  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ć lustrzanymi odbiciami na kartach GPU.
  2. Wielkość wsadu jest zwiększana 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 znajdziesz tutaj

Dostrajanie hiperparametrów

  1. Skrypt importuje bibliotekę hypertune. Podczas kompilowania obrazu kontenera musimy się upewnić, że ta biblioteka została zainstalowana.
  2. Funkcja get_args() definiuje argument wiersza poleceń dla każdego hiperparametru, który chcesz dostroić. W tym przykładzie dostrajane są hiperparametry: tempo uczenia się, wartość momentum w optymalizatorze i liczba jednostek w ostatniej warstwie ukrytej modelu, ale możesz też eksperymentować z innymi. Wartość przekazana w tych argumentach jest następnie używana do ustawiania odpowiedniego hiperparametru w kodzie (np. set learning_rate = args.learning_rate).
  3. Na końcu funkcji main() biblioteka hypertune służy do definiowania danych, które chcesz optymalizować. W TensorFlow metoda Keras model.fit zwraca obiekt History. Atrybut History.history to rekord wartości utraty i wartości danych 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 wartości wskaźników. Jeśli na przykład trenujesz model przez 3 epoki z danymi walidacyjnymi i podajesz jako dane accuracy, 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 znajdowała wartości maksymalizujące dokładność walidacji modelu, zdefiniuj metrykę jako ostatni wpis (lub NUM_EPOCS - 1) na liście val_accuracy. Następnie prześlij te dane do wystąpienia elementu HyperTune. W polu hyperparameter_metric_tag możesz wpisać dowolny ciąg tekstowy, ale później, gdy uruchomisz zadanie dostrajania hiperparametrów, będziesz musiał(-a) użyć tego samego ciągu.

6. Konteneryzowanie kodu

Pierwszym krokiem w konteneryzacji kodu jest utworzenie pliku Dockerfile. W pliku Dockerfile musisz uwzględnić 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 powinien teraz znajdować się ten plik:

+ Dockerfile
+ trainer/
    + task.py

Otwórz plik Dockerfile i wklej do niego ten kod:

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 używa obrazu Dockera TensorFlow Enterprise 2.7 na potrzeby głębokiego uczenia się na GPU. Kontenery do głębokiego uczenia się w Google Cloud są dostarczane z wstępnie zainstalowanymi popularnymi platformami do tworzenia systemów uczących się i narzędzi do analizy 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ą środowiskową dla projektu. Pamiętaj, aby zastąpić your-cloud-project identyfikatorem projektu:

PROJECT_ID='your-cloud-project'

Zdefiniuj zmienną z identyfikatorem 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 w katalogu głównym vertex-codelab:

docker build ./ -t $IMAGE_URI

Na koniec prześlij go do Google Container Registry:

docker push $IMAGE_URI

Krok 3. Utwórz zasobnik Cloud Storage

W naszym zadaniu treningowym podamy ścieżkę do zasobnika stagingowego.

Aby utworzyć nowy zasobnik w projekcie, 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 treningowe z dostrajaniem hiperparametrów

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

new_notebook

Zaimportuj pakiet Vertex AI SDK dla Pythona.

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 element parameter_spec, który jest słownikiem określającym parametry, które chcesz optymalizować. Klucz słownika to ciąg tekstowy przypisany do argumentu wiersza poleceń dla każdego hiperparametru, a wartość słownika to specyfikacja parametru.

W przypadku każdego parametru musisz zdefiniować typ oraz zakresy wartości, które będzie testować usługa do strojenia. Parametry hiperboliczne mogą być typu Double, Integer, Categorical lub Discrete. Jeśli wybierzesz typ podwójny lub całkowity, musisz podać minimalną i maksymalną wartość. Jeśli wybierzesz opcję Kategoryczna lub Rozłączna, musisz podać wartości. W przypadku typów Double i Integer musisz też podać wartość Scaling. 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)
}

Ostatnią specyfikacją do zdefiniowania jest metric_spec, czyli słownik reprezentujący dane, które mają zostać zoptymalizowane. Kluczem słownika jest hyperparameter_metric_tag ustawiony w kodzie aplikacji do trenowania, 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.

Zastąp {YOUR_BUCKET} utworzony wcześniej zasobnik.

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

Oto kilka argumentów:

  • max_trial_count: musisz wyznaczyć górną granicę liczby wersji próbnych, w których usługa będzie działać. W ogóle im więcej prób, tym lepsze wyniki, ale w pewnym momencie następuje spadek skuteczności, po którym kolejne próby nie mają już wpływu na dane, które próbujesz zoptymalizować. Najlepiej zacząć od mniejszej liczby prób i przed zwiększeniem skali sprawdzić, jak duży wpływ mają wybrane hiperparametry.
  • parallel_trial_count: jeśli używasz równoległych prób, usługa udostępnia wiele klastrów przetwarzania treningu. Zwiększenie liczby równoległych prób skraca czas działania zadania dostrajania hiperparametrów, ale może zmniejszyć ogólną skuteczność zadania. Dzieje się tak, ponieważ domyślna strategia dostrajania korzysta z wyników poprzednich prób do przypisywania wartości w kolejnych próbach.
  • search_algorithm: algorytm wyszukiwania możesz ustawić jako siatkowy, losowy lub domyślny (brak). Domyślna opcja stosuje optymalizację bayesowską do wyszukiwania przestrzeni możliwych wartości hiperparametrów i jest zalecanym algorytmem. Więcej informacji o tym algorytmie znajdziesz tutaj.

Po rozpoczęciu zadania możesz śledzić jego stan w interfejsie na karcie ZADANIE DOSTRAJANIA HIPERPARAMETRÓW.

HP_job

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

HP_results

🎉 Gratulacje! 🎉

Poznałeś/poznałaś już te sposoby korzystania z Vertex AI:

  • Uruchamianie zadania dostrajania hiperparametrów z wykorzystaniem rozproszonego treningu

Więcej informacji o różnych elementach Vertex AI znajdziesz w dokumentacji.

8. Czyszczenie

Notebook został skonfigurowany tak, aby wyłączał się po 60 minutach bezczynności, więc nie musisz się martwić zamykaniem instancji. Jeśli chcesz ręcznie wyłączyć instancję, w konsoli Vertex AI Workbench kliknij przycisk Zatrzymaj. Jeśli chcesz całkowicie usunąć zeszyt, kliknij przycisk Usuń.

usuń

Aby usunąć zasobnik Cloud Storage, w menu nawigacyjnym konsoli Cloud przejdź do usługi Storage, wybierz zasobnik i kliknij Usuń:

Usuwanie miejsca na dane