אב טיפוס לסביבת ייצור: כוונון היפר-פרמטר

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

בשיעור ה-Lab הזה נשתמש ב-Vertex AI כדי להריץ משימת כוונון היפר-פרמטרים באימון של Vertex AI.

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

.

מה תלמדו

תלמדו איך:

  • שינוי קוד האפליקציה של האימון לצורך כוונון אוטומטי של פרמטרים היפר
  • הגדרה והפעלה של משימת כוונון של היפר-פרמטרים באמצעות Vertex AI Python SDK

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

2. מבוא ל-Vertex AI

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

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

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

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

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

4. אריזה בקונטיינר של קוד אפליקציית האימון

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

כדי להתחיל, פותחים חלון טרמינל מתפריט Launcher של ה-notebook ב-Workbench שיצרתם בסדנאות הקודמות.

פתיחת מסוף ב-notebook

שלב 1: כותבים קוד לאימון

יוצרים ספרייה חדשה בשם flowers-hptune ומוסיפים אליה cd:

mkdir flowers-hptune
cd flowers-hptune

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

mkdir trainer
touch trainer/task.py

עכשיו אמורים להיות בספרייה flowers-hptune/ הפריטים הבאים:

+ trainer/
    + task.py

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

צריך להחליף את {your-gcs-bucket} באפליקציה BUCKET_ROOT בקטגוריה של Cloud Storage שבה אחסנתם את מערך הנתונים של הפרחים ב-Lab 1.

import tensorflow as tf
import numpy as np
import os
import hypertune
import argparse

## Replace {your-gcs-bucket} !!
BUCKET_ROOT='/gcs/{your-gcs-bucket}'

# Define variables
NUM_CLASSES = 5
EPOCHS=10
BATCH_SIZE = 32

IMG_HEIGHT = 180
IMG_WIDTH = 180

DATA_DIR = f'{BUCKET_ROOT}/flower_photos'

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 create_datasets(data_dir, batch_size):
  '''Creates train and validation datasets.'''

  train_dataset = tf.keras.utils.image_dataset_from_directory(
    data_dir,
    validation_split=0.2,
    subset="training",
    seed=123,
    image_size=(IMG_HEIGHT, IMG_WIDTH),
    batch_size=batch_size)

  validation_dataset = tf.keras.utils.image_dataset_from_directory(
    data_dir,
    validation_split=0.2,
    subset="validation",
    seed=123,
    image_size=(IMG_HEIGHT, IMG_WIDTH),
    batch_size=batch_size)

  train_dataset = train_dataset.cache().shuffle(1000).prefetch(buffer_size=tf.data.AUTOTUNE)
  validation_dataset = validation_dataset.cache().prefetch(buffer_size=tf.data.AUTOTUNE)

  return train_dataset, validation_dataset


def create_model(num_units, learning_rate, momentum):
  '''Creates model.'''

  model = tf.keras.Sequential([
    tf.keras.layers.Resizing(IMG_HEIGHT, IMG_WIDTH),
    tf.keras.layers.Rescaling(1./255, input_shape=(IMG_HEIGHT, IMG_WIDTH, 3)),
    tf.keras.layers.Conv2D(16, 3, padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(32, 3, padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(num_units, activation='relu'),
    tf.keras.layers.Dense(NUM_CLASSES, activation='softmax')
  ])

  model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=learning_rate, momentum=momentum),
              loss=tf.keras.losses.SparseCategoricalCrossentropy(),
              metrics=['accuracy'])
  
  return model

def main():
  args = get_args()
  train_dataset, validation_dataset = create_datasets(DATA_DIR, BATCH_SIZE)
  model = create_model(args.num_units, args.learning_rate, args.momentum)
  history = model.fit(train_dataset, validation_data=validation_dataset, epochs=EPOCHS)

  # 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=EPOCHS)


if __name__ == "__main__":
    main()

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

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

שלב 2: יוצרים קובץ Docker

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

ב-Terminal, יוצרים קובץ Dockerfile ריק ברמה הבסיסית (root) של ספריית flowers-hptune:

touch Dockerfile

עכשיו אמורים להופיע בספרייה flowers-hptune/ הפריטים הבאים:

+ Dockerfile
+ trainer/
    + task.py

פותחים את Dockerfile ומעתיקים אליו את הטקסט הבא. תוכלו לראות שהוא כמעט זהה לקובץ ה-Docker שבו השתמשנו בשיעור ה-Lab הראשון, אבל עכשיו אנחנו מתקינים את הספרייה cloudml-hyper המצב.

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

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

שלב 3: יוצרים את המאגר

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

PROJECT_ID='your-cloud-project'

להגדיר מאגר ב-Artifact Registry. נשתמש במאגר שיצרנו במעבדה הראשונה.

REPO_NAME='flower-app'

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

IMAGE_URI=us-central1-docker.pkg.dev/$PROJECT_ID/$REPO_NAME/flower_image_hptune:latest

הגדרת Docker

gcloud auth configure-docker \
    us-central1-docker.pkg.dev

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

docker build ./ -t $IMAGE_URI

לסיום, מעבירים אותו בדחיפה ל-Artifact Registry:

docker push $IMAGE_URI

אחרי שדחפתם את הקונטיינר ל-Artifact Registry, אתם מוכנים להתחיל את משימת האימון.

5. הפעלת משימה של כוונון היפר-פרמטרים באמצעות ה-SDK

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

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

new_notebook

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

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

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

צריך להחליף את {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": 1
    },
    "replica_count": 1,
    "container_spec": {
        "image_uri": "us-central1-docker.pkg.dev/{PROJECT_ID}/flower-app/flower_image_hptune:latest"
    }
}]

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

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

# Dictionary representing metric 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'}

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

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

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

לאחר מכן יוצרים את HyperparameterTuningJob ומריצים אותו.

hp_job = aiplatform.HyperparameterTuningJob(
    display_name='flowers-hptune-job',
    custom_job=my_custom_job,
    metric_spec=metric_spec,
    parameter_spec=parameter_spec,
    max_trial_count=15,
    parallel_trial_count=3)

hp_job.run()

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

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

במסוף תוכלו לראות את ההתקדמות של המשימה.

hp_job

בסיום, תוכלו לראות את התוצאות של כל ניסוי ואת קבוצת הערכים שהניבו את הביצועים הטובים ביותר.

hp_results

🎉 מזל טוב! 🎉

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

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

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

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

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

הפסקת המכונה

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

מחיקת אחסון