המלצה על סרטים: אחזור

הצג באתר TensorFlow.org הפעל בגוגל קולאב צפה במקור ב-GitHub הורד מחברת

מערכות ממליצים בעולם האמיתי מורכבות לרוב משני שלבים:

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

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

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

  1. מודל שאילתה המחשוב את ייצוג השאילתה (בדרך כלל וקטור הטמעת ממדים קבועים) באמצעות תכונות שאילתה.
  2. מודל מועמד המחשוב את ייצוג המועמד (וקטור בגודל שווה) תוך שימוש בתכונות המועמד

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

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

אנחנו הולכים ל:

  1. קבל את הנתונים שלנו ופצל אותם לסט הדרכה ומבחנים.
  2. הטמעת מודל אחזור.
  3. התאם והעריך את זה.
  4. ייצא אותו להגשה יעילה על ידי בניית אינדקס השכנים הקרובים (ANN) משוער.

מערך הנתונים

Movielens במערך הוא במערך הקלאסי מן GroupLens Research Group באוניברסיטת מינסוטה. הוא מכיל קבוצה של דירוגים שניתנו לסרטים על ידי קבוצה של משתמשים, ומהווה סוס עבודה של מחקר מערכת ממליצים.

ניתן לטפל בנתונים בשתי דרכים:

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

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

יבוא

קודם כל נסלק את היבוא שלנו מהדרך.

pip install -q tensorflow-recommenders
pip install -q --upgrade tensorflow-datasets
pip install -q scann
import os
import pprint
import tempfile

from typing import Dict, Text

import numpy as np
import tensorflow as tf
import tensorflow_datasets as tfds
import tensorflow_recommenders as tfrs

הכנת מערך הנתונים

קודם כל נתבונן בנתונים.

אנו משתמשים במערך MovieLens מ מערכי נתונים Tensorflow . טוען movielens/100k_ratings מניב tf.data.Dataset אובייקט המכיל את נתוני הרייטינג וטעינה movielens/100k_movies מניב tf.data.Dataset אובייקט המכיל את נתוני סרטים בלבד.

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

# Ratings data.
ratings = tfds.load("movielens/100k-ratings", split="train")
# Features of all the available movies.
movies = tfds.load("movielens/100k-movies", split="train")
2021-10-02 11:05:34.633747: E tensorflow/stream_executor/cuda/cuda_driver.cc:271] failed call to cuInit: CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected

מערך הנתונים של הדירוגים מחזיר מילון של מזהה סרט, מזהה משתמש, הדירוג שהוקצה, חותמת זמן, מידע על סרט ופרטי משתמש:

for x in ratings.take(1).as_numpy_iterator():
  pprint.pprint(x)
{'bucketized_user_age': 45.0,
 'movie_genres': array([7]),
 'movie_id': b'357',
 'movie_title': b"One Flew Over the Cuckoo's Nest (1975)",
 'raw_user_age': 46.0,
 'timestamp': 879024327,
 'user_gender': True,
 'user_id': b'138',
 'user_occupation_label': 4,
 'user_occupation_text': b'doctor',
 'user_rating': 4.0,
 'user_zip_code': b'53211'}
2021-10-02 11:05:35.718641: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset  will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.

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

for x in movies.take(1).as_numpy_iterator():
  pprint.pprint(x)
{'movie_genres': array([4]),
 'movie_id': b'1681',
 'movie_title': b'You So Crazy (1994)'}
2021-10-02 11:05:35.893098: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset  will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.

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

אנחנו שומרים רק את user_id , ו movie_title שדות נתון.

ratings = ratings.map(lambda x: {
    "movie_title": x["movie_title"],
    "user_id": x["user_id"],
})
movies = movies.map(lambda x: x["movie_title"])

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

עם זאת, בדוגמה הפשוטה הזו, הבה נשתמש בפיצול אקראי, ונשים 80% מהדירוגים בערכת הרכבות ו-20% בערכת הבדיקה.

tf.random.set_seed(42)
shuffled = ratings.shuffle(100_000, seed=42, reshuffle_each_iteration=False)

train = shuffled.take(80_000)
test = shuffled.skip(80_000).take(20_000)

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

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

movie_titles = movies.batch(1_000)
user_ids = ratings.batch(1_000_000).map(lambda x: x["user_id"])

unique_movie_titles = np.unique(np.concatenate(list(movie_titles)))
unique_user_ids = np.unique(np.concatenate(list(user_ids)))

unique_movie_titles[:10]
array([b"'Til There Was You (1997)", b'1-900 (1994)',
       b'101 Dalmatians (1996)', b'12 Angry Men (1957)', b'187 (1997)',
       b'2 Days in the Valley (1996)',
       b'20,000 Leagues Under the Sea (1954)',
       b'2001: A Space Odyssey (1968)',
       b'3 Ninjas: High Noon At Mega Mountain (1998)',
       b'39 Steps, The (1935)'], dtype=object)

יישום מודל

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

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

מגדל השאילתות

נתחיל עם מגדל השאילתות.

הצעד הראשון הוא להחליט על הממדיות של השאילתה ועל ייצוגי המועמדים:

embedding_dimension = 32

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

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

user_model = tf.keras.Sequential([
  tf.keras.layers.StringLookup(
      vocabulary=unique_user_ids, mask_token=None),
  # We add an additional embedding to account for unknown tokens.
  tf.keras.layers.Embedding(len(unique_user_ids) + 1, embedding_dimension)
])

מודל פשוט ככה מתאים בדיוק קלאסיקה פרוק מטריקס גישה. בעוד הגדרת תת מחלקה של tf.keras.Model עבור מודל פשוט זה יכול להיות מוגזם, נוכל להחיל אותה בקלות מודל מורכב באופן שרירותי באמצעות רכיבים סטנדרטיים Keras, כל עוד אנו להחזיר embedding_dimension פלט -wide בסוף.

מגדל המועמדים

אנחנו יכולים לעשות את אותו הדבר עם מגדל המועמדים.

movie_model = tf.keras.Sequential([
  tf.keras.layers.StringLookup(
      vocabulary=unique_movie_titles, mask_token=None),
  tf.keras.layers.Embedding(len(unique_movie_titles) + 1, embedding_dimension)
])

מדדים

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

לשם כך, אנו יכולים להשתמש tfrs.metrics.FactorizedTopK מטרי. למדד יש ארגומנט נדרש אחד: מערך הנתונים של מועמדים המשמשים כשליליים מרומזים להערכה.

במקרה שלנו, זה movies במערך, הוסב שיבוצים באמצעות מודל הסרט שלנו:

metrics = tfrs.metrics.FactorizedTopK(
  candidates=movies.batch(128).map(movie_model)
)

הֶפסֵד

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

במקרה זה, נצטרך לעשות שימוש Retrieval חפץ משימה: נוחות מעטפת חבילות שיחד פונקצית ההפסד ואת חישוב מטרי:

task = tfrs.tasks.Retrieval(
  metrics=metrics
)

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

הדגם המלא

כעת נוכל לחבר את הכל יחד למודל. TFRs חושף מחלקה מודל בסיס ( tfrs.models.Model ) אשר מייעל מודלים לבנייה: כול מה שאנחנו צריכים לעשות זה להגדיר את רכיבי __init__ השיטה, וליישם את compute_loss השיטה, לוקחים את תכונות הגלם והחזרת ערך פסד .

לאחר מכן מודל הבסיס ידאג ליצור את לולאת האימון המתאימה שתתאים לדגם שלנו.

class MovielensModel(tfrs.Model):

  def __init__(self, user_model, movie_model):
    super().__init__()
    self.movie_model: tf.keras.Model = movie_model
    self.user_model: tf.keras.Model = user_model
    self.task: tf.keras.layers.Layer = task

  def compute_loss(self, features: Dict[Text, tf.Tensor], training=False) -> tf.Tensor:
    # We pick out the user features and pass them into the user model.
    user_embeddings = self.user_model(features["user_id"])
    # And pick out the movie features and pass them into the movie model,
    # getting embeddings back.
    positive_movie_embeddings = self.movie_model(features["movie_title"])

    # The task computes the loss and the metrics.
    return self.task(user_embeddings, positive_movie_embeddings)

tfrs.Model במחלקת הבסיס היא בכיתת נוחות פשוט: היא מאפשרת לנו לחשב הן פסדי הדרכת מבחן באותה השיטה.

מתחת למכסה המנוע, זה עדיין דגם קרס רגיל. אתה יכול להשיג את אותה פונקציונליות ידי בירושה מן tf.keras.Model ואת דריסת train_step ו test_step פונקציות (ראה המדריך לפרטים נוספים):

class NoBaseClassMovielensModel(tf.keras.Model):

  def __init__(self, user_model, movie_model):
    super().__init__()
    self.movie_model: tf.keras.Model = movie_model
    self.user_model: tf.keras.Model = user_model
    self.task: tf.keras.layers.Layer = task

  def train_step(self, features: Dict[Text, tf.Tensor]) -> tf.Tensor:

    # Set up a gradient tape to record gradients.
    with tf.GradientTape() as tape:

      # Loss computation.
      user_embeddings = self.user_model(features["user_id"])
      positive_movie_embeddings = self.movie_model(features["movie_title"])
      loss = self.task(user_embeddings, positive_movie_embeddings)

      # Handle regularization losses as well.
      regularization_loss = sum(self.losses)

      total_loss = loss + regularization_loss

    gradients = tape.gradient(total_loss, self.trainable_variables)
    self.optimizer.apply_gradients(zip(gradients, self.trainable_variables))

    metrics = {metric.name: metric.result() for metric in self.metrics}
    metrics["loss"] = loss
    metrics["regularization_loss"] = regularization_loss
    metrics["total_loss"] = total_loss

    return metrics

  def test_step(self, features: Dict[Text, tf.Tensor]) -> tf.Tensor:

    # Loss computation.
    user_embeddings = self.user_model(features["user_id"])
    positive_movie_embeddings = self.movie_model(features["movie_title"])
    loss = self.task(user_embeddings, positive_movie_embeddings)

    # Handle regularization losses as well.
    regularization_loss = sum(self.losses)

    total_loss = loss + regularization_loss

    metrics = {metric.name: metric.result() for metric in self.metrics}
    metrics["loss"] = loss
    metrics["regularization_loss"] = regularization_loss
    metrics["total_loss"] = total_loss

    return metrics

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

התאמה והערכה

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

בואו נראה תחילה את הדגם.

model = MovielensModel(user_model, movie_model)
model.compile(optimizer=tf.keras.optimizers.Adagrad(learning_rate=0.1))

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

cached_train = train.shuffle(100_000).batch(8192).cache()
cached_test = test.batch(4096).cache()

לאחר מכן אמן את הדגם:

model.fit(cached_train, epochs=3)
Epoch 1/3
10/10 [==============================] - 6s 302ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0011 - factorized_top_k/top_5_categorical_accuracy: 0.0094 - factorized_top_k/top_10_categorical_accuracy: 0.0203 - factorized_top_k/top_50_categorical_accuracy: 0.1001 - factorized_top_k/top_100_categorical_accuracy: 0.1772 - loss: 69885.1129 - regularization_loss: 0.0000e+00 - total_loss: 69885.1129
Epoch 2/3
10/10 [==============================] - 3s 286ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0029 - factorized_top_k/top_5_categorical_accuracy: 0.0186 - factorized_top_k/top_10_categorical_accuracy: 0.0376 - factorized_top_k/top_50_categorical_accuracy: 0.1689 - factorized_top_k/top_100_categorical_accuracy: 0.2923 - loss: 67523.3707 - regularization_loss: 0.0000e+00 - total_loss: 67523.3707
Epoch 3/3
10/10 [==============================] - 3s 269ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0036 - factorized_top_k/top_5_categorical_accuracy: 0.0224 - factorized_top_k/top_10_categorical_accuracy: 0.0459 - factorized_top_k/top_50_categorical_accuracy: 0.1880 - factorized_top_k/top_100_categorical_accuracy: 0.3162 - loss: 66302.9609 - regularization_loss: 0.0000e+00 - total_loss: 66302.9609
<keras.callbacks.History at 0x7f560e5ea090>

אם אתה מעוניין לעקוב אחר תהליך האימון עם TensorBoard, אתה יכול להוסיף התקשרות TensorBoard כדי להתאים (), ולאחר מכן להתחיל TensorBoard באמצעות %tensorboard --logdir logs/fit . עיין בתיעוד TensorBoard לפרטים נוספים.

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

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

לבסוף, נוכל להעריך את המודל שלנו במערך הבדיקות:

model.evaluate(cached_test, return_dict=True)
5/5 [==============================] - 2s 149ms/step - factorized_top_k/top_1_categorical_accuracy: 7.5000e-04 - factorized_top_k/top_5_categorical_accuracy: 0.0099 - factorized_top_k/top_10_categorical_accuracy: 0.0226 - factorized_top_k/top_50_categorical_accuracy: 0.1245 - factorized_top_k/top_100_categorical_accuracy: 0.2324 - loss: 31079.0635 - regularization_loss: 0.0000e+00 - total_loss: 31079.0635
{'factorized_top_k/top_1_categorical_accuracy': 0.000750000006519258,
 'factorized_top_k/top_5_categorical_accuracy': 0.009850000031292439,
 'factorized_top_k/top_10_categorical_accuracy': 0.02264999970793724,
 'factorized_top_k/top_50_categorical_accuracy': 0.12449999898672104,
 'factorized_top_k/top_100_categorical_accuracy': 0.23235000669956207,
 'loss': 28244.771484375,
 'regularization_loss': 0,
 'total_loss': 28244.771484375}

ביצועי ערכת המבחנים גרועים בהרבה מביצועי אימון. זה נובע משני גורמים:

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

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

להכין תחזיות

עכשיו, כשיש לנו מודל, נרצה להיות מסוגלים לעשות תחזיות. אנו יכולים להשתמש tfrs.layers.factorized_top_k.BruteForce השכבה לעשות זאת.

# Create a model that takes in raw query features, and
index = tfrs.layers.factorized_top_k.BruteForce(model.user_model)
# recommends movies out of the entire movies dataset.
index.index_from_dataset(
  tf.data.Dataset.zip((movies.batch(100), movies.batch(100).map(model.movie_model)))
)

# Get recommendations.
_, titles = index(tf.constant(["42"]))
print(f"Recommendations for user 42: {titles[0, :3]}")
Recommendations for user 42: [b'Bridges of Madison County, The (1995)'
 b'Father of the Bride Part II (1995)' b'Rudy (1993)']

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

הגשת דגם

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

במודל אחזור של שני מגדלים, להגשה יש שני מרכיבים:

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

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

כדי להטמיע מודל כזה, אנחנו פשוט לייצא את BruteForce השכבה שיצרנו לעיל:

# Export the query model.
with tempfile.TemporaryDirectory() as tmp:
  path = os.path.join(tmp, "model")

  # Save the index.
  tf.saved_model.save(index, path)

  # Load it back; can also be done in TensorFlow Serving.
  loaded = tf.saved_model.load(path)

  # Pass a user id in, get top predicted movie titles back.
  scores, titles = loaded(["42"])

  print(f"Recommendations: {titles[0][:3]}")
2021-10-02 11:05:54.109254: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
WARNING:absl:Found untraced functions such as query_with_exclusions while saving (showing 1 of 1). These functions will not be directly callable after loading.
INFO:tensorflow:Assets written to: /tmp/tmp7otg6id7/model/assets
INFO:tensorflow:Assets written to: /tmp/tmp7otg6id7/model/assets
Recommendations: [b'Bridges of Madison County, The (1995)'
 b'Father of the Bride Part II (1995)' b'Rudy (1993)']

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

לשם כך, אנו יכולים להשתמש scann החבילה. זוהי תלות אופציונלית של TFRs, ואנחנו מותקנים אותו בנפרד בתחילת הדרכה זו על ידי התקשרות !pip install -q scann .

לאחר ההתקנה אנו יכולים להשתמש TFRs ScaNN השכבה:

scann_index = tfrs.layers.factorized_top_k.ScaNN(model.user_model)
scann_index.index_from_dataset(
  tf.data.Dataset.zip((movies.batch(100), movies.batch(100).map(model.movie_model)))
)
<tensorflow_recommenders.layers.factorized_top_k.ScaNN at 0x7f560caffc10>

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

# Get recommendations.
_, titles = scann_index(tf.constant(["42"]))
print(f"Recommendations for user 42: {titles[0, :3]}")
Recommendations for user 42: [b'Sleepless in Seattle (1993)' b'Father of the Bride Part II (1995)'
 b'Hunchback of Notre Dame, The (1996)']

ייצוא זה להגשה הוא קל כמו ייצוא BruteForce השכבה:

# Export the query model.
with tempfile.TemporaryDirectory() as tmp:
  path = os.path.join(tmp, "model")

  # Save the index.
  tf.saved_model.save(
      index,
      path,
      options=tf.saved_model.SaveOptions(namespace_whitelist=["Scann"])
  )

  # Load it back; can also be done in TensorFlow Serving.
  loaded = tf.saved_model.load(path)

  # Pass a user id in, get top predicted movie titles back.
  scores, titles = loaded(["42"])

  print(f"Recommendations: {titles[0][:3]}")
WARNING:absl:Found untraced functions such as query_with_exclusions while saving (showing 1 of 1). These functions will not be directly callable after loading.
INFO:tensorflow:Assets written to: /tmp/tmp_rde8grm/model/assets
INFO:tensorflow:Assets written to: /tmp/tmp_rde8grm/model/assets
Recommendations: [b'Bridges of Madison County, The (1995)'
 b'Father of the Bride Part II (1995)' b'Rudy (1993)']

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

המלצה בין פריט לפריט

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

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

הצעדים הבאים

בכך מסתיים מערך האחזור.

כדי להרחיב על מה שמוצג כאן, עיין ב:

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