Çoklu görev önerileri

TensorFlow.org'da görüntüleyin Google Colab'da çalıştırın Kaynağı GitHub'da görüntüleyin Not defterini indir

Gelen temel alma öğretici olumlu etkileşim sinyalleri olarak sinema saatler kullanan bir çıkarım sistemi kurdu.

Ancak birçok uygulamada, yararlanılabilecek çok sayıda zengin geri bildirim kaynağı vardır. Örneğin, bir e-ticaret sitesi, ürün sayfalarına yapılan kullanıcı ziyaretlerini (bol, ancak nispeten düşük sinyal), resim tıklamalarını, sepete ekleme ve son olarak satın alma işlemlerini kaydedebilir. İncelemeler ve iadeler gibi satın alma sonrası sinyalleri bile kaydedebilir.

Tüm bu farklı geri bildirim biçimlerini entegre etmek, kullanıcıların kullanmayı sevdiği ve genel performans pahasına herhangi bir ölçüm için optimize etmeyen sistemler oluşturmak için kritik öneme sahiptir.

Ek olarak, birden çok görev için ortak bir model oluşturmak, göreve özel bir dizi model oluşturmaktan daha iyi sonuçlar verebilir. Bu, özellikle bazı verilerin bol olduğu (örneğin tıklamalar) ve bazı verilerin seyrek olduğu (satın almalar, iadeler, manuel incelemeler) durumlarda geçerlidir. Bu senaryolarda, ortak modeli olarak bilinen bir fenomen aracılığıyla seyrek görev tahminlerini geliştirmek için bol görev öğrenilen temsillerini kullanmak mümkün olabilir transferi öğrenme . Örneğin, bu kağıt seyrek kullanıcı anketleri müstehcen Kullanıcı puanlarını öngören bir modeli esas bol tıklama günlük verilerini kullanan bir ek görevi ekleyerek geliştirilebilir olduğunu gösterir.

Bu öğreticide, hem örtük (film izleme) hem de açık sinyalleri (derecelendirmeler) kullanarak Movielens için çok amaçlı bir öneri aracı oluşturacağız.

ithalat

Önce ithalatımızı aradan çıkaralım.

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

Veri kümesinin hazırlanması

Movielens 100K veri setini kullanacağız.

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

Ve kelime dağarcığı oluşturmak ve verileri bir tren ve bir test setine bölmek için hazırlıklarımızı tekrarlayın:

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

Çok görevli bir model

Çok görevli tavsiyeciler için iki kritik bölüm vardır:

  1. İki veya daha fazla hedef için optimize ederler ve bu nedenle iki veya daha fazla kayıp verirler.
  2. Görevler arasında değişkenleri paylaşarak öğrenmeyi aktarmaya izin verirler.

Bu derste modellerimizi eskisi gibi tanımlayacağız, ancak tek bir görev yerine iki görevimiz olacak: Biri reytingleri tahmin etmek, diğeri film izleme tahmin etmek.

Kullanıcı ve film modelleri eskisi gibi:

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

Ancak şimdi iki görevimiz olacak. Birincisi, derecelendirme görevidir:

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

Amacı, derecelendirmeleri mümkün olduğunca doğru bir şekilde tahmin etmektir.

İkincisi, geri alma görevidir:

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

Daha önce olduğu gibi, bu görevin amacı, kullanıcının hangi filmleri izleyip izlemeyeceğini tahmin etmektir.

bir araya getirmek

Hepsini bir model sınıfında bir araya getirdik.

Buradaki yeni bileşen, - iki görevimiz ve iki kaybımız olduğu için - her bir kaybın ne kadar önemli olduğuna karar vermemiz gerektiğidir. Bunu, kayıpların her birine bir ağırlık vererek ve bu ağırlıkları hiperparametreler olarak ele alarak yapabiliriz. Derecelendirme görevine büyük bir kayıp ağırlığı atarsak, modelimiz derecelendirmeleri tahmin etmeye odaklanacaktır (ancak yine de alma görevinden bazı bilgileri kullanır); alma görevine büyük bir kayıp ağırlığı atarsak, bunun yerine geri almaya odaklanacaktır.

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)

Derecelendirmeye özel model

Atadığımız ağırlıklara bağlı olarak model, görevlerin farklı bir dengesini kodlayacaktır. Yalnızca derecelendirmeleri dikkate alan bir modelle başlayalım.

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.

Model, derecelendirmeleri tahmin etmede başarılıdır (yaklaşık 1,11'lik bir RMSE ile), ancak hangi filmlerin izleneceğini veya izlenmeyeceğini tahmin etmede zayıf bir performans sergiliyor: 100'deki doğruluğu, yalnızca saatleri tahmin etmek için eğitilmiş bir modelden neredeyse 4 kat daha kötü.

Alma-özelleştirilmiş model

Şimdi sadece geri almaya odaklanan bir model deneyelim.

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.

Tam tersi bir sonuç elde ederiz: geri almada iyi olan, ancak reytingleri tahmin etmede zayıf olan bir model.

ortak model

Şimdi her iki göreve de pozitif ağırlıklar atayan bir model eğitelim.

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.

Sonuç, her bir özel model gibi her iki görevde de kabaca iyi performans gösteren bir modeldir.

tahmin yapma

Eğitilmiş kullanıcı ve film yerleştirmelerinin yanı sıra öngörülen derecelendirmeyi elde etmek için eğitilmiş çoklu görev modelini kullanabiliriz:

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)

Buradaki sonuçlar, bu durumda ortak bir modelden net bir doğruluk avantajı göstermese de, çok görevli öğrenme genel olarak son derece kullanışlı bir araçtır. Bilgiyi bol miktarda veri içeren bir görevden (tıklamalar gibi) yakından ilişkili veri seyrek bir göreve (satın almalar gibi) aktarabildiğimiz zaman daha iyi sonuçlar bekleyebiliriz.