رتبه بندی لیست بندی

مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در GitHub دانلود دفترچه یادداشت

در آموزش پایه رتبه بندی ، ما یک مدل است که می توانید رتبه بندی برای جفت کاربران / فیلم پیش بینی آموزش داده است. این مدل برای به حداقل رساندن میانگین مربعات خطای رتبه‌بندی‌های پیش‌بینی‌شده آموزش داده شد.

با این حال، بهینه سازی پیش بینی های مدل بر روی فیلم های فردی لزوما بهترین روش برای آموزش مدل های رتبه بندی نیست. برای پیش‌بینی امتیازات با دقت بالا، نیازی به مدل‌های رتبه‌بندی نداریم. درعوض، ما بیشتر به توانایی مدل برای تولید فهرستی از موارد سفارش‌داده‌شده که با سفارش ترجیحی کاربر مطابقت دارد اهمیت می‌دهیم.

به‌جای بهینه‌سازی پیش‌بینی‌های مدل بر روی جفت‌های پرس و جو/موارد منفرد، می‌توانیم رتبه‌بندی مدل را از یک لیست به عنوان یک کل بهینه کنیم. این روش listwise رتبه بندی نامیده می شود.

در این آموزش از TensorFlow Recommenders برای ساخت مدل های رتبه بندی لیستی استفاده خواهیم کرد. برای این کار، ما استفاده از رتبه بندی تلفات و معیارهای ارائه شده توسط را TensorFlow رتبه بندی ، بسته بندی TensorFlow که در آن بر یادگیری به رتبه .

مقدماتی

اگر TensorFlow رتبه بندی است در محیط زمان اجرا شما در دسترس نیست، شما می توانید آن را با استفاده از نصب pip :

pip install -q tensorflow-recommenders
pip install -q --upgrade tensorflow-datasets
pip install -q tensorflow-ranking

سپس می توانیم تمام بسته های لازم را وارد کنیم:

import pprint

import numpy as np
import tensorflow as tf
import tensorflow_datasets as tfds
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/pkg_resources/__init__.py:119: PkgResourcesDeprecationWarning: 0.18ubuntu0.18.04.1 is an invalid version and will not be supported in a future release
  PkgResourcesDeprecationWarning,
import tensorflow_ranking as tfr
import tensorflow_recommenders as tfrs
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_addons/utils/ensure_tf_install.py:67: UserWarning: Tensorflow Addons supports using Python ops for all Tensorflow versions above or equal to 2.4.0 and strictly below 2.7.0 (nightly versions are not supported). 
 The versions of TensorFlow you are currently using is 2.7.0 and is not supported. 
Some things might work, some things might not.
If you were to encounter a bug, do not file an issue.
If you want to make sure you're using a tested and supported configuration, either change the TensorFlow version or the TensorFlow Addons's version. 
You can find the compatibility matrix in TensorFlow Addon's readme:
https://github.com/tensorflow/addons
  UserWarning,

ما به استفاده از مجموعه داده MovieLens 100K ادامه خواهیم داد. مانند قبل، مجموعه داده ها را بارگذاری می کنیم و فقط شناسه کاربر، عنوان فیلم و ویژگی های رتبه بندی کاربر را برای این آموزش نگه می داریم. ما همچنین خانه داری را انجام می دهیم تا واژگان خود را آماده کنیم.

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

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

unique_movie_titles = np.unique(np.concatenate(list(movies.batch(1000))))
unique_user_ids = np.unique(np.concatenate(list(ratings.batch(1_000).map(
    lambda x: x["user_id"]))))

پیش پردازش داده ها

با این حال، ما نمی توانیم مستقیماً از مجموعه داده MovieLens برای بهینه سازی لیست استفاده کنیم. برای انجام بهینه‌سازی فهرستی، باید به فهرستی از فیلم‌هایی که هر کاربر رتبه‌بندی کرده است، دسترسی داشته باشیم، اما هر نمونه در مجموعه داده MovieLens 100K فقط دارای امتیاز یک فیلم است.

برای دور زدن این موضوع، مجموعه داده را طوری تغییر می دهیم که هر مثال حاوی شناسه کاربری و لیستی از فیلم های رتبه بندی شده توسط آن کاربر باشد. برخی از فیلم‌های موجود در فهرست بالاتر از سایرین قرار خواهند گرفت. هدف مدل ما انجام پیش‌بینی‌هایی است که با این ترتیب مطابقت دارند.

برای انجام این کار، ما استفاده از tfrs.examples.movielens.movielens_to_listwise تابع کمکی. مجموعه داده MovieLens 100K را می گیرد و مجموعه داده ای حاوی نمونه های لیست همانطور که در بالا توضیح داده شد تولید می کند. جزئیات پیاده سازی را می توان در یافت کد منبع .

tf.random.set_seed(42)

# Split between train and tests sets, as before.
shuffled = ratings.shuffle(100_000, seed=42, reshuffle_each_iteration=False)

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

# We sample 50 lists for each user for the training data. For each list we
# sample 5 movies from the movies the user rated.
train = tfrs.examples.movielens.sample_listwise(
    train,
    num_list_per_user=50,
    num_examples_per_list=5,
    seed=42
)
test = tfrs.examples.movielens.sample_listwise(
    test,
    num_list_per_user=1,
    num_examples_per_list=5,
    seed=42
)

می‌توانیم نمونه‌ای را از داده‌های آموزشی بررسی کنیم. این مثال شامل شناسه کاربری، لیستی از 10 شناسه فیلم و رتبه بندی آنها توسط کاربر است.

for example in train.take(1):
  pprint.pprint(example)
{'movie_title': <tf.Tensor: shape=(5,), dtype=string, numpy=
array([b'Postman, The (1997)', b'Liar Liar (1997)', b'Contact (1997)',
       b'Welcome To Sarajevo (1997)',
       b'I Know What You Did Last Summer (1997)'], dtype=object)>,
 'user_id': <tf.Tensor: shape=(), dtype=string, numpy=b'681'>,
 'user_rating': <tf.Tensor: shape=(5,), dtype=float32, numpy=array([4., 5., 1., 4., 1.], dtype=float32)>}

تعریف مدل

ما همان مدل را با سه ضرر متفاوت آموزش خواهیم داد:

  • خطای میانگین مربعات،
  • از دست دادن لولا به صورت جفتی و
  • یک لیست از دست دادن ListMLE.

این سه ضرر مربوط به بهینه سازی نقطه ای، زوجی و فهرستی است.

برای ارزیابی مدل ما با استفاده از نرمال با تخفیف افزایش تجمعی (NDCG) . NDCG یک رتبه بندی پیش بینی شده را با جمع آوری وزنی از امتیاز واقعی هر نامزد اندازه گیری می کند. رتبه بندی فیلم هایی که توسط مدل رتبه پایین تری دارند، تخفیف بیشتری خواهند داشت. در نتیجه، یک مدل خوب که فیلم‌های با رتبه بالا را در بالاترین رتبه قرار می‌دهد، نتیجه NDCG بالایی خواهد داشت. از آنجایی که این معیار موقعیت رتبه‌بندی هر نامزد را در نظر می‌گیرد، یک معیار فهرستی است.

class RankingModel(tfrs.Model):

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

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

    # Compute embeddings for movies.
    self.movie_embeddings = tf.keras.Sequential([
      tf.keras.layers.StringLookup(
        vocabulary=unique_movie_titles),
      tf.keras.layers.Embedding(len(unique_movie_titles) + 2, embedding_dimension)
    ])

    # Compute predictions.
    self.score_model = 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)
    ])

    self.task = tfrs.tasks.Ranking(
      loss=loss,
      metrics=[
        tfr.keras.metrics.NDCGMetric(name="ndcg_metric"),
        tf.keras.metrics.RootMeanSquaredError()
      ]
    )

  def call(self, features):
    # We first convert the id features into embeddings.
    # User embeddings are a [batch_size, embedding_dim] tensor.
    user_embeddings = self.user_embeddings(features["user_id"])

    # Movie embeddings are a [batch_size, num_movies_in_list, embedding_dim]
    # tensor.
    movie_embeddings = self.movie_embeddings(features["movie_title"])

    # We want to concatenate user embeddings with movie emebeddings to pass
    # them into the ranking model. To do so, we need to reshape the user
    # embeddings to match the shape of movie embeddings.
    list_length = features["movie_title"].shape[1]
    user_embedding_repeated = tf.repeat(
        tf.expand_dims(user_embeddings, 1), [list_length], axis=1)

    # Once reshaped, we concatenate and pass into the dense layers to generate
    # predictions.
    concatenated_embeddings = tf.concat(
        [user_embedding_repeated, movie_embeddings], 2)

    return self.score_model(concatenated_embeddings)

  def compute_loss(self, features, training=False):
    labels = features.pop("user_rating")

    scores = self(features)

    return self.task(
        labels=labels,
        predictions=tf.squeeze(scores, axis=-1),
    )

آموزش مدل ها

اکنون می توانیم هر یک از این سه مدل را آموزش دهیم.

epochs = 30

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

مدل خطای میانگین مربع

این مدل بسیار شبیه به مدل در آموزش پایه رتبه بندی . ما مدل را طوری آموزش می‌دهیم که میانگین مجذور خطا بین رتبه‌بندی‌های واقعی و رتبه‌بندی‌های پیش‌بینی‌شده را به حداقل برساند. بنابراین، این ضرر برای هر فیلم به صورت جداگانه محاسبه می شود و آموزش به صورت نقطه ای است.

mse_model = RankingModel(tf.keras.losses.MeanSquaredError())
mse_model.compile(optimizer=tf.keras.optimizers.Adagrad(0.1))
mse_model.fit(cached_train, epochs=epochs, verbose=False)
<keras.callbacks.History at 0x7f64791a5d10>

مدل از دست دادن لولای جفتی

با به حداقل رساندن تلفات لولای جفتی، مدل سعی می‌کند تفاوت بین پیش‌بینی‌های مدل را برای یک آیتم با رتبه بالا و یک آیتم با رتبه پایین به حداکثر برساند: هر چه این تفاوت بیشتر باشد، ضرر مدل کمتر می‌شود. با این حال، هنگامی که اختلاف به اندازه کافی بزرگ شد، ضرر صفر می شود و مدل را از بهینه سازی بیشتر این جفت خاص باز می دارد و به آن اجازه می دهد روی جفت های دیگری که به اشتباه رتبه بندی شده اند تمرکز کند.

این ضرر برای فیلم های جداگانه محاسبه نمی شود، بلکه برای جفت فیلم محاسبه می شود. بنابراین آموزش با استفاده از این باخت به صورت دوتایی است.

hinge_model = RankingModel(tfr.keras.losses.PairwiseHingeLoss())
hinge_model.compile(optimizer=tf.keras.optimizers.Adagrad(0.1))
hinge_model.fit(cached_train, epochs=epochs, verbose=False)
<keras.callbacks.History at 0x7f647914f190>

مدل Listwise

ListMLE از دست دادن از TensorFlow بیان فهرست رتبه بندی برآورد احتمال حداکثر. برای محاسبه ضرر ListMLE، ابتدا از رتبه بندی کاربران برای ایجاد یک رتبه بندی بهینه استفاده می کنیم. سپس با استفاده از نمرات پیش‌بینی‌شده، احتمال برتری هر نامزد را با هر آیتم زیر آن در رتبه‌بندی بهینه محاسبه می‌کنیم. این مدل سعی می‌کند چنین احتمالی را به حداقل برساند تا اطمینان حاصل شود که کاندیداهایی که دارای امتیاز بالا هستند از رتبه‌بندی کاندیداهای با رتبه پایین بالاتر نیستند. شما می توانید اطلاعات بیشتر در مورد جزئیات ListMLE در بخش 2.2 مقاله یاد بگیرند مکان آگاه ListMLE: پردازش ترتیبی آموزش .

توجه داشته باشید که از آنجایی که احتمال با توجه به یک نامزد و همه نامزدهای زیر آن در رتبه‌بندی بهینه محاسبه می‌شود، ضرر به صورت زوجی نیست، بلکه به صورت فهرستی است. از این رو آموزش از بهینه سازی لیست استفاده می کند.

listwise_model = RankingModel(tfr.keras.losses.ListMLELoss())
listwise_model.compile(optimizer=tf.keras.optimizers.Adagrad(0.1))
listwise_model.fit(cached_train, epochs=epochs, verbose=False)
<keras.callbacks.History at 0x7f647b35f350>

مقایسه مدل ها

mse_model_result = mse_model.evaluate(cached_test, return_dict=True)
print("NDCG of the MSE Model: {:.4f}".format(mse_model_result["ndcg_metric"]))
1/1 [==============================] - 0s 405ms/step - ndcg_metric: 0.9053 - root_mean_squared_error: 0.9671 - loss: 0.9354 - regularization_loss: 0.0000e+00 - total_loss: 0.9354
NDCG of the MSE Model: 0.9053
hinge_model_result = hinge_model.evaluate(cached_test, return_dict=True)
print("NDCG of the pairwise hinge loss model: {:.4f}".format(hinge_model_result["ndcg_metric"]))
1/1 [==============================] - 0s 457ms/step - ndcg_metric: 0.9058 - root_mean_squared_error: 3.8330 - loss: 1.0180 - regularization_loss: 0.0000e+00 - total_loss: 1.0180
NDCG of the pairwise hinge loss model: 0.9058
listwise_model_result = listwise_model.evaluate(cached_test, return_dict=True)
print("NDCG of the ListMLE model: {:.4f}".format(listwise_model_result["ndcg_metric"]))
1/1 [==============================] - 0s 432ms/step - ndcg_metric: 0.9071 - root_mean_squared_error: 2.7224 - loss: 4.5401 - regularization_loss: 0.0000e+00 - total_loss: 4.5401
NDCG of the ListMLE model: 0.9071

از بین سه مدل، مدل آموزش دیده با استفاده از ListMLE دارای بالاترین متریک NDCG است. این نتیجه نشان می‌دهد که چگونه می‌توان از بهینه‌سازی فهرستی برای آموزش مدل‌های رتبه‌بندی استفاده کرد و به طور بالقوه می‌تواند مدل‌هایی را تولید کند که عملکرد بهتری نسبت به مدل‌های بهینه‌سازی شده به صورت نقطه‌ای یا زوجی داشته باشند.