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

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

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

מה לומדים

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

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

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

2. מבוא ל-Vertex AI

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

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

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

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

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

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

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

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

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

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

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

עוברים אל הקטע Vertex AI במסוף Cloud ולוחצים על הפעלת Vertex AI API.

לוח הבקרה של Vertex AI

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

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

תפריט Vertex AI

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

Notebook_api

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

Notebooks_UI

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

new_notebook

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

create_notebook

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

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

enable_terminal

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

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

אחרי שיוצרים את המופע, בוחרים באפשרות OPEN JUPYTERLAB (פתיחת JupyterLab).

open_jupyterlab

5. רישום המודל

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

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

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

download_model

בקטע Cloud Storage במסוף Google Cloud, לוחצים על CREATE.

create_bucket

נותנים שם ל-bucket ובוחרים באזור us-central1. ואז לוחצים על יצירה.

specify_bucket

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

gcs_model

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

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

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

עוברים לקטע Model registry של Vertex AI במסוף Cloud.

model_registry

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

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

name_and_region

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

select_container

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

אחר כך לוחצים על IMPORT.

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

imported_model

6. פריסת מודל

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

deploy_model

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

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

endpoint_settings

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

deploy_status

7. קבלת תחזיות

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

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

from google.cloud import aiplatform

import tensorflow as tf

במקום להוריד את הארטיפקטים של המודל השמור, הפעם תטענו את המודל ל-TensorFlow באמצעות hub.KerasLayer, שעוטף TensorFlow SavedModel כשכבת 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) ומצרפים אותה למודל כשלב של עיבוד מקדים. מוסיפים את העיטור @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 Console, עוברים אל Storage, בוחרים את הקטגוריה ולוחצים על Delete:

מחיקת האחסון