تاریخ را ذخیره کنید! Google I / O 18-20 مه بازمی گردد اکنون ثبت نام کنید
این صفحه به‌وسیله ‏Cloud Translation API‏ ترجمه شده است.
Switch to English

توصیه کنندگان چند کاره

مشاهده در 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)))

یک مدل چند کاره

توصیه کنندگان چند وظیفه ای دو بخش مهم دارند:

  1. آنها برای دو یا چند هدف بهینه می شوند و بنابراین دو یا بیشتر ضرر دارند.
  2. آنها متغیرها را بین وظایف به اشتراک می گذارند ، و این امکان را برای یادگیری انتقال می دهد.

در این آموزش ، ما مدل های خود را مانند گذشته تعریف خواهیم کرد ، اما به جای داشتن یک کار واحد ، دو وظیفه خواهیم داشت: یکی پیش بینی رتبه بندی ها ، و دیگری پیش بینی ساعت های فیلم.

مدل های کاربر و فیلم مانند گذشته است:

user_model = tf.keras.Sequential([
  tf.keras.layers.experimental.preprocessing.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.experimental.preprocessing.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)
    )
)

مانند قبل ، هدف این کار پیش بینی فیلم هایی است که کاربر تماشا می کند یا نمی کند.

کنار هم قرار دادنش

ما همه را با هم در یک کلاس مدل قرار می دهیم.

م componentلفه جدید در اینجا این است که - از آنجا که دو وظیفه و دو ضرر داریم - باید درمورد اهمیت هر ضرر تصمیم بگیریم. ما می توانیم این کار را با وزن دادن به هر یک از تلفات انجام دهیم و این وزنه ها را به عنوان ابر پارامترها در نظر بگیریم. اگر یک کاهش وزن بزرگ را به وظیفه رتبه بندی اختصاص دهیم ، مدل ما بر پیش بینی رتبه بندی متمرکز خواهد شد (اما هنوز هم از برخی از اطلاعات مربوط به کار بازیابی استفاده می کند). اگر ما یک کاهش وزن بزرگ را به کار بازیابی اختصاص دهیم ، در عوض بازیابی متمرکز می شود.

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.experimental.preprocessing.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.experimental.preprocessing.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
WARNING:tensorflow:The dtype of the source tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
WARNING:tensorflow:The dtype of the source tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
WARNING:tensorflow:Gradients do not exist for variables ['counter:0'] when minimizing the loss.
WARNING:tensorflow:Gradients do not exist for variables ['counter:0'] when minimizing the loss.
WARNING:tensorflow:The dtype of the source tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
WARNING:tensorflow:The dtype of the source tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
WARNING:tensorflow:Gradients do not exist for variables ['counter:0'] when minimizing the loss.
WARNING:tensorflow:Gradients do not exist for variables ['counter:0'] when minimizing the loss.
10/10 [==============================] - 2s 235ms/step - root_mean_squared_error: 2.0903 - factorized_top_k/top_1_categorical_accuracy: 4.0000e-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.0296 - factorized_top_k/top_100_categorical_accuracy: 0.0590 - loss: 4.0315 - regularization_loss: 0.0000e+00 - total_loss: 4.0315
Epoch 2/3
10/10 [==============================] - 2s 219ms/step - root_mean_squared_error: 1.1531 - factorized_top_k/top_1_categorical_accuracy: 3.8750e-04 - factorized_top_k/top_5_categorical_accuracy: 0.0026 - factorized_top_k/top_10_categorical_accuracy: 0.0055 - factorized_top_k/top_50_categorical_accuracy: 0.0298 - factorized_top_k/top_100_categorical_accuracy: 0.0593 - loss: 1.3189 - regularization_loss: 0.0000e+00 - total_loss: 1.3189
Epoch 3/3
10/10 [==============================] - 2s 219ms/step - root_mean_squared_error: 1.1198 - factorized_top_k/top_1_categorical_accuracy: 3.8750e-04 - factorized_top_k/top_5_categorical_accuracy: 0.0026 - factorized_top_k/top_10_categorical_accuracy: 0.0055 - factorized_top_k/top_50_categorical_accuracy: 0.0302 - factorized_top_k/top_100_categorical_accuracy: 0.0598 - loss: 1.2479 - regularization_loss: 0.0000e+00 - total_loss: 1.2479
5/5 [==============================] - 1s 107ms/step - root_mean_squared_error: 1.1130 - factorized_top_k/top_1_categorical_accuracy: 5.5000e-04 - factorized_top_k/top_5_categorical_accuracy: 0.0030 - factorized_top_k/top_10_categorical_accuracy: 0.0053 - factorized_top_k/top_50_categorical_accuracy: 0.0298 - factorized_top_k/top_100_categorical_accuracy: 0.0595 - 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
WARNING:tensorflow:The dtype of the source tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
WARNING:tensorflow:The dtype of the source tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
WARNING:tensorflow:Gradients do not exist for variables ['counter:0'] when minimizing the loss.
WARNING:tensorflow:Gradients do not exist for variables ['counter:0'] when minimizing the loss.
WARNING:tensorflow:The dtype of the source tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
WARNING:tensorflow:The dtype of the source tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
WARNING:tensorflow:Gradients do not exist for variables ['counter:0'] when minimizing the loss.
WARNING:tensorflow:Gradients do not exist for variables ['counter:0'] when minimizing the loss.
10/10 [==============================] - 2s 219ms/step - root_mean_squared_error: 3.7237 - factorized_top_k/top_1_categorical_accuracy: 0.0014 - factorized_top_k/top_5_categorical_accuracy: 0.0101 - factorized_top_k/top_10_categorical_accuracy: 0.0208 - factorized_top_k/top_50_categorical_accuracy: 0.1012 - factorized_top_k/top_100_categorical_accuracy: 0.1795 - loss: 69818.0284 - regularization_loss: 0.0000e+00 - total_loss: 69818.0284
Epoch 2/3
10/10 [==============================] - 2s 217ms/step - root_mean_squared_error: 3.7495 - factorized_top_k/top_1_categorical_accuracy: 0.0028 - factorized_top_k/top_5_categorical_accuracy: 0.0189 - factorized_top_k/top_10_categorical_accuracy: 0.0385 - factorized_top_k/top_50_categorical_accuracy: 0.1691 - factorized_top_k/top_100_categorical_accuracy: 0.2929 - loss: 67473.2876 - regularization_loss: 0.0000e+00 - total_loss: 67473.2876
Epoch 3/3
10/10 [==============================] - 2s 218ms/step - root_mean_squared_error: 3.7648 - factorized_top_k/top_1_categorical_accuracy: 0.0032 - factorized_top_k/top_5_categorical_accuracy: 0.0223 - factorized_top_k/top_10_categorical_accuracy: 0.0456 - factorized_top_k/top_50_categorical_accuracy: 0.1879 - factorized_top_k/top_100_categorical_accuracy: 0.3148 - loss: 66329.2514 - regularization_loss: 0.0000e+00 - total_loss: 66329.2514
5/5 [==============================] - 1s 108ms/step - root_mean_squared_error: 3.7730 - factorized_top_k/top_1_categorical_accuracy: 0.0011 - factorized_top_k/top_5_categorical_accuracy: 0.0098 - 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.2353 - 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
WARNING:tensorflow:The dtype of the source tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
WARNING:tensorflow:The dtype of the source tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
WARNING:tensorflow:Gradients do not exist for variables ['counter:0'] when minimizing the loss.
WARNING:tensorflow:Gradients do not exist for variables ['counter:0'] when minimizing the loss.
WARNING:tensorflow:The dtype of the source tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
WARNING:tensorflow:The dtype of the source tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
WARNING:tensorflow:Gradients do not exist for variables ['counter:0'] when minimizing the loss.
WARNING:tensorflow:Gradients do not exist for variables ['counter:0'] when minimizing the loss.
10/10 [==============================] - 2s 216ms/step - root_mean_squared_error: 2.5007 - factorized_top_k/top_1_categorical_accuracy: 0.0015 - factorized_top_k/top_5_categorical_accuracy: 0.0100 - factorized_top_k/top_10_categorical_accuracy: 0.0207 - factorized_top_k/top_50_categorical_accuracy: 0.1009 - factorized_top_k/top_100_categorical_accuracy: 0.1788 - loss: 69811.8239 - regularization_loss: 0.0000e+00 - total_loss: 69811.8239
Epoch 2/3
10/10 [==============================] - 2s 217ms/step - root_mean_squared_error: 1.2097 - factorized_top_k/top_1_categorical_accuracy: 0.0023 - factorized_top_k/top_5_categorical_accuracy: 0.0184 - factorized_top_k/top_10_categorical_accuracy: 0.0367 - factorized_top_k/top_50_categorical_accuracy: 0.1638 - factorized_top_k/top_100_categorical_accuracy: 0.2875 - loss: 67481.2727 - regularization_loss: 0.0000e+00 - total_loss: 67481.2727
Epoch 3/3
10/10 [==============================] - 2s 216ms/step - root_mean_squared_error: 1.1200 - factorized_top_k/top_1_categorical_accuracy: 0.0032 - factorized_top_k/top_5_categorical_accuracy: 0.0223 - factorized_top_k/top_10_categorical_accuracy: 0.0443 - factorized_top_k/top_50_categorical_accuracy: 0.1862 - factorized_top_k/top_100_categorical_accuracy: 0.3150 - loss: 66297.9304 - regularization_loss: 0.0000e+00 - total_loss: 66297.9304
5/5 [==============================] - 1s 111ms/step - root_mean_squared_error: 1.1312 - factorized_top_k/top_1_categorical_accuracy: 8.5000e-04 - factorized_top_k/top_5_categorical_accuracy: 0.0087 - factorized_top_k/top_10_categorical_accuracy: 0.0219 - factorized_top_k/top_50_categorical_accuracy: 0.1248 - factorized_top_k/top_100_categorical_accuracy: 0.2344 - loss: 31062.8213 - regularization_loss: 0.0000e+00 - total_loss: 31062.8213
Retrieval top-100 accuracy: 0.234.
Ranking RMSE: 1.131.

نتیجه مدلی است که تقریباً در هر دو وظیفه به خوبی عملکرد هر مدل خاص را دارد.

در حالی که نتایج در اینجا صحت واضحی از یک مدل مشترک در این مورد نشان نمی دهد ، یادگیری چند وظیفه به طور کلی یک ابزار بسیار مفید است. وقتی بتوانیم دانش را از یک کار فراوان داده (مانند کلیک) به یک کار پراکنده داده نزدیک (مانند خریدها) انتقال دهیم ، می توانیم نتیجه بهتری داشته باشیم.