TensorFlow.org에서 보기 | Google Colab에서 실행 | GitHub에서 소스 보기 | 노트북 다운로드 |
에서 기본 검색 자습서 우리는 긍정적 상호 작용을 신호로 영화 시계를 사용하여 검색 시스템을 구축.
그러나 많은 응용 프로그램에서 활용할 수 있는 다양한 피드백 소스가 있습니다. 예를 들어, 전자 상거래 사이트는 제품 페이지에 대한 사용자 방문(풍부하지만 상대적으로 낮은 신호), 이미지 클릭, 장바구니에 추가, 마지막으로 구매를 기록할 수 있습니다. 리뷰 및 반품과 같은 구매 후 신호를 기록할 수도 있습니다.
이러한 다양한 형태의 피드백을 통합하는 것은 사용자가 사용하기 좋아하는 시스템을 구축하는 데 중요하며 전체 성능을 희생시키면서 어느 한 지표에 대해 최적화하지 않습니다.
또한 여러 작업에 대한 공동 모델을 구축하는 것이 여러 작업별 모델을 구축하는 것보다 더 나은 결과를 생성할 수 있습니다. 이는 일부 데이터가 풍부하고(예: 클릭) 일부 데이터가 희소한 경우(구매, 반품, 수동 검토) 특히 그렇습니다. 이러한 시나리오에서 공동 모델로 알려진 현상을 통해 스파 스 작업에 자사의 예측을 개선하기 위해 풍부한 작업에서 배운 표현을 사용할 수 있습니다 전송 학습 . 예를 들어, 이 종이 희박한 사용자 조사 명시적인 사용자 등급, 예측 모델은 실질적으로 풍부한 클릭 로그 데이터를 사용하여 보조 작업을 추가함으로써 개선 될 수 있음을 보여준다.
이 튜토리얼에서는 암시적(영화 시청) 및 명시적 신호(등급)를 모두 사용하여 Movielens용 다중 목표 추천기를 구축할 것입니다.
수입품
먼저 수입품을 제거합시다.
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
데이터세트 준비
Movielens 100K 데이터 세트를 사용할 것입니다.
ratings = tfds.load('movielens/100k-ratings', split="train")
movies = tfds.load('movielens/100k-movies', split="train")
# Select the basic features.
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"])
그리고 어휘를 구축하고 데이터를 기차와 테스트 세트로 분할하기 위한 준비를 반복합니다.
# Randomly shuffle data and split between train and test.
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 = movies.batch(1_000)
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)))
멀티태스킹 모델
멀티태스킹 추천인에게는 두 가지 중요한 부분이 있습니다.
- 그들은 둘 이상의 목표를 위해 최적화하므로 둘 이상의 손실이 있습니다.
- 그들은 작업 간에 변수를 공유하여 전이 학습을 허용합니다.
이 자습서에서는 이전과 같이 모델을 정의하지만 단일 작업 대신 두 가지 작업이 있습니다. 하나는 등급을 예측하고 다른 하나는 영화 시청을 예측합니다.
사용자 및 영화 모델은 이전과 같습니다.
user_model = tf.keras.Sequential([
tf.keras.layers.StringLookup(
vocabulary=unique_user_ids, mask_token=None),
# We add 1 to account for the unknown token.
tf.keras.layers.Embedding(len(unique_user_ids) + 1, embedding_dimension)
])
movie_model = 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)
])
그러나 이제 우리는 두 가지 작업을 해야 합니다. 첫 번째는 평가 작업입니다.
tfrs.tasks.Ranking(
loss=tf.keras.losses.MeanSquaredError(),
metrics=[tf.keras.metrics.RootMeanSquaredError()],
)
목표는 가능한 한 정확하게 등급을 예측하는 것입니다.
두 번째는 검색 작업입니다.
tfrs.tasks.Retrieval(
metrics=tfrs.metrics.FactorizedTopK(
candidates=movies.batch(128)
)
)
이전과 마찬가지로 이 작업의 목표는 사용자가 시청하거나 시청하지 않을 영화를 예측하는 것입니다.
함께 넣어
우리는 모든 것을 모델 클래스에 모았습니다.
여기서 새로운 구성요소는 - 우리는 2개의 작업과 2개의 손실을 가지고 있기 때문에 - 각각의 손실이 얼마나 중요한지 결정해야 한다는 것입니다. 각 손실에 가중치를 부여하고 이러한 가중치를 하이퍼파라미터로 처리하여 이를 수행할 수 있습니다. 평가 작업에 큰 손실 가중치를 할당하면 모델은 평가 예측에 중점을 둘 것입니다(그러나 여전히 검색 작업의 일부 정보를 사용함). 검색 작업에 큰 손실 가중치를 할당하면 대신 검색에 중점을 둡니다.
class MovielensModel(tfrs.models.Model):
def __init__(self, rating_weight: float, retrieval_weight: float) -> None:
# We take the loss weights in the constructor: this allows us to instantiate
# several model objects with different loss weights.
super().__init__()
embedding_dimension = 32
# User and movie models.
self.movie_model: tf.keras.layers.Layer = 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)
])
self.user_model: tf.keras.layers.Layer = 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)
])
# A small model to take in user and movie embeddings and predict ratings.
# We can make this as complicated as we want as long as we output a scalar
# as our prediction.
self.rating_model = tf.keras.Sequential([
tf.keras.layers.Dense(256, activation="relu"),
tf.keras.layers.Dense(128, activation="relu"),
tf.keras.layers.Dense(1),
])
# The tasks.
self.rating_task: tf.keras.layers.Layer = tfrs.tasks.Ranking(
loss=tf.keras.losses.MeanSquaredError(),
metrics=[tf.keras.metrics.RootMeanSquaredError()],
)
self.retrieval_task: tf.keras.layers.Layer = tfrs.tasks.Retrieval(
metrics=tfrs.metrics.FactorizedTopK(
candidates=movies.batch(128).map(self.movie_model)
)
)
# The loss weights.
self.rating_weight = rating_weight
self.retrieval_weight = retrieval_weight
def call(self, features: Dict[Text, tf.Tensor]) -> tf.Tensor:
# We pick out the user features and pass them into the user model.
user_embeddings = self.user_model(features["user_id"])
# And pick out the movie features and pass them into the movie model.
movie_embeddings = self.movie_model(features["movie_title"])
return (
user_embeddings,
movie_embeddings,
# We apply the multi-layered rating model to a concatentation of
# user and movie embeddings.
self.rating_model(
tf.concat([user_embeddings, movie_embeddings], axis=1)
),
)
def compute_loss(self, features: Dict[Text, tf.Tensor], training=False) -> tf.Tensor:
ratings = features.pop("user_rating")
user_embeddings, movie_embeddings, rating_predictions = self(features)
# We compute the loss for each task.
rating_loss = self.rating_task(
labels=ratings,
predictions=rating_predictions,
)
retrieval_loss = self.retrieval_task(user_embeddings, movie_embeddings)
# And combine them using the loss weights.
return (self.rating_weight * rating_loss
+ self.retrieval_weight * retrieval_loss)
등급 특화 모델
우리가 할당한 가중치에 따라 모델은 작업의 다른 균형을 인코딩합니다. 등급만 고려하는 모델부터 시작하겠습니다.
model = MovielensModel(rating_weight=1.0, retrieval_weight=0.0)
model.compile(optimizer=tf.keras.optimizers.Adagrad(0.1))
cached_train = train.shuffle(100_000).batch(8192).cache()
cached_test = test.batch(4096).cache()
model.fit(cached_train, epochs=3)
metrics = model.evaluate(cached_test, return_dict=True)
print(f"Retrieval top-100 accuracy: {metrics['factorized_top_k/top_100_categorical_accuracy']:.3f}.")
print(f"Ranking RMSE: {metrics['root_mean_squared_error']:.3f}.")
Epoch 1/3 10/10 [==============================] - 7s 331ms/step - root_mean_squared_error: 2.0903 - factorized_top_k/top_1_categorical_accuracy: 2.7500e-04 - factorized_top_k/top_5_categorical_accuracy: 0.0024 - factorized_top_k/top_10_categorical_accuracy: 0.0054 - factorized_top_k/top_50_categorical_accuracy: 0.0294 - factorized_top_k/top_100_categorical_accuracy: 0.0589 - loss: 4.0315 - regularization_loss: 0.0000e+00 - total_loss: 4.0315 Epoch 2/3 10/10 [==============================] - 3s 321ms/step - root_mean_squared_error: 1.1531 - factorized_top_k/top_1_categorical_accuracy: 1.8750e-04 - factorized_top_k/top_5_categorical_accuracy: 0.0024 - factorized_top_k/top_10_categorical_accuracy: 0.0054 - factorized_top_k/top_50_categorical_accuracy: 0.0297 - factorized_top_k/top_100_categorical_accuracy: 0.0591 - loss: 1.3189 - regularization_loss: 0.0000e+00 - total_loss: 1.3189 Epoch 3/3 10/10 [==============================] - 3s 316ms/step - root_mean_squared_error: 1.1198 - factorized_top_k/top_1_categorical_accuracy: 1.6250e-04 - factorized_top_k/top_5_categorical_accuracy: 0.0025 - factorized_top_k/top_10_categorical_accuracy: 0.0055 - factorized_top_k/top_50_categorical_accuracy: 0.0300 - factorized_top_k/top_100_categorical_accuracy: 0.0597 - loss: 1.2479 - regularization_loss: 0.0000e+00 - total_loss: 1.2479 5/5 [==============================] - 3s 194ms/step - root_mean_squared_error: 1.1130 - factorized_top_k/top_1_categorical_accuracy: 4.5000e-04 - factorized_top_k/top_5_categorical_accuracy: 0.0028 - factorized_top_k/top_10_categorical_accuracy: 0.0052 - factorized_top_k/top_50_categorical_accuracy: 0.0295 - factorized_top_k/top_100_categorical_accuracy: 0.0597 - loss: 1.2336 - regularization_loss: 0.0000e+00 - total_loss: 1.2336 Retrieval top-100 accuracy: 0.060. Ranking RMSE: 1.113.
이 모델은 등급 예측(RMSE 약 1.11)에서는 괜찮지만 시청할 영화를 예측하는 데는 좋지 않습니다. 100의 정확도는 시계 예측만을 위해 훈련된 모델보다 거의 4배 나쁩니다.
검색 전문 모델
이제 검색에만 초점을 맞춘 모델을 사용해 보겠습니다.
model = MovielensModel(rating_weight=0.0, retrieval_weight=1.0)
model.compile(optimizer=tf.keras.optimizers.Adagrad(0.1))
model.fit(cached_train, epochs=3)
metrics = model.evaluate(cached_test, return_dict=True)
print(f"Retrieval top-100 accuracy: {metrics['factorized_top_k/top_100_categorical_accuracy']:.3f}.")
print(f"Ranking RMSE: {metrics['root_mean_squared_error']:.3f}.")
Epoch 1/3 10/10 [==============================] - 4s 313ms/step - root_mean_squared_error: 3.7238 - factorized_top_k/top_1_categorical_accuracy: 7.5000e-05 - factorized_top_k/top_5_categorical_accuracy: 0.0014 - factorized_top_k/top_10_categorical_accuracy: 0.0041 - factorized_top_k/top_50_categorical_accuracy: 0.0473 - factorized_top_k/top_100_categorical_accuracy: 0.1135 - loss: 69818.0298 - regularization_loss: 0.0000e+00 - total_loss: 69818.0298 Epoch 2/3 10/10 [==============================] - 3s 326ms/step - root_mean_squared_error: 3.7495 - factorized_top_k/top_1_categorical_accuracy: 0.0011 - factorized_top_k/top_5_categorical_accuracy: 0.0116 - factorized_top_k/top_10_categorical_accuracy: 0.0268 - factorized_top_k/top_50_categorical_accuracy: 0.1425 - factorized_top_k/top_100_categorical_accuracy: 0.2658 - loss: 67473.2884 - regularization_loss: 0.0000e+00 - total_loss: 67473.2884 Epoch 3/3 10/10 [==============================] - 3s 314ms/step - root_mean_squared_error: 3.7648 - factorized_top_k/top_1_categorical_accuracy: 0.0014 - factorized_top_k/top_5_categorical_accuracy: 0.0180 - factorized_top_k/top_10_categorical_accuracy: 0.0388 - factorized_top_k/top_50_categorical_accuracy: 0.1773 - factorized_top_k/top_100_categorical_accuracy: 0.3050 - loss: 66329.2543 - regularization_loss: 0.0000e+00 - total_loss: 66329.2543 5/5 [==============================] - 1s 193ms/step - root_mean_squared_error: 3.7730 - factorized_top_k/top_1_categorical_accuracy: 0.0012 - factorized_top_k/top_5_categorical_accuracy: 0.0097 - factorized_top_k/top_10_categorical_accuracy: 0.0218 - factorized_top_k/top_50_categorical_accuracy: 0.1253 - factorized_top_k/top_100_categorical_accuracy: 0.2352 - loss: 31085.0697 - regularization_loss: 0.0000e+00 - total_loss: 31085.0697 Retrieval top-100 accuracy: 0.235. Ranking RMSE: 3.773.
우리는 반대의 결과를 얻습니다. 검색은 잘하지만 평가 예측에는 좋지 않은 모델입니다.
조인트 모델
이제 두 작업에 양수 가중치를 할당하는 모델을 훈련해 보겠습니다.
model = MovielensModel(rating_weight=1.0, retrieval_weight=1.0)
model.compile(optimizer=tf.keras.optimizers.Adagrad(0.1))
model.fit(cached_train, epochs=3)
metrics = model.evaluate(cached_test, return_dict=True)
print(f"Retrieval top-100 accuracy: {metrics['factorized_top_k/top_100_categorical_accuracy']:.3f}.")
print(f"Ranking RMSE: {metrics['root_mean_squared_error']:.3f}.")
Epoch 1/3 10/10 [==============================] - 4s 299ms/step - root_mean_squared_error: 2.5007 - factorized_top_k/top_1_categorical_accuracy: 3.7500e-05 - factorized_top_k/top_5_categorical_accuracy: 0.0014 - factorized_top_k/top_10_categorical_accuracy: 0.0043 - factorized_top_k/top_50_categorical_accuracy: 0.0450 - factorized_top_k/top_100_categorical_accuracy: 0.1102 - loss: 69811.8274 - regularization_loss: 0.0000e+00 - total_loss: 69811.8274 Epoch 2/3 10/10 [==============================] - 3s 312ms/step - root_mean_squared_error: 1.2097 - factorized_top_k/top_1_categorical_accuracy: 9.8750e-04 - factorized_top_k/top_5_categorical_accuracy: 0.0110 - factorized_top_k/top_10_categorical_accuracy: 0.0255 - factorized_top_k/top_50_categorical_accuracy: 0.1385 - factorized_top_k/top_100_categorical_accuracy: 0.2605 - loss: 67481.2713 - regularization_loss: 0.0000e+00 - total_loss: 67481.2713 Epoch 3/3 10/10 [==============================] - 3s 305ms/step - root_mean_squared_error: 1.1200 - factorized_top_k/top_1_categorical_accuracy: 0.0011 - factorized_top_k/top_5_categorical_accuracy: 0.0175 - factorized_top_k/top_10_categorical_accuracy: 0.0380 - factorized_top_k/top_50_categorical_accuracy: 0.1758 - factorized_top_k/top_100_categorical_accuracy: 0.3040 - loss: 66297.9318 - regularization_loss: 0.0000e+00 - total_loss: 66297.9318 5/5 [==============================] - 1s 187ms/step - root_mean_squared_error: 1.1312 - factorized_top_k/top_1_categorical_accuracy: 9.5000e-04 - factorized_top_k/top_5_categorical_accuracy: 0.0083 - factorized_top_k/top_10_categorical_accuracy: 0.0220 - factorized_top_k/top_50_categorical_accuracy: 0.1248 - factorized_top_k/top_100_categorical_accuracy: 0.2347 - loss: 31062.8206 - regularization_loss: 0.0000e+00 - total_loss: 31062.8206 Retrieval top-100 accuracy: 0.235. Ranking RMSE: 1.131.
결과는 각각의 전문화된 모델과 마찬가지로 두 작업 모두에서 대략적으로 잘 수행되는 모델입니다.
예측하기
훈련된 멀티태스킹 모델을 사용하여 훈련된 사용자 및 영화 임베딩과 예측 등급을 얻을 수 있습니다.
trained_movie_embeddings, trained_user_embeddings, predicted_rating = model({
"user_id": np.array(["42"]),
"movie_title": np.array(["Dances with Wolves (1990)"])
})
print("Predicted rating:")
print(predicted_rating)
Predicted rating: tf.Tensor([[3.4021819]], shape=(1, 1), dtype=float32)
여기의 결과는 이 경우 조인트 모델의 명확한 정확도 이점을 보여주지 않지만 다중 작업 학습은 일반적으로 매우 유용한 도구입니다. 데이터가 풍부한 작업(예: 클릭)에서 밀접하게 관련된 데이터가 부족한 작업(예: 구매)으로 지식을 이전할 수 있을 때 더 나은 결과를 기대할 수 있습니다.