Vertex AI: כוונון היפר-פרמטרים מבוזר

1. סקירה כללית

בשיעור ה-Lab הזה תלמדו איך להשתמש ב-Vertex AI לכוונון היפר-פרמטרים ולאימון מבוזבז. בתרגיל הזה נעשה שימוש ב-TensorFlow לקוד המודל, אבל העקרונות רלוונטיים גם למסגרות אחרות של למידת מכונה.

מה תלמדו

תלמדו איך:

  • אימון מודל באמצעות אימון מבוזבז בקונטיינר בהתאמה אישית
  • הפעלת מספר ניסויים של קוד האימון לצורך כוונון היפר-פרמטרים אוטומטי

העלות הכוללת להרצת הסדנה הזו ב-Google Cloud היא כ-6$.

2. מבוא ל-Vertex AI

במעבדה הזו נעשה שימוש במוצרי ה-AI החדשים ביותר שזמינים ב-Google Cloud. Vertex AI משלבת את חבילות ה-ML ב-Google Cloud לחוויית פיתוח חלקה. בעבר, ניתן היה לגשת למודלים שעברו אימון באמצעות AutoML ומודלים בהתאמה אישית דרך שירותים נפרדים. המוצר החדש משלב את שניהם ב-API אחד, יחד עם מוצרים חדשים אחרים. תוכלו גם להעביר פרויקטים קיימים ל-Vertex AI. יש לך משוב? אפשר להיכנס לדף התמיכה.

Vertex AI כולל מוצרים רבים ושונים לתמיכה בתהליכי עבודה של למידת מכונה מקצה לקצה. בשיעור ה-Lab הזה נתמקד באימון וב-Workbench.

סקירה כללית של המוצר Vertex

3. סקירה כללית של תרחיש לדוגמה

בשיעור ה-Lab הזה תשתמשו בכוונון היפר-פרמטרים כדי לגלות פרמטרים אופטימליים למודל סיווג תמונות שאומן על מערך הנתונים של סוסים או בני אדם מ-TensorFlow Datasets.

כוונון של היפר-פרמטרים

הכוונון של ההיפר-פרמטרים באמצעות Vertex AI Training פועל על ידי הפעלת כמה ניסויים של אפליקציית האימון עם ערכים של ההיפר-פרמטרים שבחרתם, תוך שמירה על המגבלות שציינתם. מערכת Vertex AI עוקבת אחרי התוצאות של כל ניסוי ומבצעת התאמות לניסויים הבאים.

כדי להשתמש בהתאמה של היפר-פרמטרים באמצעות Vertex AI Training, צריך לבצע שני שינויים בקוד האימון:

  1. מגדירים ארגומנט של שורת הפקודה במודול האימון הראשי לכל היפר-פרמטר שרוצים לכוונן.
  2. משתמשים בערך המועבר בארגומנטים האלה כדי להגדיר את ההיפר-פרמטר התואם בקוד של האפליקציה.

אימון מבוזרת

אם יש לכם יחידת GPU אחת, TensorFlow ישתמש במאיץ הזה כדי להאיץ את אימון המודלים בלי מאמץ נוסף מצידכם. אבל אם רוצים לשפר את השימוש בכמה מעבדי GPU, צריך להשתמש ב-tf.distribute, שהוא המודול של TensorFlow להרצת חישוב בכמה מכשירים.

בסדנת ה-Labs הזו נעשה שימוש ב-tf.distribute.MirroredStrategy, שאפשר להוסיף לאפליקציות האימון שלכם באמצעות כמה שינויים קוד בלבד. לפי האסטרטגיה הזו, נוצר עותק של המודל בכל יחידת GPU במחשב. עדכוני ההדרגתיות הבאים יתבצעו באופן סינכרוני. כלומר, כל GPU מחשב את החזרה והקדימה דרך המודל בחלק אחר של נתוני הקלט. לאחר מכן, הנגזרות המחושבות מכל אחד מהפלחים האלה נצברות בכל המעבדים הגרפיים ומחושב להן ממוצע בתהליך שנקרא all-reduce. פרמטרים של מודל מתעדכנים באמצעות שיפועי הממוצע האלה.

אתם לא צריכים לדעת את הפרטים כדי להשלים את סדנת ה-Lab הזו, אבל אם אתם רוצים לקבל מידע נוסף על האופן שבו אימון מבוזבז פועל ב-TensorFlow, כדאי לצפות בסרטון הבא:

4. הגדרת הסביבה

כדי להריץ את ה-Codelab הזה צריך פרויקט ב-Google Cloud Platform שהחיוב מופעל בו. כדי ליצור פרויקט, פועלים לפי ההוראות שמפורטות כאן.

שלב 1: מפעילים את ממשק ה-API של Compute Engine

עוברים אל Compute Engine ובוחרים באפשרות Enable (הפעלה) אם היא עדיין לא מופעלת.

שלב 2: מפעילים את Container Registry API

עוברים אל Container Registry ובוחרים באפשרות Enable (הפעלה) אם היא עדיין לא מסומנת. תשתמשו בו כדי ליצור קונטיינר למשימת האימון בהתאמה אישית.

שלב 3: מפעילים את Vertex AI API

עוברים לקטע Vertex AI במסוף Cloud ולוחצים על Enable Vertex AI API.

מרכז הבקרה של Vertex AI

שלב 4: יצירת מכונה של Vertex AI Workbench

בקטע Vertex AI במסוף Cloud, לוחצים על Workbench:

התפריט של Vertex AI

מפעילים את Notebooks API, אם הוא לא פועל.

Notebook_api

אחרי ההפעלה, לוחצים על מנהלי מחברות:

Notebooks_UI

לאחר מכן בוחרים באפשרות מחברות חדשה.

new_notebook

נותנים שם למחברת ולוחצים על הגדרות מתקדמות.

create_notebook

בקטע 'הגדרות מתקדמות', מפעילים את ההגדרה 'כיבוי במצב חוסר פעילות' ומגדירים את מספר הדקות ל-60. המשמעות היא שהמחשב הנייד יכבה באופן אוטומטי כשלא משתמשים בו, כדי שלא תחויבו בעלויות מיותרות.

idle_timeout

בקטע Security (אבטחה), בוחרים באפשרות Enable terminal (הפעלת מסוף) אם היא עדיין לא מופעלת.

enable-terminal

אפשר להשאיר את כל שאר ההגדרות המתקדמות כפי שהן.

לאחר מכן, לוחצים על יצירה. תהליך ההקצאה של המכונה יימשך כמה דקות.

אחרי שהמכונה נוצרה, בוחרים באפשרות Open JupyterLab.

open_jupyterlab

בפעם הראשונה שתשתמשו במכונה חדשה, תתבקשו לבצע אימות. כדי לעשות זאת, פועלים לפי השלבים בממשק המשתמש.

אימות

5. כתיבה של קוד אימון

כדי להתחיל, פותחים חלון Terminal במכונה של המחברת בתפריט Launcher:

launcher_terminal

יוצרים ספרייה חדשה בשם vertex-codelab ומעבירים אליה את הספרייה באמצעות cd.

mkdir vertex-codelab
cd vertex-codelab

מריצים את הפקודות הבאות כדי ליצור ספרייה לקוד האימון וקובץ Python שבו תוסיפו את הקוד:

mkdir trainer
touch trainer/task.py

עכשיו אמורים להופיע בספרייה vertex-codelab:

+ trainer/
    + task.py

לאחר מכן, פותחים את הקובץ task.py שיצרתם ומדביקים את כל הקוד שבהמשך.

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

נבחן לעומק את הקוד ונבדוק את הרכיבים הספציפיים לאימון מבוזבז ולכוונון של היפר-פרמטרים.

הדרכה מבוזרת

  1. בפונקציה main() נוצר האובייקט MirroredStrategy. בשלב הבא, צריך לתחום את יצירת משתני המודל בהיקף של האסטרטגיה. השלב הזה מורה ל-TensorFlow אילו משתנים יש לשקף בין ה-GPUs.
  2. גודל האצווה יגדל לפי הערך של num_replicas_in_sync. השיטה המומלצת היא שינוי גודל האצווה כשמשתמשים באסטרטגיות סינכרוניות של נתונים במקביל ב-TensorFlow. מידע נוסף זמין כאן.

כוונון של היפר-פרמטרים

  1. הסקריפט מייבא את הספרייה hypertune. בהמשך, כשנבנה את קובץ האימג' בקונטיינר, נצטרך לוודא שאנחנו מתקינים את הספרייה הזו.
  2. הפונקציה get_args() מגדירה ארגומנט בשורת הפקודה לכל היפר-פרמטר שרוצים לכוונן. בדוגמה הזו, הפרמטרים ההיפר-מרחביים שיכוונו הם קצב הלמידה, ערך המומנטום באופטימיזטור ומספר היחידות בשכבה החבויה האחרונה של המודל, אבל אתם יכולים להתנסות בפרמטרים אחרים. לאחר מכן, הערך המועבר בארגומנטים האלה משמש להגדרת ההיפרבארמטרים המתאימים בקוד (למשל, הגדרת learning_rate = args.learning_rate)
  3. בסוף הפונקציה main(), הספרייה hypertune משמשת להגדרת המדד שרוצים לבצע עבורו אופטימיזציה. ב-TensorFlow, השיטה model.fit של Keras מחזירה אובייקט History. המאפיין History.history הוא רשומה של ערכי אובדן האימון וערכי המדדים ברצפי תקופות אימון. אם מעבירים נתוני אימות אל model.fit, המאפיין History.history יכלול גם את הערכים של אובדן האימות והמדדים. לדוגמה, אם אימנתם מודל לשלוש תקופות עם נתוני אימות וסיפקתם את accuracy כמדד, המאפיין History.history ייראה דומה למילון הבא.
{
 "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
 ]

אם רוצים ששירות הכוונון של ההיפר-פרמטרים יגלה את הערכים שמשפרים את דיוק האימות של המודל, צריך להגדיר את המדד בתור הרשומה האחרונה (או NUM_EPOCS - 1) ברשימה val_accuracy. לאחר מכן, מעבירים את המדד הזה למכונה של HyperTune. אפשר לבחור כל מחרוזת שרוצים עבור hyperparameter_metric_tag, אבל תצטרכו להשתמש במחרוזת הזו שוב בהמשך, כשתפעילו את המשימה של כוונון הפרמטרים העל-היפר.

6. יצירת קונטיינרים של הקוד

השלב הראשון ביצירת קונטיינרים לקוד הוא ליצור קובץ Dockerfile. בקובץ Dockerfile תכללו את כל הפקודות הנדרשות להרצת קובץ האימג'. הוא יתקין את כל הספריות הנדרשות ותגדיר את נקודת הכניסה לקוד האימון.

שלב 1: כתיבת Dockerfile

ב-Terminal, מוודאים שנמצאים בספרייה vertex-codelab ויוצרים קובץ Dockerfile ריק:

touch Dockerfile

עכשיו אמורים להיות בספרייה vertex-codelab הפריטים הבאים:

+ Dockerfile
+ trainer/
    + task.py

פותחים את קובץ ה-Docker ומעתיקים אליו את הפקודה הבאה:

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

קובץ ה-Dockerfile הזה משתמש בקובץ אימג' של Docker ב-Deep Learning Container TensorFlow Enterprise 2.7 GPU. הקונטיינרים של Deep Learning ב-Google Cloud מגיעים עם הרבה frameworks נפוצים של מדעי נתונים ולמידת מכונה שהותקנו מראש. אחרי הורדת התמונה, קובץ ה-Dockerfile הזה מגדיר את נקודת הכניסה לקוד האימון.

שלב 2: יצירת הקונטיינר

ב-Terminal, מריצים את הפקודה הבאה כדי להגדיר משתנה סביבה לפרויקט. חשוב להחליף את your-cloud-project במזהה הפרויקט:

PROJECT_ID='your-cloud-project'

מגדירים משתנה עם ה-URI של קובץ האימג' בקונטיינר ב-Google Container Registry:

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

הגדרת Docker

gcloud auth configure-docker

לאחר מכן, יוצרים את הקונטיינר על ידי הפעלת הפקודה הבאה מהשורש של ספריית vertex-codelab:

docker build ./ -t $IMAGE_URI

לבסוף, מעבירים אותו ל-Google Container Registry:

docker push $IMAGE_URI

שלב 3: יצירת קטגוריה ב-Cloud Storage

במשימה של האימון, נעביר את הנתיב לקטגוריית עיבוד נתונים.

כדי ליצור קטגוריה חדשה בפרויקט, מריצים את הפקודה הבאה ב-Terminal.

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

7. הפעלת משימה של כוונון היפר-פרמטרים

שלב 1: יוצרים משימה מותאמת אישית של אימון עם כוונון של פרמטרים היפר-מרחביים

פותחים מסמך notebook חדש של TensorFlow 2 במרכז האפליקציות.

new_notebook

מייבאים את Vertex AI Python SDK.

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

כדי להפעיל את משימת הכוונון של ההיפר-פרמטרים, צריך קודם להגדיר את worker_pool_specs, שמציין את סוג המכונה ואת תמונת ה-Docker. המפרט הבא מגדיר מכונה אחת עם שני מעבדי NVIDIA Tesla V100 GPU.

צריך להחליף את {PROJECT_ID} ב-image_uri בשם הפרויקט שלכם.

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

בשלב הבא, מגדירים את parameter_spec, שהוא מילון שמציין את הפרמטרים שרוצים לבצע להם אופטימיזציה. מפתח המילון הוא המחרוזת שהקצית לארגומנט של שורת הפקודה לכל היפר-פרמטר, וערך המילון הוא מפרט הפרמטר.

לכל היפר-פרמטר, צריך להגדיר את הסוג ואת הגבולות של הערכים ששירות הכוונון ינסה. הפרמטרים היפרבאריים יכולים להיות מסוג Double,‏ Integer,‏ Categorical או Discrete. אם בוחרים בסוג Double או Integer, צריך לספק ערך מינימלי ומקסימלי. אם בוחרים באפשרות 'קטגורית' או 'בדיד', יש לספק את הערכים. לסוגי Double ו-Integer, תצטרכו גם לספק את הערך של Scaling. בסרטון הזה מוסבר איך בוחרים את התצוגה המתאימה ביותר.

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

המפרט הסופי שצריך להגדיר הוא metric_spec, שהוא מילון שמייצג את המדד שרוצים לבצע עבורו אופטימיזציה. מפתח המילון הוא הערך hyperparameter_metric_tag שהגדרתם בקוד של אפליקציית האימון, והערך הוא יעד האופטימיזציה.

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

אחרי הגדרת המפרטים, תיצרו CustomJob, שהוא המפרט המשותף שישמש להרצת המשימה בכל אחת מהניסויים של כוונון הפרמטרים העל-היפר.

צריך להחליף את {YOUR_BUCKET} בקטגוריה שיצרתם קודם.

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

לאחר מכן יוצרים את 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()

יש כמה ארגומנטים שחשוב לשים לב אליהם:

  • max_trial_count: תצטרכו לקבוע גבול עליון למספר תקופות הניסיון שהשירות יפעל. בדרך כלל, ככל שמבצעים יותר ניסויים, התוצאות טובות יותר. עם זאת, תגיע נקודה שבה התוצאות לא ישתפרו יותר, ואז ניסויים נוספים לא ישפיעו כמעט על המדד שאתם מנסים לבצע אופטימיזציה שלו. מומלץ להתחיל עם מספר קטן יותר של ניסויים ולקבל מושג לגבי ההשפעה של ההיפר-פרמטרים שבחרתם לפני ההגדלה.
  • parallel_trial_count: אם משתמשים בניסויים מקבילים, השירות מקצה כמה אשכולות לעיבוד אימון. הגדלת מספר הניסויים המקבילים מקצרת את משך הזמן של הריצה של המשימה 'כוונון היפר-פרמטרים', אבל היא עלולה להפחית את היעילות הכוללת של המשימה. הסיבה לכך היא ששיטת ברירת המחדל לכוונון מתבססת על תוצאות של ניסויים קודמים כדי לקבוע את הקצאת הערכים בניסויים הבאים.
  • search_algorithm: אפשר להגדיר את אלגוריתם החיפוש להצגת רשת, אקראית או ברירת מחדל (ללא). אפשרות ברירת המחדל מחילה אופטימיזציה בייסינית כדי לחפש את המרחב של ערכי ההיפארמטרים האפשריים, והיא האלגוריתם המומלץ. כאן אפשר לקרוא מידע נוסף על האלגוריתם הזה.

אחרי שהמשימה תתחיל, תוכלו לעקוב אחרי הסטטוס שלה בממשק המשתמש בכרטיסייה משימות כוונון של היפר-פרמטר.

HP_job

בסיום המשימה, תוכלו להציג ולמיין את התוצאות של הניסויים כדי לגלות את השילוב הטוב ביותר של ערכי ההיפארמטרים.

HP_results

🎉 מזל טוב! 🎉

למדתם איך להשתמש ב-Vertex AI כדי:

  • הרצת משימת כוונון של היפר-פרמטר באמצעות אימון מבוזרת

כדי לקבל מידע נוסף על החלקים השונים ב-Vertex AI, אתם יכולים לעיין במסמכי העזרה.

8. הסרת המשאבים

מכיוון שהגדרתנו ל-notebook זמן תפוגה של 60 דקות חוסר פעילות, אין צורך לדאוג לגבי כיבוי המכונה. כדי לכבות את המכונה באופן ידני, לוחצים על הלחצן Stop בקטע Vertex AI Workbench במסוף. אם אתה רוצה למחוק לגמרי את ה-notebook, לחץ על הלחצן 'מחיקה'.

delete

כדי למחוק את הקטגוריה של האחסון, עוברים לתפריט הניווט במסוף Cloud, לוחצים על Storage, בוחרים את הקטגוריה ולוחצים על Delete:

מחיקת אחסון