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

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

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

מה לומדים

במאמר הזה נסביר איך:

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

העלות הכוללת להרצת שיעור ה-Lab הזה ב-Google Cloud היא בערך 6$.

2. מבוא ל-Vertex AI

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

‫Vertex AI כולל מוצרים רבים ושונים לתמיכה בתהליכי עבודה של למידת מכונה מקצה לקצה. בשיעור ה-Lab הזה נתמקד ב-Training וב-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 מחשב את המעברים קדימה ואחורה במודל על פרוסה שונה של נתוני הקלט. הגרדיאנטים המחושבים מכל אחד מהפלחים האלה מצטברים בכל יחידות ה-GPU ומחושב להם ממוצע בתהליך שנקרא all-reduce. פרמטרים של המודל מתעדכנים באמצעות הגרדיאנטים הממוצעים האלה.

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

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

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

שלב 1: הפעלת Compute Engine API

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

שלב 2: הפעלת Container Registry API

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

שלב 3: הפעלת Vertex AI API

עוברים אל הקטע Vertex AI במסוף Cloud ולוחצים על הפעלת 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. המשמעות היא שמחברת ה-notebook תיסגר אוטומטית כשלא משתמשים בה, כדי שלא תצטברו עלויות מיותרות.

idle_timeout

בקטע 'אבטחה', בוחרים באפשרות 'הפעלת מסוף' אם היא עדיין לא מופעלת.

enable-terminal

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

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

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

open_jupyterlab

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

אימות

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

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

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. האובייקט MirroredStrategy נוצר בפונקציה main(). בשלב הבא, עוטפים את יצירת משתני המודל בהיקף של שיטת הבידינג. בשלב הזה מגדירים ל-TensorFlow אילו משתנים לשכפל במעבדי ה-GPU.
  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 יכלול גם את ערכי המדדים ואת הפסד האימות. לדוגמה, אם אימנתם מודל ל-3 תקופות עם נתוני אימות וסיפקתם את 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

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

touch Dockerfile

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

+ Dockerfile
+ trainer/
    + task.py

פותחים את Dockerfile ומעתיקים לתוכו את הקוד הבא:

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 ל-GPU של TensorFlow Enterprise 2.7 של Deep Learning Container. הקונטיינרים של Deep Learning ב-Google Cloud מגיעים עם הרבה frameworks נפוצים של ML ומדעי נתונים שהותקנו מראש. אחרי שמורידים את התמונה הזו, קובץ ה-Docker הזה מגדיר את נקודת הכניסה לקוד האימון.

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

במסוף, מריצים את הפקודה הבאה כדי להגדיר משתנה סביבה לפרויקט. חשוב להחליף את 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

במשימת האימון, נעביר את הנתיב לדלי של staging.

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

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

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

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

ממרכז האפליקציות, פותחים מחברת חדשה של 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, תצטרכו לציין גם את ערך ההתאמה. בסרטון הזה אפשר לקבל מידע נוסף על בחירת הסולם המתאים ביותר.

# 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: אפשר להגדיר את אלגוריתם החיפוש לערכים grid,‏ random או default (None). אפשרות ברירת המחדל היא להחיל אופטימיזציה בייסיאנית כדי לחפש את מרחב הערכים האפשריים של היפרפרמטרים, וזהו האלגוריתם המומלץ. כאן אפשר לקרוא מידע נוסף על האלגוריתם הזה.

אחרי שהעבודה תתחיל, תוכלו לעקוב אחרי הסטטוס בממשק המשתמש בכרטיסייה HYPERPARAMETER TUNING JOBS.

HP_job

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

HP_results

‫🎉 איזה כיף! 🎉

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

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

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

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

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

delete

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

מחיקת האחסון