קבלת חיזויים ממודל תמונה של TensorFlow שעבר אימון מקדים ב-Vertex AI

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

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

מה תלמדו

תלמדו איך:

  • ייבוא מודל TensorFlow למרשם המודלים של Vertex AI
  • קבלת חיזויים אונליין
  • עדכון פונקציית מילוי הבקשות של TensorFlow

העלות הכוללת של הפעלת שיעור ה-Lab הזה ב-Google Cloud היא כ-$1.

2. מבוא ל-Vertex AI

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

התפריט של Vertex AI

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

Notebook_api

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

Notebooks_UI

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

new_notebook

נותנים שם למחברת, ובקטע Permission בוחרים באפשרות Service account.

create_notebook

בוחרים באפשרות הגדרות מתקדמות.

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

enable_terminal

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

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

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

open_jupyterlab

5. רישום מודל

שלב 1: מעלים את המודל ל-Cloud Storage

לוחצים על הקישור הזה כדי לעבור לדף TensorFlow Hub של מודל MobileNet V1 שאומן על מערך הנתונים של ImagNet.

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

download_model

בקטע Cloud Storage במסוף Google Cloud, בוחרים באפשרות CREATE.

create_bucket

נותנים שם לקטגוריה ובוחרים את האזור us-central1. לאחר מכן לוחצים על יצירה.

specify_bucket

מעלים את המודל של TensorFlow Hub שהורדתם לקטגוריה. חשוב להסיר קודם את הקובץ.

gcs_model

הקטגוריה אמורה להיראות כך:

imagenet_mobilenet_v1_050_128_classification_5/
  saved_model.pb
  variables/
    variables.data-00000-of-00001
    variables.index

שלב 2: מייבאים את המודל למרשם

עוברים לקטע Model Domain (רישום המודלים) של Vertex AI במסוף Cloud.

model_registry

בוחרים באפשרות ייבוא.

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

name_and_region

בקטע Model settings (הגדרות המודל), מציינים את הקונטיינר העדכני ביותר של TensorFlow שנוצר מראש. לאחר מכן בוחרים את הנתיב ב-Cloud Storage שבו אחסנתם את ארטיפקטים של המודל.

select_container

אפשר לדלג על הקטע הסבר.

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

אחרי הייבוא, המודל יופיע במרשם המודלים

imported_model

6. פריסת מודל

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

deploy_model

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

בקטע Model settings, מגדירים את Maximum number of compute nodes לערך 1, את machine type לערך n1-standard-2 ומשאירים את כל שאר ההגדרות כפי שהן. לאחר מכן לוחצים על DEPLOY.

endpoint_settings

לאחר הפריסה, סטטוס הפריסה ישתנה ל-Deployed on Vertex AI.

deploy_status

7. הצגת תחזיות

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

tf_nb

כדי לייבא את הספריות הנדרשות, מריצים את התא הבא

from google.cloud import aiplatform

import tensorflow as tf
import numpy as np
from PIL import Image

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

# Download image labels

labels_path = tf.keras.utils.get_file('ImageNetLabels.txt','https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')
imagenet_labels = np.array(open(labels_path).read().splitlines())

כדי להגיע לנקודת הקצה, צריך להגדיר את משאב נקודת הקצה. חשוב להחליף את {PROJECT_NUMBER} ו-{ENDPOINT_ID}.

PROJECT_NUMBER = "{PROJECT_NUMBER}"
ENDPOINT_ID = "{ENDPOINT_ID}"

endpoint = aiplatform.Endpoint(
    endpoint_name=f"projects/{PROJECT_NUMBER}/locations/us-central1/endpoints/{ENDPOINT_ID}")

מספר הפרויקט מופיע בדף הבית של המסוף.

project_number

ומזהה נקודת הקצה בקטע Endpoints ב-Vertex AI.

endpoint_id

בשלב הבא נבדוק את נקודת הקצה.

קודם כול, מורידים את התמונה הבאה ומעלים אותה למכונה.

test_image

פותחים את התמונה באמצעות PIL. לאחר מכן משנים את הגודל ומשנים את הגודל ב-255. שימו לב שגודל התמונה הצפוי של המודל מופיע בדף של המודל ב-TensorFlow Hub.

IMAGE_PATH = "test-image.jpg"
IMAGE_SIZE = (128, 128)

im = Image.open(IMAGE_PATH)
im = im.resize(IMAGE_SIZE
im = np.array(im)/255.0

בשלב הבא, ממירים את נתוני NumPy לרשימה כדי שניתן יהיה לשלוח אותם בגוף הבקשה של ה-http.

x_test = im.astype(np.float32).tolist()

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

# make prediction request
result = endpoint.predict(instances=[x_test]).predictions

# post process result
predicted_class = tf.math.argmax(result[0], axis=-1)
string_label = imagenet_labels[predicted_class]

print(f"label ID: {predicted_class}")
print(f"string label: {string_label}")

8. [אופציונלי] שימוש ב-TF Serving כדי לבצע אופטימיזציה של התחזיות

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

שלב 1: שינוי פונקציית ההצגה

פותחים מסמך notebook חדש ב-TensorFlow ומביאים את הספריות הנדרשות.

from google.cloud import aiplatform

import tensorflow as tf

במקום להוריד את ארטיפקטים שנשמרו של המודל, הפעם טוענים את המודל ב-TensorFlow באמצעות הפקודה hub.KerasLayer, שעוטפת את TensorFlow SaveModel כשכבת Keras. כדי ליצור את המודל, אפשר להשתמש ב-Keras Sequential API עם מודל TF Hub שהורדתם כשכבה, ולציין את צורת הקלט למודל.

tfhub_model = tf.keras.Sequential(
    [hub.KerasLayer("https://tfhub.dev/google/imagenet/mobilenet_v1_050_128/classification/5")]
)
tfhub_model.build([None, 128, 128, 3])

מגדירים את ה-URI לקטגוריה שיצרתם מקודם.

BUCKET_URI = "gs://{YOUR_BUCKET}"
MODEL_DIR = BUCKET_URI + "/bytes_model"

כששולחים בקשה לשרת חיזוי אונליין, הבקשה מתקבלת על ידי שרת HTTP. שרת ה-HTTP מחלץ את בקשת החיזוי מגוף תוכן הבקשה של ה-HTTP. בקשת החיזוי שחולצה מועברת לפונקציית ההצגה. בקונטיינרים של תחזיות שנוצרו מראש ב-Vertex AI, תוכן הבקשה מועבר לפונקציית ההצגה כ-tf.string.

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

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

כדי לפתור את הבעיה, מגדירים פונקציית שירות (serving_fn) ומצרפים אותה למודל כשלב עיבוד מקדים. מוסיפים מעטר (decorator) מסוג @tf.function כדי שפונקציית ההצגה תשולבה במודל הבסיסי (במקום ב-upstream ב-CPU).

CONCRETE_INPUT = "numpy_inputs"


def _preprocess(bytes_input):
    decoded = tf.io.decode_jpeg(bytes_input, channels=3)
    decoded = tf.image.convert_image_dtype(decoded, tf.float32)
    resized = tf.image.resize(decoded, size=(128, 128))
    return resized


@tf.function(input_signature=[tf.TensorSpec([None], tf.string)])
def preprocess_fn(bytes_inputs):
    decoded_images = tf.map_fn(
        _preprocess, bytes_inputs, dtype=tf.float32, back_prop=False
    )
    return {
        CONCRETE_INPUT: decoded_images
    }  # User needs to make sure the key matches model's input


@tf.function(input_signature=[tf.TensorSpec([None], tf.string)])
def serving_fn(bytes_inputs):
    images = preprocess_fn(bytes_inputs)
    prob = m_call(**images)
    return prob


m_call = tf.function(tfhub_model.call).get_concrete_function(
    [tf.TensorSpec(shape=[None, 128, 128, 3], dtype=tf.float32, name=CONCRETE_INPUT)]
)

tf.saved_model.save(tfhub_model, MODEL_DIR, signatures={"serving_default": serving_fn})

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

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

loaded = tf.saved_model.load(MODEL_DIR)

serving_input = list(
    loaded.signatures["serving_default"].structured_input_signature[1].keys()
)[0]
print("Serving function input name:", serving_input)

שלב 2: ייבוא לרישום ופריסה

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

model = aiplatform.Model.upload(
    display_name="optimized-model",
    artifact_uri=MODEL_DIR,
    serving_container_image_uri="us-docker.pkg.dev/vertex-ai/prediction/tf2-cpu.2-8:latest",
)

print(model)

אפשר גם לפרוס את המודל באמצעות ה-SDK, במקום באמצעות ממשק המשתמש.

endpoint = model.deploy(
     deployed_model_display_name='my-bytes-endpoint',
     traffic_split={"0": 100},
     machine_type="n1-standard-4",
     accelerator_count=0,
     min_replica_count=1,
     max_replica_count=1,
   )

שלב 3: בדיקת המודל

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

מורידים שוב את תוויות התמונה

import numpy as np
labels_path = tf.keras.utils.get_file('ImageNetLabels.txt','https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')
imagenet_labels = np.array(open(labels_path).read().splitlines())

מקודדים את התמונה ב-Base64.

import base64

with open("test-image.jpg", "rb") as f:
    data = f.read()
b64str = base64.b64encode(data).decode("utf-8")

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

instances = [{serving_input: {"b64": b64str}}]

# Make request
result = endpoint.predict(instances=instances).predictions

# Convert image class to string label
predicted_class = tf.math.argmax(result[0], axis=-1)
string_label = imagenet_labels[predicted_class]

print(f"label ID: {predicted_class}")
print(f"string label: {string_label}")

🎉 כל הכבוד! 🎉

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

  • אירוח ופריסה של מודל שעבר אימון מקדים

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

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

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

עצירת המופע

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

מחיקת אחסון