संदर्भ सुविधाओं का लाभ उठाते हुए

TensorFlow.org पर देखें Google Colab में चलाएं GitHub पर स्रोत देखें नोटबुक डाउनलोड करें

फीचराइजेशन ट्यूटोरियल में हमने अपने मॉडल में सिर्फ यूजर और मूवी आइडेंटिफायर के अलावा कई फीचर्स को शामिल किया है, लेकिन हमने यह पता नहीं लगाया है कि क्या वे फीचर्स मॉडल सटीकता में सुधार करते हैं।

कई कारक प्रभावित करते हैं कि क्या अनुशंसाकर्ता मॉडल में आईडी से परे सुविधाएं उपयोगी हैं:

  1. संदर्भ का महत्व : यदि उपयोगकर्ता वरीयताएँ संदर्भों और समय में अपेक्षाकृत स्थिर हैं, तो संदर्भ सुविधाएँ अधिक लाभ प्रदान नहीं कर सकती हैं। यदि, हालांकि, उपयोगकर्ता प्राथमिकताएं अत्यधिक प्रासंगिक हैं, तो संदर्भ जोड़ने से मॉडल में उल्लेखनीय सुधार होगा। उदाहरण के लिए, सप्ताह का दिन यह तय करते समय एक महत्वपूर्ण विशेषता हो सकती है कि क्या एक छोटी क्लिप या फिल्म की सिफारिश की जाए: उपयोगकर्ताओं के पास सप्ताह के दौरान केवल छोटी सामग्री देखने का समय हो सकता है, लेकिन वे आराम कर सकते हैं और सप्ताहांत के दौरान पूरी लंबाई वाली फिल्म का आनंद ले सकते हैं। . इसी तरह, क्वेरी टाइमस्टैम्प लोकप्रियता की गतिशीलता को मॉडलिंग करने में एक महत्वपूर्ण भूमिका निभा सकते हैं: एक फिल्म अपनी रिलीज के समय अत्यधिक लोकप्रिय हो सकती है, लेकिन बाद में जल्दी ही क्षय हो जाती है। इसके विपरीत, अन्य फिल्में सदाबहार हो सकती हैं जिन्हें बार-बार खुशी-खुशी देखा जाता है।
  2. डेटा विरलता : डेटा विरल होने पर गैर-आईडी सुविधाओं का उपयोग करना महत्वपूर्ण हो सकता है। किसी दिए गए उपयोगकर्ता या आइटम के लिए उपलब्ध कुछ अवलोकनों के साथ, मॉडल प्रति-उपयोगकर्ता या प्रति-आइटम प्रतिनिधित्व के अच्छे अनुमान के साथ संघर्ष कर सकता है। एक सटीक मॉडल बनाने के लिए, प्रशिक्षण डेटा से परे मॉडल को सामान्य बनाने में मदद करने के लिए आइटम श्रेणियों, विवरण और छवियों जैसी अन्य विशेषताओं का उपयोग करना होगा। यह कोल्ड-स्टार्ट स्थितियों में विशेष रूप से प्रासंगिक है, जहां कुछ वस्तुओं या उपयोगकर्ताओं पर अपेक्षाकृत कम डेटा उपलब्ध है।

इस ट्यूटोरियल में, हम मूवी टाइटल और यूजर आईडी से परे हमारे MovieLens मॉडल में सुविधाओं का उपयोग करने के साथ प्रयोग करेंगे।

प्रारंभिक

हम पहले आवश्यक पैकेज आयात करते हैं।

pip install -q tensorflow-recommenders
pip install -q --upgrade tensorflow-datasets
import os
import tempfile

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

import tensorflow_recommenders as tfrs

हम फीचराइजेशन ट्यूटोरियल का पालन करते हैं और यूजर आईडी, टाइमस्टैम्प और मूवी टाइटल फीचर रखते हैं।

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

फीचर शब्दसंग्रह तैयार करने के लिए हम कुछ हाउसकीपिंग भी करते हैं।

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

मॉडल परिभाषा

क्वेरी मॉडल

हम अपने मॉडल की पहली परत के रूप में फीचराइजेशन ट्यूटोरियल में परिभाषित उपयोगकर्ता मॉडल के साथ शुरू करते हैं, जो कच्चे इनपुट उदाहरणों को फीचर एम्बेडिंग में बदलने का काम करता है। हालांकि, हम टाइमस्टैम्प सुविधाओं को चालू या बंद करने की अनुमति देने के लिए इसे थोड़ा बदलते हैं। यह हमें मॉडल पर टाइमस्टैम्प सुविधाओं के प्रभाव को अधिक आसानी से प्रदर्शित करने की अनुमति देगा। नीचे दिए गए कोड में, use_timestamps पैरामीटर हमें इस पर नियंत्रण देता है कि हम टाइमस्टैम्प सुविधाओं का उपयोग करते हैं या नहीं।

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)

ध्यान दें कि इस ट्यूटोरियल में टाइमस्टैम्प सुविधाओं का हमारा उपयोग अवांछित तरीके से प्रशिक्षण-परीक्षण विभाजन की हमारी पसंद के साथ इंटरैक्ट करता है। क्योंकि हमने अपने डेटा को कालानुक्रमिक रूप से विभाजित करने के बजाय यादृच्छिक रूप से विभाजित किया है (यह सुनिश्चित करने के लिए कि परीक्षण डेटासेट से संबंधित घटनाएं प्रशिक्षण सेट की तुलना में बाद में होती हैं), हमारा मॉडल भविष्य से प्रभावी ढंग से सीख सकता है। यह अवास्तविक है: आखिरकार, हम कल के डेटा पर आज के मॉडल को प्रशिक्षित नहीं कर सकते।

इसका मतलब यह है कि मॉडल में समय की विशेषताओं को जोड़ने से यह भविष्य के इंटरैक्शन पैटर्न सीख सकता है। हम इसे केवल दृष्टांत उद्देश्यों के लिए करते हैं: MovieLens डेटासेट अपने आप में बहुत सघन है, और कई वास्तविक दुनिया के डेटासेट के विपरीत उपयोगकर्ता आईडी और मूवी शीर्षक से परे सुविधाओं से बहुत अधिक लाभ नहीं होता है।

यह चेतावनी एक तरफ, वास्तविक दुनिया के मॉडल अन्य समय-आधारित सुविधाओं जैसे कि दिन के समय या सप्ताह के दिन से अच्छी तरह से लाभान्वित हो सकते हैं, खासकर यदि डेटा में मजबूत मौसमी पैटर्न हैं।

उम्मीदवार मॉडल

सरलता के लिए, हम उम्मीदवार मॉडल को स्थिर रखेंगे। फिर से, हम इसे फीचराइजेशन ट्यूटोरियल से कॉपी करते हैं:

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)

संयुक्त मॉडल

UserModel और MovieModel दोनों को परिभाषित करने के साथ, हम एक संयुक्त मॉडल को एक साथ रख सकते हैं और हमारे नुकसान और मेट्रिक्स तर्क को लागू कर सकते हैं।

यहां हम एक पुनर्प्राप्ति मॉडल बना रहे हैं। यह कैसे काम करता है, इस पर एक पुनश्चर्या के लिए, मूल पुनर्प्राप्ति ट्यूटोरियल देखें।

ध्यान दें कि हमें यह भी सुनिश्चित करने की आवश्यकता है कि क्वेरी मॉडल और उम्मीदवार मॉडल संगत आकार के आउटपुट एम्बेडिंग करें। क्योंकि हम अधिक सुविधाओं को जोड़कर उनके आकार में बदलाव करेंगे, इसे पूरा करने का सबसे आसान तरीका प्रत्येक मॉडल के बाद एक सघन प्रक्षेपण परत का उपयोग करना है:

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)

प्रयोगों

डेटा तैयार करें

हम पहले डेटा को एक प्रशिक्षण सेट और एक परीक्षण सेट में विभाजित करते हैं।

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

आधार रेखा: कोई टाइमस्टैम्प सुविधाएँ नहीं

हम अपना पहला मॉडल आज़माने के लिए तैयार हैं: आइए अपनी आधार रेखा स्थापित करने के लिए टाइमस्टैम्प सुविधाओं का उपयोग न करने के साथ शुरू करें।

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}.")
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.

यह हमें लगभग 0.2 की बेसलाइन टॉप-100 सटीकता देता है।

समय की विशेषताओं के साथ समय की गतिशीलता को कैप्चर करना

अगर हम समय की विशेषताओं को जोड़ते हैं तो क्या परिणाम बदल जाता है?

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}.")
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.

यह काफी बेहतर है: न केवल प्रशिक्षण सटीकता बहुत अधिक है, बल्कि परीक्षण सटीकता में भी काफी सुधार हुआ है।

अगले कदम

यह ट्यूटोरियल दिखाता है कि अधिक सुविधाओं को शामिल करते समय सरल मॉडल भी अधिक सटीक हो सकते हैं। हालांकि, अपनी सुविधाओं का अधिकतम लाभ उठाने के लिए अक्सर बड़े, गहरे मॉडल बनाना आवश्यक होता है। इसे और अधिक विस्तार से जानने के लिए डीप रिट्रीवल ट्यूटोरियल पर एक नजर डालें।