リストごとのランキング

TensorFlow.orgで表示GoogleColabで実行GitHubでソースを表示 ノートブックをダウンロード

では、基本的なランキングチュートリアル、我々は、ユーザ/映画のペアのための評価を予測できるモデルを訓練しました。モデルは、予測された評価の平均二乗誤差を最小化するようにトレーニングされました。

ただし、個々の映画でモデルの予測を最適化することは、ランキングモデルをトレーニングするための最良の方法であるとは限りません。スコアを非常に正確に予測するためにランキングモデルは必要ありません。代わりに、ユーザーの好みの順序に一致するアイテムの順序付きリストを生成するモデルの機能に関心があります。

個々のクエリ/アイテムのペアでモデルの予測を最適化する代わりに、リスト全体のモデルのランキングを最適化できます。この方法では、ランキングリストごとと呼ばれています。

このチュートリアルでは、TensorFlowレコメンダーを使用して、リストごとのランキングモデルを構築します。そうするために、我々はによって提供さ損失およびメトリクスランキングの使用になります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,

引き続きMovieLens100Kデータセットを使用します。以前と同様に、データセットをロードし、このチュートリアルのユーザーID、映画のタイトル、およびユーザー評価機能のみを保持します。また、語彙を準備するためにいくつかのハウスキーピングを行います。

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データセットの各例には、単一の映画の評価のみが含まれています。

これを回避するために、各例にユーザーIDとそのユーザーによって評価された映画のリストが含まれるようにデータセットを変換します。リスト内の一部の映画は、他の映画よりも上位にランク付けされます。このモデルの目標は、この順序に一致する予測を行うことです。

これを行うために、我々は、使用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
)

トレーニングデータから例を調べることができます。この例には、ユーザーID、10本の映画IDのリスト、およびユーザーによるそれらの評価が含まれています。

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)>}

モデル定義

同じモデルを3つの異なる損失でトレーニングします。

  • 平均二乗誤差、
  • ペアワイズヒンジ損失、および
  • リストごとのListMLE損失。

これらの3つの損失は、ポイントごと、ペアごと、およびリストごとの最適化に対応します。

モデルを評価するために、我々は、使用して正規化、割引累積ゲイン(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),
    )

モデルのトレーニング

これで、3つのモデルのそれぞれをトレーニングできます。

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>

リストワイズモデル

ListMLE TensorFlowランキング表現からの損失は、最尤推定を一覧表示します。 ListMLEの損失を計算するには、最初にユーザーの評価を使用して最適なランキングを生成します。次に、予測されたスコアを使用して、各候補が最適なランキングでその下のアイテムによってランク付けされない可能性を計算します。このモデルは、このような可能性を最小限に抑えて、評価の高い候補者が評価の低い候補者よりも上位にランク付けされないようにします。あなたは、紙のセクション2.2でListMLEの詳細についての詳細を学ぶことができるポジションを意識しListMLE:A逐次学習プロセスを

尤度は、最適なランキングで候補とその下にあるすべての候補に関して計算されるため、損失はペアごとではなくリストごとであることに注意してください。したがって、トレーニングではリストの最適化を使用します。

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

3つのモデルの中で、ListMLEを使用してトレーニングされたモデルのNDCGメトリックが最も高くなります。この結果は、リストごとの最適化を使用してランキングモデルをトレーニングする方法を示しており、ポイントごとまたはペアごとに最適化されたモデルよりもパフォーマンスが高いモデルを作成できる可能性があります。