Bağlam özelliklerinden yararlanma

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

Özellik oluşturma eğitiminde , yalnızca kullanıcı ve film tanımlayıcılarının ötesinde birden çok özelliği modellerimize dahil ettik, ancak bu özelliklerin model doğruluğunu iyileştirip iyileştirmediğini keşfetmedik.

Bir öneri modelinde kimliklerin ötesindeki özelliklerin yararlı olup olmadığını birçok faktör etkiler:

  1. Bağlamın Önemi : Kullanıcı tercihleri, bağlamlar ve zaman boyunca nispeten sabitse, bağlam özellikleri pek fayda sağlamayabilir. Bununla birlikte, kullanıcı tercihleri ​​oldukça bağlamsal ise, bağlam eklemek modeli önemli ölçüde iyileştirecektir. Örneğin, haftanın günü, kısa bir klip mi yoksa bir film mi önerileceğine karar verirken önemli bir özellik olabilir: kullanıcıların yalnızca hafta boyunca kısa içerik izlemek için zamanları olabilir, ancak hafta sonu rahatlayabilir ve uzun metrajlı bir filmin keyfini çıkarabilirler. . Benzer şekilde, sorgu zaman damgaları popülerlik dinamiklerini modellemede önemli bir rol oynayabilir: bir film yayınlandığı zaman oldukça popüler olabilir, ancak daha sonra hızla bozulabilir. Tersine, diğer filmler tekrar tekrar mutlu bir şekilde izlenen her zaman yeşil kalan filmler olabilir.
  2. Veri seyrekliği : Veriler seyrekse, kimlik dışı özelliklerin kullanılması kritik olabilir. Belirli bir kullanıcı veya öğe için birkaç gözlem mevcut olduğundan, model, kullanıcı başına veya öğe başına iyi bir temsilin tahmin edilmesinde güçlük çekebilir. Doğru bir model oluşturmak için, modelin eğitim verilerinin ötesinde genelleştirilmesine yardımcı olmak için öğe kategorileri, açıklamalar ve resimler gibi diğer özellikler kullanılmalıdır. Bu, özellikle bazı öğeler veya kullanıcılar hakkında nispeten az verinin mevcut olduğu soğuk çalıştırma durumlarıyla ilgilidir.

Bu eğitimde, MovieLens modelimizde film başlıklarının ve kullanıcı kimliklerinin ötesindeki özellikleri kullanmayı deneyeceğiz.

ön elemeler

Öncelikle gerekli paketleri import ediyoruz.

pip install -q tensorflow-recommenders
pip install -q --upgrade tensorflow-datasets
tutucu1 l10n-yer
import os
import tempfile

import numpy as np
import tensorflow as tf
import tensorflow_datasets as tfds

import tensorflow_recommenders as tfrs

Özelleştirme eğitimini takip ediyoruz ve kullanıcı kimliği, zaman damgası ve film başlığı özelliklerini koruyoruz.

ratings = tfds.load("movielens/100k-ratings", split="train")
movies = tfds.load("movielens/100k-movies", split="train")

ratings = ratings.map(lambda x: {
    "movie_title": x["movie_title"],
    "user_id": x["user_id"],
    "timestamp": x["timestamp"],
})
movies = movies.map(lambda x: x["movie_title"])

Ayrıca, özellik sözlükleri hazırlamak için bazı temizlik işleri yapıyoruz.

timestamps = np.concatenate(list(ratings.map(lambda x: x["timestamp"]).batch(100)))

max_timestamp = timestamps.max()
min_timestamp = timestamps.min()

timestamp_buckets = np.linspace(
    min_timestamp, max_timestamp, num=1000,
)

unique_movie_titles = np.unique(np.concatenate(list(movies.batch(1000))))
unique_user_ids = np.unique(np.concatenate(list(ratings.batch(1_000).map(
    lambda x: x["user_id"]))))

Model tanımı

sorgu modeli

Ham girdi örneklerini özellik yerleştirmelerine dönüştürmekle görevli, modelimizin ilk katmanı olarak özellik oluşturma eğitiminde tanımlanan kullanıcı modeliyle başlıyoruz. Ancak, zaman damgası özelliklerini açmamıza veya kapatmamıza izin vermek için biraz değiştiriyoruz. Bu, zaman damgası özelliklerinin model üzerindeki etkisini daha kolay göstermemizi sağlayacaktır. Aşağıdaki kodda, use_timestamps parametresi bize zaman damgası özelliklerini kullanıp kullanmayacağımız konusunda kontrol sağlar.

class UserModel(tf.keras.Model):

  def __init__(self, use_timestamps):
    super().__init__()

    self._use_timestamps = use_timestamps

    self.user_embedding = tf.keras.Sequential([
        tf.keras.layers.StringLookup(
            vocabulary=unique_user_ids, mask_token=None),
        tf.keras.layers.Embedding(len(unique_user_ids) + 1, 32),
    ])

    if use_timestamps:
      self.timestamp_embedding = tf.keras.Sequential([
          tf.keras.layers.Discretization(timestamp_buckets.tolist()),
          tf.keras.layers.Embedding(len(timestamp_buckets) + 1, 32),
      ])
      self.normalized_timestamp = tf.keras.layers.Normalization(
          axis=None
      )

      self.normalized_timestamp.adapt(timestamps)

  def call(self, inputs):
    if not self._use_timestamps:
      return self.user_embedding(inputs["user_id"])

    return tf.concat([
        self.user_embedding(inputs["user_id"]),
        self.timestamp_embedding(inputs["timestamp"]),
        tf.reshape(self.normalized_timestamp(inputs["timestamp"]), (-1, 1)),
    ], axis=1)

Bu eğitimdeki zaman damgası özelliklerini kullanmamızın, eğitim-test ayrımı seçimimizle istenmeyen bir şekilde etkileşime girdiğini unutmayın. Verilerimizi kronolojik değil rastgele böldüğümüz için (test veri kümesine ait olayların eğitim kümesindeki olaylardan daha sonra gerçekleşmesini sağlamak için), modelimiz gelecekten etkili bir şekilde öğrenebilir. Bu gerçekçi değil: Ne de olsa bugünden yarının verileriyle bir model yetiştiremeyiz.

Bu, modele zaman özellikleri eklemenin gelecekteki etkileşim modellerini öğrenmesini sağladığı anlamına gelir. Bunu yalnızca örnekleme amacıyla yapıyoruz: MovieLens veri kümesinin kendisi çok yoğundur ve gerçek dünyadaki birçok veri kümesinin aksine, kullanıcı kimlikleri ve film adlarının ötesindeki özelliklerden pek faydalanmaz.

Bu uyarı bir yana, gerçek dünya modelleri, özellikle veriler güçlü mevsimsel kalıplara sahipse, günün saati veya haftanın günü gibi diğer zamana dayalı özelliklerden yararlanabilir.

aday modeli

Basit olması için aday modeli sabit tutacağız. Yine, onu özellikleştirme eğitiminden kopyalıyoruz :

class MovieModel(tf.keras.Model):

  def __init__(self):
    super().__init__()

    max_tokens = 10_000

    self.title_embedding = tf.keras.Sequential([
      tf.keras.layers.StringLookup(
          vocabulary=unique_movie_titles, mask_token=None),
      tf.keras.layers.Embedding(len(unique_movie_titles) + 1, 32)
    ])

    self.title_vectorizer = tf.keras.layers.TextVectorization(
        max_tokens=max_tokens)

    self.title_text_embedding = tf.keras.Sequential([
      self.title_vectorizer,
      tf.keras.layers.Embedding(max_tokens, 32, mask_zero=True),
      tf.keras.layers.GlobalAveragePooling1D(),
    ])

    self.title_vectorizer.adapt(movies)

  def call(self, titles):
    return tf.concat([
        self.title_embedding(titles),
        self.title_text_embedding(titles),
    ], axis=1)

kombine model

Hem UserModel hem de MovieModel tanımlıyken, birleşik bir model oluşturabilir ve kayıp ve ölçüm mantığımızı uygulayabiliriz.

Burada bir geri alma modeli oluşturuyoruz. Bunun nasıl çalıştığı hakkında bilgi tazelemek için Temel alma öğreticisine bakın.

Ayrıca, sorgu modelinin ve aday model çıktısının uyumlu boyutta gömülü olduğundan emin olmamız gerektiğini unutmayın. Boyutlarını daha fazla özellik ekleyerek değiştireceğimiz için, bunu gerçekleştirmenin en kolay yolu, her modelden sonra yoğun bir projeksiyon katmanı kullanmaktır:

class MovielensModel(tfrs.models.Model):

  def __init__(self, use_timestamps):
    super().__init__()
    self.query_model = tf.keras.Sequential([
      UserModel(use_timestamps),
      tf.keras.layers.Dense(32)
    ])
    self.candidate_model = tf.keras.Sequential([
      MovieModel(),
      tf.keras.layers.Dense(32)
    ])
    self.task = tfrs.tasks.Retrieval(
        metrics=tfrs.metrics.FactorizedTopK(
            candidates=movies.batch(128).map(self.candidate_model),
        ),
    )

  def compute_loss(self, features, training=False):
    # We only pass the user id and timestamp features into the query model. This
    # is to ensure that the training inputs would have the same keys as the
    # query inputs. Otherwise the discrepancy in input structure would cause an
    # error when loading the query model after saving it.
    query_embeddings = self.query_model({
        "user_id": features["user_id"],
        "timestamp": features["timestamp"],
    })
    movie_embeddings = self.candidate_model(features["movie_title"])

    return self.task(query_embeddings, movie_embeddings)

deneyler

Verileri hazırlayın

İlk önce verileri bir eğitim seti ve bir test seti olarak ayırdık.

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)

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

Temel: zaman damgası özelliği yok

İlk modelimizi denemeye hazırız: Temel çizgimizi oluşturmak için zaman damgası özelliklerini kullanmayarak başlayalım.

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

model.fit(cached_train, epochs=3)

train_accuracy = model.evaluate(
    cached_train, return_dict=True)["factorized_top_k/top_100_categorical_accuracy"]
test_accuracy = model.evaluate(
    cached_test, return_dict=True)["factorized_top_k/top_100_categorical_accuracy"]

print(f"Top-100 accuracy (train): {train_accuracy:.2f}.")
print(f"Top-100 accuracy (test): {test_accuracy:.2f}.")
tutucu9 l10n-yer
Epoch 1/3
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
40/40 [==============================] - 10s 169ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0092 - factorized_top_k/top_5_categorical_accuracy: 0.0172 - factorized_top_k/top_10_categorical_accuracy: 0.0256 - factorized_top_k/top_50_categorical_accuracy: 0.0824 - factorized_top_k/top_100_categorical_accuracy: 0.1473 - loss: 14579.4628 - regularization_loss: 0.0000e+00 - total_loss: 14579.4628
Epoch 2/3
40/40 [==============================] - 9s 173ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0020 - factorized_top_k/top_5_categorical_accuracy: 0.0126 - factorized_top_k/top_10_categorical_accuracy: 0.0251 - factorized_top_k/top_50_categorical_accuracy: 0.1129 - factorized_top_k/top_100_categorical_accuracy: 0.2133 - loss: 14136.2137 - regularization_loss: 0.0000e+00 - total_loss: 14136.2137
Epoch 3/3
40/40 [==============================] - 9s 174ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0021 - factorized_top_k/top_5_categorical_accuracy: 0.0155 - factorized_top_k/top_10_categorical_accuracy: 0.0307 - factorized_top_k/top_50_categorical_accuracy: 0.1389 - factorized_top_k/top_100_categorical_accuracy: 0.2535 - loss: 13939.9265 - regularization_loss: 0.0000e+00 - total_loss: 13939.9265
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
40/40 [==============================] - 10s 189ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0036 - factorized_top_k/top_5_categorical_accuracy: 0.0226 - factorized_top_k/top_10_categorical_accuracy: 0.0427 - factorized_top_k/top_50_categorical_accuracy: 0.1729 - factorized_top_k/top_100_categorical_accuracy: 0.2944 - loss: 13711.3802 - regularization_loss: 0.0000e+00 - total_loss: 13711.3802
5/5 [==============================] - 3s 267ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0010 - factorized_top_k/top_5_categorical_accuracy: 0.0078 - factorized_top_k/top_10_categorical_accuracy: 0.0184 - factorized_top_k/top_50_categorical_accuracy: 0.1051 - factorized_top_k/top_100_categorical_accuracy: 0.2126 - loss: 30995.8988 - regularization_loss: 0.0000e+00 - total_loss: 30995.8988
Top-100 accuracy (train): 0.29.
Top-100 accuracy (test): 0.21.

Bu bize 0,2 civarında bir temel top-100 doğruluğu verir.

Zaman özellikleriyle zaman dinamiklerini yakalama

Zaman özelliklerini eklersek sonuç değişir mi?

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

model.fit(cached_train, epochs=3)

train_accuracy = model.evaluate(
    cached_train, return_dict=True)["factorized_top_k/top_100_categorical_accuracy"]
test_accuracy = model.evaluate(
    cached_test, return_dict=True)["factorized_top_k/top_100_categorical_accuracy"]

print(f"Top-100 accuracy (train): {train_accuracy:.2f}.")
print(f"Top-100 accuracy (test): {test_accuracy:.2f}.")
tutucu11 l10n-yer
Epoch 1/3
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
40/40 [==============================] - 10s 175ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0057 - factorized_top_k/top_5_categorical_accuracy: 0.0148 - factorized_top_k/top_10_categorical_accuracy: 0.0238 - factorized_top_k/top_50_categorical_accuracy: 0.0812 - factorized_top_k/top_100_categorical_accuracy: 0.1487 - loss: 14606.0927 - regularization_loss: 0.0000e+00 - total_loss: 14606.0927
Epoch 2/3
40/40 [==============================] - 9s 176ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0026 - factorized_top_k/top_5_categorical_accuracy: 0.0153 - factorized_top_k/top_10_categorical_accuracy: 0.0304 - factorized_top_k/top_50_categorical_accuracy: 0.1375 - factorized_top_k/top_100_categorical_accuracy: 0.2512 - loss: 13958.5635 - regularization_loss: 0.0000e+00 - total_loss: 13958.5635
Epoch 3/3
40/40 [==============================] - 9s 177ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0026 - factorized_top_k/top_5_categorical_accuracy: 0.0189 - factorized_top_k/top_10_categorical_accuracy: 0.0393 - factorized_top_k/top_50_categorical_accuracy: 0.1713 - factorized_top_k/top_100_categorical_accuracy: 0.3015 - loss: 13696.8511 - regularization_loss: 0.0000e+00 - total_loss: 13696.8511
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
40/40 [==============================] - 9s 172ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0050 - factorized_top_k/top_5_categorical_accuracy: 0.0323 - factorized_top_k/top_10_categorical_accuracy: 0.0606 - factorized_top_k/top_50_categorical_accuracy: 0.2254 - factorized_top_k/top_100_categorical_accuracy: 0.3637 - loss: 13382.7869 - regularization_loss: 0.0000e+00 - total_loss: 13382.7869
5/5 [==============================] - 1s 237ms/step - 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.0214 - factorized_top_k/top_50_categorical_accuracy: 0.1259 - factorized_top_k/top_100_categorical_accuracy: 0.2468 - loss: 30699.8529 - regularization_loss: 0.0000e+00 - total_loss: 30699.8529
Top-100 accuracy (train): 0.36.
Top-100 accuracy (test): 0.25.

Bu biraz daha iyi: sadece eğitim doğruluğu çok daha yüksek değil, aynı zamanda test doğruluğu da önemli ölçüde iyileştirildi.

Sonraki adımlar

Bu öğretici, daha fazla özellik birleştirildiğinde basit modellerin bile daha doğru hale gelebileceğini göstermektedir. Ancak, özelliklerinizden en iyi şekilde yararlanmak için genellikle daha büyük, daha derin modeller oluşturmanız gerekir. Bunu daha ayrıntılı olarak keşfetmek için derin erişim eğitimine bir göz atın.