Vertex AI Workbench: אימון מודל TensorFlow באמצעות נתונים מ-BigQuery

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

בשיעור ה-Lab הזה תלמדו איך להשתמש ב-Vertex AI Workbench לצורך ניתוח נתונים ואימון מודלים של למידת מכונה.

מה תלמדו

נסביר לכם איך:

  • יצירת מכונה של Vertex AI Workbench והגדרתה
  • שימוש במחבר BigQuery של Vertex AI Workbench
  • אימון מודל בליבה של Vertex AI Workbench

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

2. מבוא ל-Vertex AI

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

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

Vertex AI Workbench עוזר למשתמשים ליצור במהירות תהליכי עבודה מקצה לקצה שמבוססים על מסמכי notebook, באמצעות שילוב עמוק עם שירותי נתונים (כמו Dataproc, ‏ Dataflow, ‏ BigQuery ו-Dataplex) ו-Vertex AI. בעזרת ה-API הזה, מדעני הנתונים יכולים להתחבר לשירותי הנתונים של GCP, לנתח מערכי נתונים, להתנסות בשיטות שונות של בניית מודלים, לפרוס מודלים מאומנים בסביבת הייצור ולנהל את MLOps במהלך מחזור החיים של המודל.

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

בשיעור ה-Lab הזה נבחן את מערך הנתונים London Bicycles Hire. הנתונים האלה מכילים מידע על נסיעות באופניים מתוכנית שיתוף האופניים הציבורית של לונדון מאז 2011. נתחיל בחקר של מערך הנתונים הזה ב-BigQuery דרך מחבר BigQuery של Vertex AI Workbench. לאחר מכן, תטעינו את הנתונים ב-Jupyter Notebook באמצעות pandas ותאמינו מודל TensorFlow כדי לחזות את משך הנסיעה באופניים על סמך מועד הנסיעה והמרחק שהאדם נסע באופניים.

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

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

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

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

עוברים אל 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

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

service_account

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

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

enable_terminal

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

לאחר מכן, לוחצים על יצירה.

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

enable_terminal

5. התנסות במערך נתונים ב-BigQuery

במכונה של Vertex AI Workbench, עוברים לצד ימין ולוחצים על המחבר BigQuery in Notebooks.

מחבר BQ

מחבר BigQuery מאפשר לחקור בקלות מערכי נתונים של BigQuery ולשלוח שאילתות לגביהם. בנוסף למערכי הנתונים בפרויקט, אפשר לעיין במערכי נתונים בפרויקטים אחרים בלחיצה על הלחצן Add Project.

קוד אימות

בשיעור ה-Lab הזה תשתמשו בנתונים ממערכות הנתונים הציבוריות של BigQuery. גוללים למטה עד שמוצאים את מערך הנתונים london_bicycles. אפשר לראות שמערך הנתונים הזה כולל שתי טבלאות, cycle_hire ו-cycle_stations. נבחן כל אחת מהן.

london_bike_ds

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

cycle_hire_ds

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

cycle_hire_preview_ds

לאחר מכן מדביקים את הפקודה הבאה בעורך SQL ולוחצים על Submit Query.

SELECT
  start_station_name,
  end_station_name,
  IF(start_station_name = end_station_name,
    TRUE,
    FALSE) same_station,
  AVG(duration) AS avg_duration,
  COUNT(*) AS total_rides
FROM
  `bigquery-public-data.london_bicycles.cycle_hire`
GROUP BY
  start_station_name,
  end_station_name,
  same_station
ORDER BY
  total_rides DESC

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

journey_query_results

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

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

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

מעתיקים את השאילתה הבאה לעורך ה-SQL ולוחצים על 'שליחת שאילתה'. שימו לב שבתנאי האיחוד (join) יש שלוש טבלאות כי אנחנו צריכים להצטרף לטבלת התחנות פעמיים כדי לקבל את הרוחב/הקו רוחב גם של נקודת ההתחלה וגם של תחנת הסיום של המחזור.

WITH staging AS (
    SELECT
        STRUCT(
            start_stn.name,
            ST_GEOGPOINT(start_stn.longitude, start_stn.latitude) AS POINT,
            start_stn.docks_count,
            start_stn.install_date
        ) AS starting,
        STRUCT(
            end_stn.name,
            ST_GEOGPOINT(end_stn.longitude, end_stn.latitude) AS point,
            end_stn.docks_count,
            end_stn.install_date
        ) AS ending,
        STRUCT(
            rental_id,
            bike_id,
            duration, --seconds
            ST_DISTANCE(
                ST_GEOGPOINT(start_stn.longitude, start_stn.latitude),
                ST_GEOGPOINT(end_stn.longitude, end_stn.latitude)
            ) AS distance, --meters
            start_date,
            end_date
        ) AS bike
        FROM `bigquery-public-data.london_bicycles.cycle_stations` AS start_stn
        LEFT JOIN `bigquery-public-data.london_bicycles.cycle_hire` as b
        ON start_stn.id = b.start_station_id
        LEFT JOIN `bigquery-public-data.london_bicycles.cycle_stations` AS end_stn
        ON end_stn.id = b.end_station_id
        LIMIT 700000)

SELECT * from STAGING

6. אימון מודל למידת מכונה בליבה של TensorFlow

ל-Vertex AI Workbench יש שכבת תאימות למחשוב שמאפשרת להשיק ליבה (kernel) של TensorFlow, PySpark, R וכו', והכול ממכונת notebook אחת. בשיעור ה-Lab הזה תלמדו ליצור מסמך notebook באמצעות הליבה של TensorFlow.

יצירת DataFrame

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

copy_for_df

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

tf_kernel

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

# The following two lines are only necessary to run once.
# Comment out otherwise for speed-up.
from google.cloud.bigquery import Client, QueryJobConfig
client = Client()

query = """WITH staging AS (
    SELECT
        STRUCT(
            start_stn.name,
            ST_GEOGPOINT(start_stn.longitude, start_stn.latitude) AS POINT,
            start_stn.docks_count,
            start_stn.install_date
        ) AS starting,
        STRUCT(
            end_stn.name,
            ST_GEOGPOINT(end_stn.longitude, end_stn.latitude) AS point,
            end_stn.docks_count,
            end_stn.install_date
        ) AS ending,
        STRUCT(
            rental_id,
            bike_id,
            duration, --seconds
            ST_DISTANCE(
                ST_GEOGPOINT(start_stn.longitude, start_stn.latitude),
                ST_GEOGPOINT(end_stn.longitude, end_stn.latitude)
            ) AS distance, --meters
            start_date,
            end_date
        ) AS bike
        FROM `bigquery-public-data.london_bicycles.cycle_stations` AS start_stn
        LEFT JOIN `bigquery-public-data.london_bicycles.cycle_hire` as b 
        ON start_stn.id = b.start_station_id
        LEFT JOIN `bigquery-public-data.london_bicycles.cycle_stations` AS end_stn
        ON end_stn.id = b.end_station_id
        LIMIT 700000)

SELECT * from STAGING"""
job = client.query(query)
df = job.to_dataframe()

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

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

from datetime import datetime
import pandas as pd
import tensorflow as tf

מריצים את הקוד הבא כדי ליצור dataframe מופחת שמכיל רק את העמודות הנדרשות לחלק של ה-ML בתרגול הזה.

values = df['bike'].values
duration = list(map(lambda a: a['duration'], values))
distance = list(map(lambda a: a['distance'], values))
dates = list(map(lambda a: a['start_date'], values))
data = pd.DataFrame(data={'duration': duration, 'distance': distance, 'start_date':dates})
data = data.dropna()

העמודה start_date היא datetime ב-Python. אם תשתמשו בdatetime הזה ישירות במודל, תוכלו ליצור שתי תכונות חדשות שמציינות את היום בשבוע והשעה ביום שבה התרחשה טיול האופניים.

data['weekday'] = data['start_date'].apply(lambda a: a.weekday())
data['hour'] = data['start_date'].apply(lambda a: a.time().hour)
data = data.drop(columns=['start_date'])

לבסוף, ממירים את העמודה 'משך זמן' מניות לשניות כדי שיהיה קל יותר להבין אותה

data['duration'] = data['duration'].apply(lambda x:float(x / 60))

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

data.head()

data_head

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

# Use 80/20 train/eval split
train_size = int(len(data) * .8)
print ("Train size: %d" % train_size)
print ("Evaluation size: %d" % (len(data) - train_size))

# Split data into train and test sets
train_data = data[:train_size]
val_data = data[train_size:]

יצירת מודל TensorFlow

תיצורו מודל TensorFlow באמצעות Keras Functional API. כדי לעבד מראש את נתוני הקלט, עליכם להשתמש ב-API של שכבות עיבוד מראש של Keras.

פונקציית השירות הבאה תיצור tf.data.Dataset מ-Dataframe של pandas.

def df_to_dataset(dataframe, label, shuffle=True, batch_size=32):
  dataframe = dataframe.copy()
  labels = dataframe.pop(label)
  ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels))
  if shuffle:
    ds = ds.shuffle(buffer_size=len(dataframe))
  ds = ds.batch(batch_size)
  ds = ds.prefetch(batch_size)
  return ds

משתמשים בפונקציה שלמעלה כדי ליצור שני tf.data.Dataset, אחד לאימון ואחד לאימות. יכול להיות שיופיעו כמה אזהרות, אבל אפשר להתעלם מהן.

train_dataset = df_to_dataset(train_data, 'duration')
validation_dataset = df_to_dataset(val_data, 'duration')

במודל הזה תשתמשו בשכבות הבאות לעיבוד מראש:

  • שכבת הנירמול: מבצעת נירמול חכם של תכונות הקלט.
  • שכבת IntegerLookup: הופכת ערכים קטגוריאליים של מספרים שלמים לאינדקסים של מספרים שלמים.
  • שכבת CategoryEncoding: הופכת תכונות קטגוריות שלמים לייצוגים צפופים מסוג one-hot,‏ multi-hot או TF-IDF.

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

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

def get_normalization_layer(name, dataset):
  # Create a Normalization layer for our feature.
  normalizer = tf.keras.layers.Normalization(axis=None)

  # Prepare a Dataset that only yields our feature.
  feature_ds = dataset.map(lambda x, y: x[name])

  # Learn the statistics of the data.
  normalizer.adapt(feature_ds)

  return normalizer

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

def get_category_encoding_layer(name, dataset, dtype, max_tokens=None):
  index = tf.keras.layers.IntegerLookup(max_tokens=max_tokens)

  # Prepare a Dataset that only yields our feature
  feature_ds = dataset.map(lambda x, y: x[name])

  # Learn the set of possible values and assign them a fixed integer index.
  index.adapt(feature_ds)

  # Create a Discretization for our integer indices.
  encoder = tf.keras.layers.CategoryEncoding(num_tokens=index.vocabulary_size())

  # Apply one-hot encoding to our indices. The lambda function captures the
  # layer so we can use them, or include them in the functional model later.
  return lambda feature: encoder(index(feature))

בשלב הבא, יוצרים את החלק של העיבוד המקדים של המודל. קודם כול, יוצרים שכבה tf.keras.Input לכל אחת מהתכונות.

# Create a Keras input layer for each feature
numeric_col = tf.keras.Input(shape=(1,), name='distance')
hour_col = tf.keras.Input(shape=(1,), name='hour', dtype='int64')
weekday_col = tf.keras.Input(shape=(1,), name='weekday', dtype='int64')

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

all_inputs = []
encoded_features = []

# Pass 'distance' input to normalization layer
normalization_layer = get_normalization_layer('distance', train_dataset)
encoded_numeric_col = normalization_layer(numeric_col)
all_inputs.append(numeric_col)
encoded_features.append(encoded_numeric_col)

# Pass 'hour' input to category encoding layer
encoding_layer = get_category_encoding_layer('hour', train_dataset, dtype='int64')
encoded_hour_col = encoding_layer(hour_col)
all_inputs.append(hour_col)
encoded_features.append(encoded_hour_col)

# Pass 'weekday' input to category encoding layer
encoding_layer = get_category_encoding_layer('weekday', train_dataset, dtype='int64')
encoded_weekday_col = encoding_layer(weekday_col)
all_inputs.append(weekday_col)
encoded_features.append(encoded_weekday_col)

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

all_features = tf.keras.layers.concatenate(encoded_features)
x = tf.keras.layers.Dense(64, activation="relu")(all_features)
output = tf.keras.layers.Dense(1)(x)
model = tf.keras.Model(all_inputs, output)

לבסוף, מקמפלים את המודל.

model.compile(optimizer = tf.keras.optimizers.Adam(0.001),
              loss='mean_squared_logarithmic_error')

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

tf.keras.utils.plot_model(model, show_shapes=True, rankdir="LR")

keras_model

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

נלמד את המערכת במשך 1 עידן כדי לוודא שהקוד פועל.

model.fit(train_dataset, validation_data = validation_dataset, epochs = 1)

אימון מודל עם GPU

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

כדי לשנות את פרופיל החומרה, לוחצים על סוג המכונה בפינה השמאלית העליונה ובוחרים באפשרות Modify hardware (שינוי החומרה).

modify_hardware

בוחרים באפשרות Attach GPUs (צירוף כרטיסי GPU) ובוחרים ב-GPU מסוג NVIDIA T4 Tensor Core.

add_gpu

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

model.fit(train_dataset, validation_data = validation_dataset, epochs = 5)

🎉 מזל טוב! 🎉

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

  • ניתוח נתונים ב-BigQuery
  • שימוש בלקוח BigQuery כדי לטעון נתונים ל-Python
  • אימון מודל TensorFlow באמצעות שכבות עיבוד מראש של Keras ו-GPU

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

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

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

delete