التوصية بالأفلام: الترتيب

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

غالبًا ما تتكون أنظمة التوصية في العالم الحقيقي من مرحلتين:

  1. مرحلة الاسترجاع مسؤولة عن اختيار مجموعة أولية من مئات المرشحين من بين جميع المرشحين المحتملين. الهدف الرئيسي من هذا النموذج هو التخلص بكفاءة من جميع المرشحين الذين لا يهتم بهم المستخدم. ولأن نموذج الاسترجاع قد يتعامل مع ملايين المرشحين ، يجب أن يكون فعالاً من الناحية الحسابية.
  2. تأخذ مرحلة الترتيب مخرجات نموذج الاسترجاع وتضبطها لتحديد أفضل مجموعة ممكنة من التوصيات. وتتمثل مهمتها في تضييق نطاق مجموعة العناصر التي قد يهتم بها المستخدم إلى قائمة مختصرة بالمرشحين المحتملين.

سنركز على المرحلة الثانية ، الترتيب. إذا كنت مهتما في مرحلة الاسترجاع، إلقاء نظرة على موقعنا على استرجاع البرنامج التعليمي.

في هذا البرنامج التعليمي ، سنقوم بما يلي:

  1. احصل على بياناتنا وقسمها إلى مجموعة تدريب واختبار.
  2. تطبيق نموذج الترتيب.
  3. تناسبها وتقييمها.

الواردات

دعنا أولاً نخرج وارداتنا من الطريق.

pip install -q tensorflow-recommenders
pip install -q --upgrade tensorflow-datasets
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

تحضير مجموعة البيانات

ونحن في طريقنا إلى استخدام نفس البيانات مثل استرجاع البرنامج التعليمي. هذه المرة ، سنحتفظ أيضًا بالتقييمات: هذه هي الأهداف التي نحاول توقعها.

ratings = tfds.load("movielens/100k-ratings", split="train")

ratings = ratings.map(lambda x: {
    "movie_title": x["movie_title"],
    "user_id": x["user_id"],
    "user_rating": x["user_rating"]
})
2021-10-02 11:04:25.388548: E tensorflow/stream_executor/cuda/cuda_driver.cc:271] failed call to cuInit: CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected

كما في السابق ، سنقسم البيانات عن طريق وضع 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 = ratings.batch(1_000_000).map(lambda x: x["movie_title"])
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)))

تنفيذ النموذج

بنيان

لا تواجه نماذج الترتيب نفس قيود الكفاءة التي تواجهها نماذج الاسترجاع ، ولذا فإننا نتمتع بقدر أكبر قليلاً من الحرية في اختيارنا للهياكل.

النموذج المكون من طبقات كثيفة متعددة مكدسة هو بنية شائعة نسبيًا لمهام الترتيب. يمكننا تنفيذه على النحو التالي:

class RankingModel(tf.keras.Model):

  def __init__(self):
    super().__init__()
    embedding_dimension = 32

    # Compute embeddings for users.
    self.user_embeddings = tf.keras.Sequential([
      tf.keras.layers.StringLookup(
        vocabulary=unique_user_ids, mask_token=None),
      tf.keras.layers.Embedding(len(unique_user_ids) + 1, embedding_dimension)
    ])

    # Compute embeddings for movies.
    self.movie_embeddings = 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)
    ])

    # Compute predictions.
    self.ratings = tf.keras.Sequential([
      # Learn multiple dense layers.
      tf.keras.layers.Dense(256, activation="relu"),
      tf.keras.layers.Dense(64, activation="relu"),
      # Make rating predictions in the final layer.
      tf.keras.layers.Dense(1)
  ])

  def call(self, inputs):

    user_id, movie_title = inputs

    user_embedding = self.user_embeddings(user_id)
    movie_embedding = self.movie_embeddings(movie_title)

    return self.ratings(tf.concat([user_embedding, movie_embedding], axis=1))

يأخذ هذا النموذج معرفات المستخدم وعناوين الأفلام ، ويخرج تقييمًا متوقعًا:

RankingModel()((["42"], ["One Flew Over the Cuckoo's Nest (1975)"]))
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'list'> input: ['42']
Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'list'> input: ['42']
Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'list'> input: ["One Flew Over the Cuckoo's Nest (1975)"]
Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'list'> input: ["One Flew Over the Cuckoo's Nest (1975)"]
Consider rewriting this model with the Functional API.
<tf.Tensor: shape=(1, 1), dtype=float32, numpy=array([[0.03740937]], dtype=float32)>

الخسارة والمقاييس

المكون التالي هو الخسارة المستخدمة لتدريب نموذجنا. يحتوي TFRS على العديد من طبقات ومهام الخسارة لتسهيل ذلك.

في هذه الحالة، فإننا سوف الاستفادة من Ranking وجوه مهمة: مجمع الراحة التي حزم معا وظيفة الخسارة وحساب المتري.

سنستخدم ذلك جنبا إلى جنب مع MeanSquaredError فقدان Keras من أجل التنبؤ التصنيف.

task = tfrs.tasks.Ranking(
  loss = tf.keras.losses.MeanSquaredError(),
  metrics=[tf.keras.metrics.RootMeanSquaredError()]
)

المهمة نفسها عبارة عن طبقة Keras تأخذ الحقيقة والمتنبأ بها كوسيطات ، وتعيد الخسارة المحسوبة. سنستخدم ذلك لتنفيذ حلقة تدريب النموذج.

النموذج الكامل

يمكننا الآن تجميع كل ذلك معًا في نموذج. معدلات الخصوبة الكلية الكشف عن فئة الطراز الأساسي ( tfrs.models.Model ) الذي يبسط نماذج مبانى: كل ما عليك القيام به هو لإعداد المكونات في __init__ الطريقة، وتنفيذ compute_loss طريقة، مع الأخذ في ملامح الخام وإرجاع قيمة الخسارة .

سيهتم النموذج الأساسي بعد ذلك بإنشاء حلقة التدريب المناسبة لتناسب نموذجنا.

class MovielensModel(tfrs.models.Model):

  def __init__(self):
    super().__init__()
    self.ranking_model: tf.keras.Model = RankingModel()
    self.task: tf.keras.layers.Layer = tfrs.tasks.Ranking(
      loss = tf.keras.losses.MeanSquaredError(),
      metrics=[tf.keras.metrics.RootMeanSquaredError()]
    )

  def call(self, features: Dict[str, tf.Tensor]) -> tf.Tensor:
    return self.ranking_model(
        (features["user_id"], features["movie_title"]))

  def compute_loss(self, features: Dict[Text, tf.Tensor], training=False) -> tf.Tensor:
    labels = features.pop("user_rating")

    rating_predictions = self(features)

    # The task computes the loss and the metrics.
    return self.task(labels=labels, predictions=rating_predictions)

التركيب والتقييم

بعد تحديد النموذج ، يمكننا استخدام إجراءات تركيب وتقييم Keras القياسية لملاءمة النموذج وتقييمه.

لنبدأ أولاً بإنشاء مثيل للنموذج.

model = MovielensModel()
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 [==============================] - 2s 26ms/step - root_mean_squared_error: 2.1718 - loss: 4.3303 - regularization_loss: 0.0000e+00 - total_loss: 4.3303
Epoch 2/3
10/10 [==============================] - 0s 8ms/step - root_mean_squared_error: 1.1227 - loss: 1.2602 - regularization_loss: 0.0000e+00 - total_loss: 1.2602
Epoch 3/3
10/10 [==============================] - 0s 8ms/step - root_mean_squared_error: 1.1162 - loss: 1.2456 - regularization_loss: 0.0000e+00 - total_loss: 1.2456
<keras.callbacks.History at 0x7f28389eaa90>

بينما يتدرب النموذج ، تتناقص الخسارة ويتحسن مقياس RMSE.

أخيرًا ، يمكننا تقييم نموذجنا على مجموعة الاختبار:

model.evaluate(cached_test, return_dict=True)
5/5 [==============================] - 2s 14ms/step - root_mean_squared_error: 1.1108 - loss: 1.2287 - regularization_loss: 0.0000e+00 - total_loss: 1.2287
{'root_mean_squared_error': 1.1108061075210571,
 'loss': 1.2062578201293945,
 'regularization_loss': 0,
 'total_loss': 1.2062578201293945}

كلما انخفض مقياس RMSE ، كان نموذجنا أكثر دقة في التنبؤ بالتقييمات.

اختبار نموذج الترتيب

يمكننا الآن اختبار نموذج الترتيب من خلال حساب تنبؤات مجموعة من الأفلام ثم ترتيب هذه الأفلام بناءً على التوقعات:

test_ratings = {}
test_movie_titles = ["M*A*S*H (1970)", "Dances with Wolves (1990)", "Speed (1994)"]
for movie_title in test_movie_titles:
  test_ratings[movie_title] = model({
      "user_id": np.array(["42"]),
      "movie_title": np.array([movie_title])
  })

print("Ratings:")
for title, score in sorted(test_ratings.items(), key=lambda x: x[1], reverse=True):
  print(f"{title}: {score}")
Ratings:
M*A*S*H (1970): [[3.584712]]
Dances with Wolves (1990): [[3.551556]]
Speed (1994): [[3.5215874]]

تصدير للتقديم

يمكن تصدير النموذج بسهولة للتقديم:

tf.saved_model.save(model, "export")
2021-10-02 11:04:38.235611: 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 ranking_1_layer_call_and_return_conditional_losses, ranking_1_layer_call_fn, ranking_1_layer_call_fn, ranking_1_layer_call_and_return_conditional_losses, ranking_1_layer_call_and_return_conditional_losses while saving (showing 5 of 5). These functions will not be directly callable after loading.
INFO:tensorflow:Assets written to: export/assets
INFO:tensorflow:Assets written to: export/assets

يمكننا الآن إعادة تحميله وإجراء التنبؤات:

loaded = tf.saved_model.load("export")

loaded({"user_id": np.array(["42"]), "movie_title": ["Speed (1994)"]}).numpy()
array([[3.5215874]], dtype=float32)

الخطوات التالية

يمنحنا النموذج أعلاه بداية جيدة نحو بناء نظام تصنيف.

بالطبع ، يتطلب إنشاء نظام تصنيف عملي مزيدًا من الجهد.

في معظم الحالات ، يمكن تحسين نموذج الترتيب بشكل كبير باستخدام المزيد من الميزات بدلاً من مجرد معرفات المستخدم والمرشح. لمعرفة كيفية القيام بذلك، لديها نظرة على الجانب ميزات البرنامج التعليمي.

من الضروري أيضًا الفهم الدقيق للأهداف التي تستحق التحسين. للبدء في بناء المزكي أن يحسن أهداف متعددة، إلقاء نظرة على موقعنا على تعدد المهام البرنامج التعليمي.