추천 영화: 순위

TensorFlow.org에서 보기 Google Colab에서 실행 GitHub에서 소스 보기 노트북 다운로드

실제 추천 시스템은 종종 두 단계로 구성됩니다.

  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)

데이터에 있는 고유한 사용자 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 계층입니다. 이를 사용하여 모델의 훈련 루프를 구현합니다.

전체 모델

이제 모든 것을 하나의 모델로 통합할 수 있습니다. TFRS는 기본 모델 클래스 (공개 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)

다음 단계

위의 모델은 순위 시스템 구축을 위한 적절한 시작을 제공합니다.

물론 실용적인 순위 시스템을 만들기 위해서는 훨씬 더 많은 노력이 필요합니다.

대부분의 경우 순위 모델은 사용자 및 후보 식별자보다 더 많은 기능을 사용하여 상당히 향상될 수 있습니다. 이 작업을 수행하는 방법을 보려면 한 번 봐 가지고 측 기능 튜토리얼.

최적화할 가치가 있는 목표에 대한 주의 깊은 이해도 필요합니다. 여러 목표를 최적화하는 추천인을 구축 시작하려면, 우리 한 번 봐 가지고 멀티 태스크 자습서를.