Đề xuất phim: xếp hạng

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

Hệ thống tư vấn trong thế giới thực thường bao gồm hai giai đoạn:

  1. Giai đoạn truy xuất chịu trách nhiệm chọn một tập hợp ban đầu gồm hàng trăm ứng viên từ tất cả các ứng viên có thể. Mục tiêu chính của mô hình này là loại bỏ hiệu quả tất cả các ứng cử viên mà người dùng không quan tâm. Bởi vì mô hình truy xuất có thể xử lý hàng triệu ứng viên, nên nó phải hiệu quả về mặt tính toán.
  2. Giai đoạn xếp hạng lấy kết quả đầu ra của mô hình truy xuất và tinh chỉnh chúng để chọn ra một số đề xuất tốt nhất có thể. Nhiệm vụ của nó là thu hẹp tập hợp các mục mà người dùng có thể quan tâm vào một danh sách rút gọn các ứng cử viên có khả năng.

Chúng ta sẽ tập trung vào giai đoạn thứ hai, xếp hạng. Nếu bạn đang quan tâm trong giai đoạn phục hồi, có một cái nhìn tại của chúng tôi hồi hướng dẫn.

Trong hướng dẫn này, chúng ta sẽ:

  1. Nhận dữ liệu của chúng tôi và chia nó thành một tập hợp đào tạo và kiểm tra.
  2. Thực hiện một mô hình xếp hạng.
  3. Phù hợp và đánh giá nó.

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 ta sẽ sử dụng các dữ liệu tương tự như hồi hướng dẫn. Lần này, chúng tôi cũng sẽ giữ nguyên xếp hạng: đây là những mục tiêu mà chúng tôi đang cố gắng dự đoán.

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

Như trước đây, chúng tôi sẽ chia nhỏ dữ liệu bằng cách đưa 80% xếp hạng vào nhóm tàu ​​và 20% trong nhóm thử nghiệm.

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)

Hãy cũng tìm ra id người dùng duy nhất và tiêu đề phim có trong dữ liệu.

Điều này rất quan trọng vì chúng ta cần có khả năng ánh xạ các giá trị thô của các đối tượng địa lý phân loại để nhúng vectơ vào các mô hình của chúng ta. Để làm điều đó, chúng ta cần một từ vựng ánh xạ một giá trị đối tượng địa lý thô thành một số nguyên trong một phạm vi liền kề: điều này cho phép chúng ta tra cứu các nhúng tương ứng trong bảng nhúng của chúng ta.

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

Thực hiện một mô hình

Ngành kiến ​​trúc

Các mô hình xếp hạng không phải đối mặt với các hạn chế về hiệu quả giống như các mô hình truy xuất, và vì vậy chúng tôi có một chút tự do hơn trong việc lựa chọn kiến ​​trúc của mình.

Một mô hình bao gồm nhiều lớp dày đặc xếp chồng lên nhau là một kiến ​​trúc tương đối phổ biến để xếp hạng các tác vụ. Chúng ta có thể thực hiện nó như sau:

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

Mô hình này lấy id người dùng và tiêu đề phim và xuất ra xếp hạng dự đoán:

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

Mất mát và số liệu

Thành phần tiếp theo là tổn thất được sử dụng để đào tạo mô hình của chúng ta. TFRS có một số lớp tổn thất và các nhiệm vụ để làm cho việc này trở nên dễ dàng.

Trong trường hợp này, chúng tôi sẽ tận dụng các Ranking đối tượng nhiệm vụ: a wrapper tiện lợi mà bó cùng hàm tổn thất và tính toán số liệu.

Chúng tôi sẽ sử dụng nó cùng với MeanSquaredError mất Keras để dự đoán xếp hạng.

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

Bản thân tác vụ là một lớp Keras lấy giá trị true và được dự đoán làm đối số và trả về tổn thất được tính toán. Chúng tôi sẽ sử dụng nó để triển khai vòng lặp đào tạo của mô hình.

Mô hình đầy đủ

Bây giờ chúng ta có thể tập hợp tất cả lại thành một mô hình. TFR cho thấy một lớp mô hình cơ sở ( tfrs.models.Model ) mà sắp xếp mô hình cao ốc: tất cả chúng ta cần làm là thiết lập các thành phần trong __init__ phương pháp, và thực hiện các compute_loss phương pháp, dùng trong các tính năng thô và trả về một giá trị thiệt hại .

Sau đó, mô hình cơ sở sẽ đảm nhận việc tạo vòng lặp đào tạo thích hợp để phù hợp với mô hình của chúng tôi.

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)

Phù hợp và đánh giá

Sau khi xác định mô hình, chúng ta có thể sử dụng các quy trình đánh giá và lắp đặt tiêu chuẩn của Keras để điều chỉnh và đánh giá mô hình.

Đầu tiên hãy tạo mô hình.

model = MovielensModel()
model.compile(optimizer=tf.keras.optimizers.Adagrad(learning_rate=0.1))

Sau đó trộn, trộn và lưu vào bộ đệm dữ liệu đào tạo và đánh giá.

cached_train = train.shuffle(100_000).batch(8192).cache()
cached_test = test.batch(4096).cache()

Sau đó đào tạo mô hình:

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>

Khi mô hình đào tạo, mức lỗ sẽ giảm xuống và chỉ số RMSE đang được cải thiện.

Cuối cùng, chúng tôi có thể đánh giá mô hình của mình trên bộ thử nghiệm:

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}

Chỉ số RMSE càng thấp, mô hình của chúng tôi dự đoán xếp hạng càng chính xác.

Thử nghiệm mô hình xếp hạng

Giờ đây, chúng tôi có thể kiểm tra mô hình xếp hạng bằng cách tính toán các dự đoán cho một nhóm phim và sau đó xếp hạng các phim này dựa trên các dự đoán:

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

Xuất khẩu để phục vụ

Có thể dễ dàng xuất mô hình để phục vụ:

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

Bây giờ chúng tôi có thể tải lại và thực hiện các dự đoán:

loaded = tf.saved_model.load("export")

loaded({"user_id": np.array(["42"]), "movie_title": ["Speed (1994)"]}).numpy()
array([[3.5215874]], dtype=float32)

Bước tiếp theo

Mô hình trên cho chúng ta một khởi đầu thuận lợi để xây dựng một hệ thống xếp hạng.

Tất nhiên, việc tạo ra một hệ thống xếp hạng thực tế đòi hỏi nhiều nỗ lực hơn.

Trong hầu hết các trường hợp, mô hình xếp hạng có thể được cải thiện đáng kể bằng cách sử dụng nhiều tính năng hơn thay vì chỉ định danh người dùng và ứng viên. Để xem làm thế nào để làm điều đó, có một cái nhìn tại bên các tính năng hướng dẫn.

Việc hiểu kỹ càng về các mục tiêu cần tối ưu hóa cũng là điều cần thiết. Để bắt đầu xây dựng một recommender tối ưu hóa nhiều mục tiêu, có một cái nhìn tại của chúng tôi đa nhiệm hướng dẫn.