Người giới thiệu nhiều nhiệm vụ

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ ghi chép

Trong hướng dẫn phục hồi cơ bản chúng tôi đã xây dựng một hệ thống thu hồi sử dụng đồng hồ điện ảnh như tín hiệu tương tác tích cực.

Tuy nhiên, trong nhiều ứng dụng, có nhiều nguồn phản hồi phong phú để rút ra. Ví dụ: một trang web thương mại điện tử có thể ghi lại lượt truy cập của người dùng vào các trang sản phẩm (nhiều, nhưng tín hiệu tương đối thấp), nhấp chuột vào hình ảnh, thêm vào giỏ hàng và cuối cùng là mua hàng. Nó thậm chí có thể ghi lại các tín hiệu sau khi mua hàng như đánh giá và trả hàng.

Việc tích hợp tất cả các hình thức phản hồi khác nhau này là rất quan trọng để xây dựng các hệ thống mà người dùng thích sử dụng và hệ thống đó không tối ưu hóa cho bất kỳ chỉ số nào mà phải trả giá bằng hiệu suất tổng thể.

Ngoài ra, việc xây dựng một mô hình chung cho nhiều nhiệm vụ có thể tạo ra kết quả tốt hơn so với việc xây dựng một số mô hình cụ thể cho từng nhiệm vụ. Điều này đặc biệt đúng khi một số dữ liệu dồi dào (ví dụ: số lần nhấp chuột) và một số dữ liệu ít (mua hàng, trả lại, đánh giá thủ công). Trong những tình huống, một mô hình chung có thể sử dụng cơ quan đại diện học được từ nhiệm vụ phong phú nhằm nâng cao dự đoán của nó vào các nhiệm vụ thưa thớt qua một hiện tượng được gọi là học tập chuyển giao . Ví dụ, nghiên cứu này cho thấy một mô hình dự đoán xếp hạng người dùng rõ ràng từ các cuộc khảo sát người sử dụng thưa thớt có thể được cải thiện đáng kể bằng cách thêm một nhiệm vụ phụ trợ có sử dụng dữ liệu nhấp chuột đăng nhập dồi dào.

Trong hướng dẫn này, chúng tôi sẽ xây dựng một đề xuất đa mục tiêu cho Movielens, sử dụng cả tín hiệu ngầm định (xem phim) và tín hiệu rõ ràng (xếp hạng).

Nhập khẩu

Đầu tiên chúng ta hãy lấy hàng nhập khẩu của chúng ta ra khỏi con đường.

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

Chuẩn bị tập dữ liệu

Chúng tôi sẽ sử dụng tập dữ liệu 100K của Movielens.

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

Và lặp lại các bước chuẩn bị của chúng tôi để xây dựng các từ vựng và chia nhỏ dữ liệu thành một nhóm và một tập hợp thử nghiệm:

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

Một mô hình đa tác vụ

Có hai phần quan trọng đối với những người đề xuất đa tác vụ:

  1. Chúng tối ưu hóa cho hai hoặc nhiều mục tiêu và do đó có hai hoặc nhiều lỗ.
  2. Chúng chia sẻ các biến giữa các nhiệm vụ, cho phép học chuyển tiếp.

Trong hướng dẫn này, chúng ta sẽ xác định các mô hình của mình như trước đây, nhưng thay vì có một nhiệm vụ duy nhất, chúng ta sẽ có hai nhiệm vụ: một nhiệm vụ dự đoán xếp hạng và một nhiệm vụ dự đoán lượt xem phim.

Người dùng và mô hình phim giống như trước đây:

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

Tuy nhiên, bây giờ chúng ta sẽ có hai nhiệm vụ. Đầu tiên là nhiệm vụ xếp hạng:

tfrs.tasks.Ranking(
    loss=tf.keras.losses.MeanSquaredError(),
    metrics=[tf.keras.metrics.RootMeanSquaredError()],
)

Mục tiêu của nó là dự đoán xếp hạng chính xác nhất có thể.

Thứ hai là nhiệm vụ truy xuất:

tfrs.tasks.Retrieval(
    metrics=tfrs.metrics.FactorizedTopK(
        candidates=movies.batch(128)
    )
)

Như trước đây, mục tiêu của nhiệm vụ này là dự đoán những bộ phim mà người dùng sẽ xem hoặc không xem.

Kết hợp nó lại với nhau

Chúng tôi tập hợp tất cả lại với nhau trong một lớp mô hình.

Thành phần mới ở đây là - vì chúng tôi có hai nhiệm vụ và hai tổn thất - chúng tôi cần quyết định xem mức độ quan trọng của mỗi tổn thất. Chúng ta có thể làm điều này bằng cách cho mỗi khoản lỗ là một trọng số và coi các trọng số này là siêu tham số. Nếu chúng tôi gán một trọng số tổn thất lớn cho nhiệm vụ xếp hạng, mô hình của chúng tôi sẽ tập trung vào việc dự đoán xếp hạng (nhưng vẫn sử dụng một số thông tin từ nhiệm vụ truy xuất); nếu chúng tôi gán trọng số tổn thất lớn cho nhiệm vụ truy xuất, thay vào đó nó sẽ tập trung vào việc truy xuất.

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)

Mô hình chuyên biệt về xếp hạng

Tùy thuộc vào trọng số mà chúng tôi ấn định, mô hình sẽ mã hóa số dư khác nhau của các tác vụ. Hãy bắt đầu với một mô hình chỉ xem xét xếp hạng.

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.

Mô hình này hoạt động tốt khi dự đoán xếp hạng (với RMSE khoảng 1,11), nhưng hoạt động kém trong việc dự đoán phim nào sẽ được xem hay không: độ chính xác ở mức 100 kém hơn gần 4 lần so với một mô hình được đào tạo chỉ để dự đoán đồng hồ.

Mô hình chuyên biệt về truy xuất

Bây giờ chúng ta hãy thử một mô hình chỉ tập trung vào truy xuất.

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.

Chúng tôi nhận được kết quả ngược lại: một mô hình hoạt động tốt về truy xuất, nhưng kém về dự đoán xếp hạng.

Mô hình chung

Bây giờ chúng ta hãy đào tạo một mô hình chỉ định trọng số dương cho cả hai nhiệm vụ.

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.

Kết quả là một mô hình thực hiện gần như tốt cả hai nhiệm vụ như mỗi mô hình chuyên biệt.

Đưa ra dự đoán

Chúng tôi có thể sử dụng mô hình đa nhiệm được đào tạo để có được người dùng được đào tạo và nhúng phim, cũng như xếp hạng dự đoán:

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)

Mặc dù kết quả ở đây không cho thấy lợi ích chính xác rõ ràng từ mô hình chung trong trường hợp này, nhưng nhìn chung, học đa nhiệm là một công cụ cực kỳ hữu ích. Chúng ta có thể mong đợi kết quả tốt hơn khi có thể chuyển kiến ​​thức từ một nhiệm vụ có nhiều dữ liệu (chẳng hạn như nhấp chuột) sang một nhiệm vụ thưa thớt dữ liệu có liên quan chặt chẽ (chẳng hạn như mua hàng).