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
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.
שלב 3: יצירת מכונה של Vertex AI Workbench
בקטע Vertex AI במסוף Cloud, לוחצים על Workbench:
מפעילים את Notebooks API, אם הוא לא פועל.
אחרי ההפעלה, לוחצים על מנהלי מחברות:
לאחר מכן בוחרים באפשרות מחברות חדשה.
נותנים שם למחברת, ובקטע Permission בוחרים באפשרות Service account.
בוחרים באפשרות הגדרות מתקדמות.
בקטע אבטחה, בוחרים באפשרות 'הפעלת מסוף' אם היא עדיין לא מופעלת.
אפשר להשאיר את כל שאר ההגדרות המתקדמות כפי שהן.
לאחר מכן, לוחצים על יצירה. תהליך ההקצאה של המכונה יימשך כמה דקות.
אחרי יצירת המכונה, בוחרים באפשרות OPEN JUPYTERLAB.
5. רישום מודל
שלב 1: מעלים את המודל ל-Cloud Storage
לוחצים על הקישור הזה כדי לעבור לדף TensorFlow Hub של מודל MobileNet V1 שאומן על מערך הנתונים של ImagNet.
לוחצים על Download כדי להוריד את הארטיפקטים של המודל שנשמר.
בקטע Cloud Storage במסוף Google Cloud, בוחרים באפשרות CREATE.
נותנים שם לקטגוריה ובוחרים את האזור us-central1. לאחר מכן לוחצים על יצירה.
מעלים את המודל של TensorFlow Hub שהורדתם לקטגוריה. חשוב להסיר קודם את הקובץ.
הקטגוריה אמורה להיראות כך:
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 settings (הגדרות המודל), מציינים את הקונטיינר העדכני ביותר של TensorFlow שנוצר מראש. לאחר מכן בוחרים את הנתיב ב-Cloud Storage שבו אחסנתם את ארטיפקטים של המודל.
אפשר לדלג על הקטע הסבר.
לאחר מכן בוחרים באפשרות ייבוא.
אחרי הייבוא, המודל יופיע במרשם המודלים
6. פריסת מודל
במרשם המודלים, בוחרים את שלוש הנקודות בצד ימין של המודל ולוחצים על פריסה לנקודת קצה (endpoint).
בקטע הגדרה של נקודת הקצה, בוחרים באפשרות יצירת נקודת קצה חדשה ונותנים שם לנקודת הקצה.
בקטע Model settings, מגדירים את Maximum number of compute nodes לערך 1, את machine type לערך n1-standard-2
ומשאירים את כל שאר ההגדרות כפי שהן. לאחר מכן לוחצים על DEPLOY.
לאחר הפריסה, סטטוס הפריסה ישתנה ל-Deployed on Vertex AI.
7. הצגת תחזיות
פותחים את מחברת Workbench שיצרתם בשלבים של ההגדרה. במרכז האפליקציות, יוצרים notebook חדש של TensorFlow 2.
כדי לייבא את הספריות הנדרשות, מריצים את התא הבא
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}")
מספר הפרויקט מופיע בדף הבית של המסוף.
ומזהה נקודת הקצה בקטע Endpoints ב-Vertex AI.
בשלב הבא נבדוק את נקודת הקצה.
קודם כול, מורידים את התמונה הבאה ומעלים אותה למכונה.
פותחים את התמונה באמצעות 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: