MLコミュニティデーは11月9日です! TensorFlow、JAXからの更新のために私たちに参加し、より多くの詳細をご覧ください

おすすめの映画:ランキング

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

実際のレコメンダーシステムは、多くの場合、次の2つの段階で構成されます。

  1. 検索段階では、考えられるすべての候補から数百の候補の初期セットを選択します。このモデルの主な目的は、ユーザーが関心のないすべての候補を効率的に取り除くことです。検索モデルは数百万の候補を処理する可能性があるため、計算効率が高くなければなりません。
  2. ランク付け段階では、検索モデルの出力を取得し、それらを微調整して、可能な限り最良の少数の推奨事項を選択します。そのタスクは、ユーザーが興味を持つ可能性のあるアイテムのセットを、可能性のある候補の候補リストに絞り込むことです。

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

また、データに存在する一意のユーザーIDと映画のタイトルを把握しましょう。

これは重要です。なぜなら、カテゴリの特徴の生の値をモデルの埋め込みベクトルにマッピングできる必要があるからです。これを行うには、生の特徴値を連続した範囲の整数にマップする語彙が必要です。これにより、埋め込みテーブルで対応する埋め込みを検索できます。

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

このモデルは、ユーザーIDと映画のタイトルを取得し、予測された評価を出力します。

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()]
)

タスク自体は、trueを取り、引数として予測され、計算された損失を返すKerasレイヤーです。これを使用して、モデルのトレーニングループを実装します。

フルモデル

これで、すべてを1つのモデルにまとめることができます。 TFRSは、ベースモデルクラス(公開しtfrs.models.Model buldingモデルを合理化):私たちが行う必要があるのは、中にコンポーネントを設定することです__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)

次のステップ

上記のモデルは、ランキングシステムの構築に向けた適切なスタートを与えてくれます。

もちろん、実用的なランキングシステムを作るにはもっと多くの努力が必要です。

ほとんどの場合、ランキングモデルは、ユーザーIDと候補IDだけでなく、より多くの機能を使用することで大幅に改善できます。それを行う方法を参照するには、見てい側面が特徴のチュートリアル。

最適化する価値のある目的を注意深く理解することも必要です。複数の目標を最適化し、推薦の構築を始めるために、私たちを見ていマルチタスクのチュートリアルを。