Vertex AI: trenowanie wielu instancji roboczych i nauczanie transferowe z wykorzystaniem TensorFlow

1. Przegląd

W tym module wykorzystasz Vertex AI do uruchomienia zadania trenowania modelu TensorFlow z wieloma procesami roboczymi.

Czego się dowiesz

Poznasz takie zagadnienia jak:

  • Modyfikowanie kodu aplikacji do trenowania na potrzeby trenowania z wieloma instancjami roboczymi
  • Konfigurowanie i uruchamianie zadania trenowania z wieloma procesami roboczymi w interfejsie Vertex AI
  • Konfigurowanie i uruchamianie zadania trenowania z wieloma pracownikami za pomocą pakietu Vertex SDK

Całkowity koszt ukończenia tego modułu w Google Cloud wynosi około 5 USD.

2. Wprowadzenie do Vertex AI

W tym module wykorzystujemy najnowszą ofertę produktów AI dostępną w Google Cloud. Vertex AI integruje usługi ML w Google Cloud, zapewniając płynne środowisko programistyczne. Wcześniej modele wytrenowane za pomocą AutoML i modele niestandardowe były dostępne w ramach osobnych usług. Nowa oferta łączy je w jeden interfejs API wraz z innymi nowymi usługami. Możesz też przeprowadzić migrację istniejących projektów 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 związane z uczeniem maszynowym. Ten moduł skupia się na wyróżnionych poniżej usługach: trenowanieWorkbench.

Omówienie usługi Vertex

3. Omówienie przypadku użycia

W tym module użyjesz uczenia przez przeniesienie, aby wytrenować model klasyfikacji obrazów na podstawie zbioru danych o maniokuTensorFlow Datasets. Używana architektura to model ResNet50 z biblioteki tf.keras.applications wstępnie wytrenowany na zbiorze danych ImageNet.

Dlaczego warto korzystać z trenowania rozproszonego?

Jeśli masz pojedynczy procesor GPU, TensorFlow użyje tego akceleratora, aby przyspieszyć trenowanie modelu bez dodatkowej pracy z Twojej strony. Jeśli jednak chcesz uzyskać dodatkowe korzyści z używania wielu procesorów graficznych na jednym komputerze lub wielu komputerach (z których każdy może mieć wiele procesorów graficznych), musisz użyć tf.distribute, czyli biblioteki TensorFlow do przeprowadzania obliczeń na wielu urządzeniach. Urządzenie to procesor lub akcelerator, np. GPU lub TPU, na komputerze, na którym TensorFlow może wykonywać operacje.

Najprostszym sposobem na rozpoczęcie trenowania rozproszonego jest użycie jednego urządzenia z wieloma procesorami graficznymi. Strategia dystrybucji TensorFlow z modułu tf.distribute będzie zarządzać koordynacją dystrybucji danych i aktualizacji gradientów na wszystkich procesorach GPU. Jeśli opanujesz trenowanie na jednym hoście i chcesz jeszcze bardziej zwiększyć skalę, dodanie do klastra większej liczby maszyn może pomóc Ci jeszcze bardziej zwiększyć wydajność. Możesz używać klastra maszyn, które mają tylko procesory lub które mają co najmniej 1 procesor graficzny. Ten moduł dotyczy tego drugiego przypadku i pokazuje, jak używać MultiWorkerMirroredStrategy do rozdzielania trenowania modelu TensorFlow na wiele maszyn w Vertex AI.

MultiWorkerMirroredStrategy to synchroniczna strategia równoległości danych, której możesz używać po wprowadzeniu tylko kilku zmian w kodzie. Na każdym urządzeniu w klastrze tworzona jest kopia modelu. Kolejne aktualizacje gradientu będą przeprowadzane synchronicznie. Oznacza to, że każde urządzenie pracownika oblicza przejścia w przód i w tył przez model na innym wycinku danych wejściowych. Obliczone gradienty z każdego z tych wycinków są następnie agregowane na wszystkich urządzeniach na maszynie i na wszystkich maszynach w klastrze oraz redukowane (zwykle do średniej) w procesie zwanym all-reduce. Następnie optymalizator aktualizuje parametry za pomocą tych zmniejszonych gradientów, dzięki czemu urządzenia pozostają zsynchronizowane. Aby dowiedzieć się więcej o trenowaniu rozproszonym za pomocą TensorFlow, obejrzyj film poniżej:

4. Konfigurowanie środowiska

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

Krok 1. Włącz interfejs Compute Engine API

Przejdź do Compute Engine i kliknij Włącz, jeśli nie jest jeszcze włączona. Będzie Ci potrzebny do utworzenia instancji notatnika.

Krok 2. Włącz interfejs Container Registry API

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

Krok 3. Włącz interfejs Vertex AI API

Otwórz sekcję Vertex AI w konsoli Cloud i kliknij Włącz interfejs Vertex AI API.

Panel Vertex AI

Krok 4. Tworzenie instancji 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 kliknij ZARZĄDZANE NOTATNIKI:

Notebooks_UI

Następnie wybierz NOWY NOTEBOOK.

new_notebook

Nadaj notatnikowi nazwę, a potem kliknij Ustawienia zaawansowane.

create_notebook

W sekcji Ustawienia zaawansowane włącz wyłączanie w przypadku bezczynności i ustaw liczbę minut na 60. Oznacza to, że Twój notebook będzie się automatycznie wyłączać, gdy nie jest używany, dzięki czemu nie poniesiesz niepotrzebnych kosztów.

idle_timeout

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

enable_terminal

Wszystkie pozostałe 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 pojawi się prośba o uwierzytelnienie. Aby to zrobić, postępuj zgodnie z instrukcjami wyświetlanymi w interfejsie.

uwierzytelnij

5. Konteneryzowanie kodu aplikacji trenującej

Zadanie trenowania przesyłasz do Vertex AI, umieszczając kod aplikacji do trenowania w kontenerze Dockera i przesyłając ten kontener do Google Container Registry. Dzięki temu możesz wytrenować model utworzony w dowolnym frameworku.

Aby rozpocząć, w menu Launchera otwórz okno terminala w instancji notatnika:

Otwórz terminal w notatniku

Utwórz nowy katalog o nazwie cassava i przejdź do niego:

mkdir cassava
cd cassava

Krok 1. Utwórz plik Dockerfile

Pierwszym krokiem w procesie kontenerowania kodu jest utworzenie pliku Dockerfile. W pliku Dockerfile umieścisz 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:

touch Dockerfile

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

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

WORKDIR /

# 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 kontenera Deep Learning Container TensorFlow Enterprise 2.7 z GPU. Kontenery do deep learningu w Google Cloud mają fabrycznie zainstalowanych wiele popularnych platform ML i narzędzi do analizy danych. Po pobraniu tego obrazu ten plik Dockerfile konfiguruje punkt wejścia dla kodu trenującego. Nie masz jeszcze tych plików. W następnym kroku dodasz kod do trenowania i dostrajania modelu.

Krok 2. Utwórz zasobnik Cloud Storage

W tym zadaniu treningowym wyeksportujesz wytrenowany model TensorFlow do zasobnika Cloud Storage. 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'

Następnie uruchom w terminalu to polecenie, aby utworzyć nowy zasobnik w projekcie.

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

Krok 3. Dodaj kod trenowania modelu

W terminalu uruchom to polecenie, aby utworzyć katalog z kodem szkoleniowym i plik Pythona, do którego dodasz kod:

mkdir trainer
touch trainer/task.py

W katalogu cassava/ powinny się teraz znajdować te pliki:

+ Dockerfile
+ trainer/
    + task.py

Następnie otwórz utworzony plik task.py i skopiuj poniższy kod. Musisz zastąpić {your-gcs-bucket} nazwą utworzonego zasobnika Cloud Storage.

import tensorflow as tf
import tensorflow_datasets as tfds
import os


PER_REPLICA_BATCH_SIZE = 64
EPOCHS = 2

# TODO: replace {your-gcs-bucket} with the name of the Storage bucket you created earlier
BUCKET = 'gs://{your-gcs-bucket}/mwms'

def preprocess_data(image, label):
  '''Resizes and scales images.'''

  image = tf.image.resize(image, (300,300))
  return tf.cast(image, tf.float32) / 255., label


def create_dataset(batch_size):
  '''Loads Cassava dataset and preprocesses data.'''

  data, info = tfds.load(name='cassava', as_supervised=True, with_info=True)
  number_of_classes = info.features['label'].num_classes
  train_data = data['train'].map(preprocess_data,
                                 num_parallel_calls=tf.data.experimental.AUTOTUNE)
  train_data  = train_data.shuffle(1000)
  train_data  = train_data.batch(batch_size)
  train_data  = train_data.prefetch(tf.data.experimental.AUTOTUNE)

  # Set AutoShardPolicy
  options = tf.data.Options()
  options.experimental_distribute.auto_shard_policy = tf.data.experimental.AutoShardPolicy.DATA
  train_data = train_data.with_options(options)

  return train_data, number_of_classes


def create_model(number_of_classes):
  '''Creates and compiles pretrained ResNet50 model.'''

  base_model = tf.keras.applications.ResNet50(weights='imagenet', include_top=False)
  x = base_model.output
  x = tf.keras.layers.GlobalAveragePooling2D()(x)
  x = tf.keras.layers.Dense(1016, activation='relu')(x)
  predictions = tf.keras.layers.Dense(number_of_classes, activation='softmax')(x)
  model = tf.keras.Model(inputs=base_model.input, outputs=predictions)

  model.compile(
      loss='sparse_categorical_crossentropy',
      optimizer=tf.keras.optimizers.Adam(0.0001),
      metrics=['accuracy'])

  return model


def _is_chief(task_type, task_id):
  '''Helper function. Determines if machine is chief.'''

  return task_type == 'chief'


def _get_temp_dir(dirpath, task_id):
  '''Helper function. Gets temporary directory for saving model.'''

  base_dirpath = 'workertemp_' + str(task_id)
  temp_dir = os.path.join(dirpath, base_dirpath)
  tf.io.gfile.makedirs(temp_dir)
  return temp_dir


def write_filepath(filepath, task_type, task_id):
  '''Helper function. Gets filepath to save model.'''

  dirpath = os.path.dirname(filepath)
  base = os.path.basename(filepath)
  if not _is_chief(task_type, task_id):
    dirpath = _get_temp_dir(dirpath, task_id)
  return os.path.join(dirpath, base)


def main():
  # Create strategy
  strategy = tf.distribute.MultiWorkerMirroredStrategy()

  # Get data
  global_batch_size = PER_REPLICA_BATCH_SIZE * strategy.num_replicas_in_sync
  train_data, number_of_classes = create_dataset(global_batch_size)

  # Wrap variable creation within strategy scope
  with strategy.scope():
    model = create_model(number_of_classes)

  model.fit(train_data, epochs=EPOCHS)

  # Determine type and task of the machine from
  # the strategy cluster resolver
  task_type, task_id = (strategy.cluster_resolver.task_type,
                        strategy.cluster_resolver.task_id)

  # Based on the type and task, write to the desired model path
  write_model_path = write_filepath(BUCKET, task_type, task_id)
  model.save(write_model_path)

if __name__ == "__main__":
    main()

Zanim utworzysz kontener, przyjrzyjmy się bliżej kodowi, który korzysta z funkcji MultiWorkerMirroredStrategy z interfejsu tf.distribute.Strategy API.

W kodzie jest kilka komponentów, które są niezbędne do jego działania w MultiWorkerMirroredStrategy.

  1. Dane muszą być podzielone na fragmenty, co oznacza, że każdy pracownik otrzymuje podzbiór całego zbioru danych. Dlatego na każdym etapie każdy pracownik przetwarza globalną wielkość wsadu niepokrywających się elementów zbioru danych. Dzielenie na partycje odbywa się automatycznie w przypadku tf.data.experimental.AutoShardPolicy, które można ustawić na FILE lub DATA. W tym przykładzie funkcja create_dataset() ustawia wartość AutoShardPolicy na DATA, ponieważ zbiór danych dotyczących manioku nie jest pobierany jako wiele plików. Jeśli jednak nie ustawisz zasady na DATA, zacznie obowiązywać domyślna zasada AUTO, a wynik końcowy będzie taki sam. Więcej informacji o dzieleniu zbiorów danych za pomocą MultiWorkerMirroredStrategy znajdziesz tutaj.
  2. W funkcji main() tworzony jest obiekt MultiWorkerMirroredStrategy. Następnie umieść tworzenie zmiennych modelu w zakresie strategii. Ten ważny krok informuje TensorFlow, które zmienne powinny być odzwierciedlane w replikach.
  3. Wielkość wsadu jest zwiększana o num_replicas_in_sync. Dzięki temu każda replika przetwarza na każdym etapie tę samą liczbę przykładów. Skalowanie wielkości wsadu jest sprawdzoną metodą w przypadku korzystania w TensorFlow ze strategii synchronicznego równoległego przetwarzania danych.
  4. Zapisanie modelu jest nieco bardziej skomplikowane w przypadku wielu instancji roboczych, ponieważ miejsce docelowe musi być inne dla każdej z nich. Główny proces roboczy zapisze model w wybranym katalogu, a pozostałe procesy robocze zapiszą model w katalogach tymczasowych. Ważne jest, aby te katalogi tymczasowe były unikalne, aby zapobiec zapisywaniu danych w tym samym miejscu przez wielu pracowników. Zapisywanie może obejmować operacje zbiorowe, co oznacza, że wszyscy pracownicy muszą zapisać dane, a nie tylko kierownik. Funkcje _is_chief(), _get_temp_dir(), write_filepath()main() zawierają powtarzalny kod, który pomaga zapisać model.

Jeśli MultiWorkerMirroredStrategy było używane w innym środowisku, zmienna środowiskowa TF_CONFIG mogła zostać skonfigurowana. Vertex AI automatycznie ustawia wartość TF_CONFIG, więc nie musisz definiować tej zmiennej na każdej maszynie w klastrze.

Krok 4. 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/multiworker:cassava"

Konfigurowanie Dockera

gcloud auth configure-docker

Następnie utwórz kontener, uruchamiając to polecenie w katalogu głównym cassava:

docker build ./ -t $IMAGE_URI

Na koniec przenieś go do Google Container Registry:

docker push $IMAGE_URI

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

6. Uruchamianie zadania trenowania z wieloma procesami roboczymi w Vertex AI

W tym laboratorium używamy trenowania niestandardowego za pomocą kontenera niestandardowego w Google Container Registry, ale możesz też uruchomić zadanie trenowania za pomocą gotowych kontenerów.

Na początek otwórz sekcję Trenowanie w sekcji Vertex w konsoli Cloud:

Menu uCAIP

Krok 1. Skonfiguruj zadanie trenowania

Kliknij Utwórz, aby wprowadzić parametry zadania trenowania.

  • W sekcji Zbiór danych wybierz Brak zarządzanego zbioru danych.
  • Następnie jako metodę trenowania wybierz Trenowanie niestandardowe (zaawansowane) i kliknij Dalej.
  • W polu Nazwa modelu wpisz multiworker-cassava (lub dowolną inną nazwę).
  • Kliknij Dalej.

W kroku Ustawienia kontenera wybierz Kontener niestandardowy:

Opcja niestandardowego kontenera

W pierwszym polu (Obraz kontenera) wpisz wartość zmiennej IMAGE_URI z poprzedniej sekcji. Powinien on mieć postać: gcr.io/your-cloud-project/multiworker:cassava, gdzie zamiast gcr.io/your-cloud-project/multiworker:cassava wpiszesz identyfikator swojego projektu. Pozostałe pola pozostaw puste i kliknij Dalej.

Pomiń krok Hiperparametry, klikając ponownie Dalej.

Krok 2. Skonfiguruj klaster obliczeniowy

Vertex AI udostępnia 4 pule instancji roboczych, które obsługują różne typy zadań wykonywanych przez maszyny.

Pula instancji roboczych 0 konfiguruje instancję główną, instancję główną, harmonogram lub „główną”. W MultiWorkerMirroredStrategy wszystkie maszyny są oznaczone jako procesory robocze, czyli maszyny fizyczne, na których wykonywane są replikowane obliczenia. Oprócz tego, że każda maszyna jest pracownikiem, musi być jeden pracownik, który wykonuje dodatkowe zadania, takie jak zapisywanie punktów kontrolnych i zapisywanie plików podsumowujących w TensorBoard. Ta maszyna jest nazywana szefem. Zawsze jest tylko 1 główny pracownik, więc liczba pracowników w puli instancji roboczych 0 będzie zawsze wynosić 1.

W sekcji Obliczenia i ceny pozostaw wybrany region bez zmian i skonfiguruj Pulę instancji roboczych 0 w ten sposób:

Worker_pool_0

W puli instancji roboczych 1 konfigurujesz instancje robocze dla klastra.

Skonfiguruj pulę instancji roboczych 1 w ten sposób:

Worker_pool_1

Klaster jest teraz skonfigurowany tak, aby zawierał 2 maszyny z samym procesorem. Po uruchomieniu kodu aplikacji trenującej MultiWorkerMirroredStrategy rozdzieli trenowanie na oba urządzenia.

MultiWorkerMirroredStrategy ma tylko typy zadań głównych i roboczych, więc nie musisz konfigurować dodatkowych pul instancji roboczych. Jeśli jednak używasz TensorFlow ParameterServerStrategy, serwery parametrów skonfigurujesz w puli instancji roboczych 2. Jeśli chcesz dodać do klastra ewaluatora, skonfiguruj tę maszynę w puli instancji roboczych 3.

Kliknij Rozpocznij trenowanie, aby uruchomić zadanie dostrajania hiperparametrów. W sekcji Trenowanie w konsoli na karcie PIPELINE TRENOWANIA zobaczysz nowo uruchomione zadanie:

Zadania trenowania

🎉 Gratulacje! 🎉

Dowiedziałeś się, jak używać Vertex AI do:

  • Uruchom zadanie treningowe z wieloma instancjami roboczymi, aby wytrenować kod podany w kontenerze niestandardowym. W tym przykładzie użyto modelu TensorFlow, ale możesz wytrenować model utworzony za pomocą dowolnej platformy, korzystając z kontenerów niestandardowych lub wbudowanych.

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

7. [Opcjonalnie] Użyj pakietu Vertex SDK

W poprzedniej sekcji pokazaliśmy, jak uruchomić zadanie trenowania w interfejsie. W tej sekcji znajdziesz alternatywny sposób przesyłania zadania trenowania za pomocą interfejsu Vertex Python API.

Wróć do instancji notatnika i utwórz notatnik TensorFlow 2 z poziomu Launchera:

new_notebook

Zaimportuj pakiet Vertex AI SDK.

from google.cloud import aiplatform

Aby uruchomić zadanie trenowania z wieloma instancjami roboczymi, musisz najpierw zdefiniować specyfikację puli instancji roboczych. Pamiętaj, że użycie GPU w specyfikacji jest całkowicie opcjonalne. Możesz usunąć accelerator_typeaccelerator_count, jeśli chcesz używać klastra tylko z procesorami, jak pokazano w poprzedniej sekcji.

# The spec of the worker pools including machine type and Docker image
# Be sure to replace {YOUR-PROJECT-ID} with your project ID.
worker_pool_specs=[
     {
        "replica_count": 1,
        "machine_spec": {
          "machine_type": "n1-standard-8", "accelerator_type": "NVIDIA_TESLA_V100", "accelerator_count": 1
        },
        "container_spec": {"image_uri": "gcr.io/{YOUR-PROJECT-ID}/multiworker:cassava"}
      },
      {
        "replica_count": 1,
        "machine_spec": {
          "machine_type": "n1-standard-8", "accelerator_type": "NVIDIA_TESLA_V100", "accelerator_count": 1
        },
        "container_spec": {"image_uri": "gcr.io/{YOUR-PROJECT-ID}/multiworker:cassava"}
      }
]

Następnie utwórz i uruchom CustomJob. Zamiast {YOUR_BUCKET} wpisz zasobnik w projekcie, który będzie służyć jako miejsce tymczasowe. Możesz użyć tego samego zasobnika, który został utworzony wcześniej.

# Replace YOUR_BUCKET
my_multiworker_job = aiplatform.CustomJob(display_name='multiworker-cassava-sdk',
                              worker_pool_specs=worker_pool_specs,
                              staging_bucket='gs://{YOUR_BUCKET}')

my_multiworker_job.run()

W sekcji Trenowanie w konsoli na karcie ZADANIA NIESTANDARDOWE zobaczysz zadanie trenowania:

Zadania niestandardowe

8. Czyszczenie

Skonfigurowaliśmy notebooka tak, aby po 60 minutach bezczynności wyłączał się automatycznie, więc nie musimy się martwić o zamykanie 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 Storage, w menu nawigacyjnym w konsoli Cloud otwórz Storage, wybierz zasobnik i kliknij Usuń:

Usuń miejsce na dane