साइड फीचर्स का उपयोग करना: फीचर प्रीप्रोसेसिंग

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

अनुशंसा मॉडल बनाने के लिए गहन शिक्षण ढांचे का उपयोग करने के महान लाभों में से एक समृद्ध, लचीली सुविधा अभ्यावेदन बनाने की स्वतंत्रता है।

ऐसा करने में पहला कदम सुविधाओं को तैयार कर रहा है, क्योंकि कच्चे फीचर आमतौर पर एक मॉडल में तुरंत उपयोग करने योग्य नहीं होंगे।

उदाहरण के लिए:

  • उपयोगकर्ता और आइटम आईडी स्ट्रिंग (शीर्षक, उपयोगकर्ता नाम) या बड़े, गैर-सन्निहित पूर्णांक (डेटाबेस आईडी) हो सकते हैं।
  • आइटम विवरण कच्चा पाठ हो सकता है।
  • इंटरेक्शन टाइमस्टैम्प कच्चे यूनिक्स टाइमस्टैम्प हो सकते हैं।

मॉडल बनाने में उपयोगी होने के लिए इन्हें उचित रूप से रूपांतरित करने की आवश्यकता है:

  • उपयोगकर्ता और आइटम आईडी को एम्बेडिंग वैक्टर में अनुवादित किया जाना है: उच्च-आयामी संख्यात्मक प्रतिनिधित्व जो प्रशिक्षण के दौरान समायोजित किए जाते हैं ताकि मॉडल को अपने उद्देश्य की बेहतर भविष्यवाणी करने में मदद मिल सके।
  • कच्चे पाठ को टोकननाइज़ करने की आवश्यकता है (छोटे भागों जैसे अलग-अलग शब्दों में विभाजित) और एम्बेडिंग में अनुवादित किया जाना चाहिए।
  • संख्यात्मक विशेषताओं को सामान्यीकृत करने की आवश्यकता है ताकि उनके मान 0 के आसपास एक छोटे से अंतराल में हों।

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

इस ट्यूटोरियल में, हम अनुसंशाएं पर ध्यान केंद्रित करने जा रहे हैं और preprocessing हम पर क्या करने की जरूरत MovieLens डाटासेट । यदि आप एक recommender प्रणाली फोकस के बिना एक बड़ा ट्यूटोरियल में रुचि रखते हैं, पूर्ण पर एक नजर है Keras preprocessing गाइड

MovieLens डेटासेट

आइए पहले देखें कि MovieLens डेटासेट से हम किन विशेषताओं का उपयोग कर सकते हैं:

pip install -q --upgrade tensorflow-datasets
import pprint

import tensorflow_datasets as tfds

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

for x in ratings.take(1).as_numpy_iterator():
  pprint.pprint(x)
2021-10-02 11:59:46.956587: E tensorflow/stream_executor/cuda/cuda_driver.cc:271] failed call to cuInit: CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected
{'bucketized_user_age': 45.0,
 'movie_genres': array([7]),
 'movie_id': b'357',
 'movie_title': b"One Flew Over the Cuckoo's Nest (1975)",
 'raw_user_age': 46.0,
 'timestamp': 879024327,
 'user_gender': True,
 'user_id': b'138',
 'user_occupation_label': 4,
 'user_occupation_text': b'doctor',
 'user_rating': 4.0,
 'user_zip_code': b'53211'}
2021-10-02 11:59:47.327679: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset  will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.

यहां कुछ प्रमुख विशेषताएं हैं:

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

पहले दो स्पष्ट विशेषताएं हैं; टाइमस्टैम्प एक सतत विशेषता है।

श्रेणीबद्ध सुविधाओं को एम्बेडिंग में बदलना

एक स्पष्ट सुविधा एक विशेषता यह है कि एक सतत मात्रा व्यक्त नहीं करता है, बल्कि तय मानों का एक सेट में से एक पर ले जाता है।

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

उदाहरण के लिए, मान लीजिए कि हमारा लक्ष्य यह अनुमान लगाना है कि कौन सा उपयोगकर्ता कौन सी फिल्म देखने जा रहा है। ऐसा करने के लिए, हम एक एम्बेडिंग वेक्टर द्वारा प्रत्येक उपयोगकर्ता और प्रत्येक मूवी का प्रतिनिधित्व करते हैं। प्रारंभ में, ये एम्बेडिंग यादृच्छिक मान लेंगे - लेकिन प्रशिक्षण के दौरान, हम उन्हें समायोजित करेंगे ताकि उपयोगकर्ताओं की एम्बेडिंग और उनके द्वारा देखी जाने वाली फिल्में एक साथ समाप्त हो जाएं।

कच्ची स्पष्ट विशेषताओं को लेना और उन्हें एम्बेडिंग में बदलना आम तौर पर दो चरणों वाली प्रक्रिया है:

  1. सबसे पहले, हमें आम तौर पर एक मैपिंग (जिसे "शब्दावली" कहा जाता है) बनाकर कच्चे मूल्यों को सन्निहित पूर्णांकों की एक श्रृंखला में अनुवाद करने की आवश्यकता होती है, जो कच्चे मूल्यों ("स्टार वार्स") को पूर्णांक (जैसे, 15) में मैप करता है।
  2. दूसरे, हमें इन पूर्णांकों को लेने और उन्हें एम्बेडिंग में बदलने की आवश्यकता है।

शब्दावली को परिभाषित करना

शब्दावली को परिभाषित करने के लिए पहला कदम है। केरस प्रीप्रोसेसिंग लेयर्स का उपयोग करके हम इसे आसानी से कर सकते हैं।

import numpy as np
import tensorflow as tf

movie_title_lookup = tf.keras.layers.StringLookup()

परत में अभी तक कोई शब्दावली नहीं है, लेकिन हम इसे अपने डेटा का उपयोग करके बना सकते हैं।

movie_title_lookup.adapt(ratings.map(lambda x: x["movie_title"]))

print(f"Vocabulary: {movie_title_lookup.get_vocabulary()[:3]}")
Vocabulary: ['[UNK]', 'Star Wars (1977)', 'Contact (1997)']

एक बार हमारे पास यह हो जाने के बाद हम परत का उपयोग कच्चे टोकन को एम्बेडिंग आईडी में अनुवाद करने के लिए कर सकते हैं:

movie_title_lookup(["Star Wars (1977)", "One Flew Over the Cuckoo's Nest (1975)"])
<tf.Tensor: shape=(2,), dtype=int64, numpy=array([ 1, 58])>

ध्यान दें कि परत की शब्दावली में एक (या अधिक!) अज्ञात (या "शब्दावली से बाहर", OOV) टोकन शामिल हैं। यह वास्तव में आसान है: इसका मतलब है कि परत श्रेणीबद्ध मूल्यों को संभाल सकती है जो शब्दावली में नहीं हैं। व्यावहारिक रूप से, इसका मतलब यह है कि मॉडल शब्दावली निर्माण के दौरान नहीं देखी गई सुविधाओं का उपयोग करके भी सीखना और सिफारिशें करना जारी रख सकता है।

फीचर हैशिंग का उपयोग करना

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

हम इसे इसके तार्किक चरम पर ले जा सकते हैं और पूरी तरह से फीचर हैशिंग पर भरोसा कर सकते हैं, जिसमें कोई शब्दावली नहीं है। इस में कार्यान्वित किया जाता tf.keras.layers.Hashing परत।

# We set up a large number of bins to reduce the chance of hash collisions.
num_hashing_bins = 200_000

movie_title_hashing = tf.keras.layers.Hashing(
    num_bins=num_hashing_bins
)

हम शब्दावली बनाने की आवश्यकता के बिना पहले की तरह लुकअप कर सकते हैं:

movie_title_hashing(["Star Wars (1977)", "One Flew Over the Cuckoo's Nest (1975)"])
<tf.Tensor: shape=(2,), dtype=int64, numpy=array([101016,  96565])>

एम्बेडिंग को परिभाषित करना

अब हम पूर्णांक आईडी है, हम उपयोग कर सकते हैं Embedding embeddings रूप में करना करने के लिए परत।

एक एम्बेडिंग परत के दो आयाम होते हैं: पहला आयाम हमें बताता है कि हम कितनी अलग श्रेणियां एम्बेड कर सकते हैं; दूसरा हमें बताता है कि उनमें से प्रत्येक का प्रतिनिधित्व करने वाला वेक्टर कितना बड़ा हो सकता है।

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

movie_title_embedding = tf.keras.layers.Embedding(
    # Let's use the explicit vocabulary lookup.
    input_dim=movie_title_lookup.vocab_size(),
    output_dim=32
)
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.

हम दोनों को एक साथ एक परत में रख सकते हैं जो कच्चे पाठ को लेता है और एम्बेडिंग उत्पन्न करता है।

movie_title_model = tf.keras.Sequential([movie_title_lookup, movie_title_embedding])

ठीक उसी तरह, हम सीधे अपने मूवी टाइटल के लिए एम्बेडिंग प्राप्त कर सकते हैं:

movie_title_model(["Star Wars (1977)"])
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'list'> input: ['Star Wars (1977)']
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: ['Star Wars (1977)']
Consider rewriting this model with the Functional API.
<tf.Tensor: shape=(1, 32), dtype=float32, numpy=
array([[-0.00255408,  0.00941082,  0.02599109, -0.02758816, -0.03652344,
        -0.03852248, -0.03309812, -0.04343383,  0.03444691, -0.02454401,
         0.00619583, -0.01912323, -0.03988413,  0.03595274,  0.00727529,
         0.04844356,  0.04739804,  0.02836904,  0.01647964, -0.02924066,
        -0.00425701,  0.01747661,  0.0114414 ,  0.04916174,  0.02185034,
        -0.00399858,  0.03934855,  0.03666003,  0.01980535, -0.03694187,
        -0.02149243, -0.03765338]], dtype=float32)>

हम उपयोगकर्ता एम्बेडिंग के साथ भी ऐसा ही कर सकते हैं:

user_id_lookup = tf.keras.layers.StringLookup()
user_id_lookup.adapt(ratings.map(lambda x: x["user_id"]))

user_id_embedding = tf.keras.layers.Embedding(user_id_lookup.vocab_size(), 32)

user_id_model = tf.keras.Sequential([user_id_lookup, user_id_embedding])
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.

निरंतर सुविधाओं का सामान्यीकरण

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

for x in ratings.take(3).as_numpy_iterator():
  print(f"Timestamp: {x['timestamp']}.")
Timestamp: 879024327.
Timestamp: 875654590.
Timestamp: 882075110.

इसका उपयोग करने से पहले हमें इसे संसाधित करने की आवश्यकता है। जबकि ऐसे कई तरीके हैं जिनसे हम ऐसा कर सकते हैं, विवेकीकरण और मानकीकरण दो सामान्य हैं।

मानकीकरण

मानकीकरण rescales सुविधा का मतलब घटाकर और इसके मानक विचलन से विभाजित करके अपनी सीमा को सामान्य बनाने की सुविधा है। यह एक सामान्य प्रीप्रोसेसिंग परिवर्तन है।

यह आसानी से उपयोग करते हुए पूरा किया जा सकता tf.keras.layers.Normalization परत:

timestamp_normalization = tf.keras.layers.Normalization(
    axis=None
)
timestamp_normalization.adapt(ratings.map(lambda x: x["timestamp"]).batch(1024))

for x in ratings.take(3).as_numpy_iterator():
  print(f"Normalized timestamp: {timestamp_normalization(x['timestamp'])}.")
Normalized timestamp: [-0.84293723].
Normalized timestamp: [-1.4735204].
Normalized timestamp: [-0.27203268].

विवेकीकरण

एक अन्य सामान्य परिवर्तन एक निरंतर विशेषता को कई श्रेणीबद्ध विशेषताओं में बदलना है। यह अच्छी तरह से समझ में आता है अगर हमारे पास यह संदेह करने के कारण हैं कि किसी विशेषता का प्रभाव गैर-निरंतर है।

ऐसा करने के लिए, हमें सबसे पहले उन बाल्टियों की सीमाओं को स्थापित करना होगा जिनका उपयोग हम विवेक के लिए करेंगे। सबसे आसान तरीका सुविधा के न्यूनतम और अधिकतम मूल्य की पहचान करना है, और परिणामी अंतराल को समान रूप से विभाजित करना है:

max_timestamp = ratings.map(lambda x: x["timestamp"]).reduce(
    tf.cast(0, tf.int64), tf.maximum).numpy().max()
min_timestamp = ratings.map(lambda x: x["timestamp"]).reduce(
    np.int64(1e9), tf.minimum).numpy().min()

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

print(f"Buckets: {timestamp_buckets[:3]}")
Buckets: [8.74724710e+08 8.74743291e+08 8.74761871e+08]

बकेट की सीमाओं को देखते हुए हम टाइमस्टैम्प को एम्बेडिंग में बदल सकते हैं:

timestamp_embedding_model = tf.keras.Sequential([
  tf.keras.layers.Discretization(timestamp_buckets.tolist()),
  tf.keras.layers.Embedding(len(timestamp_buckets) + 1, 32)
])

for timestamp in ratings.take(1).map(lambda x: x["timestamp"]).batch(1).as_numpy_iterator():
  print(f"Timestamp embedding: {timestamp_embedding_model(timestamp)}.")
Timestamp embedding: [[-0.02532113 -0.00415025  0.00458465  0.02080876  0.03103903 -0.03746337
   0.04010465 -0.01709593 -0.00246077 -0.01220842  0.02456966 -0.04816503
   0.04552222  0.03535838  0.00769508  0.04328252  0.00869263  0.01110227
   0.02754457 -0.02659499 -0.01055292 -0.03035731  0.00463334 -0.02848787
  -0.03416766  0.02538678 -0.03446608 -0.0384447  -0.03032914 -0.02391632
   0.02637175 -0.01158618]].

पाठ सुविधाओं को संसाधित करना

हम अपने मॉडल में टेक्स्ट फीचर भी जोड़ना चाह सकते हैं। आमतौर पर, उत्पाद विवरण जैसी चीजें फ्री फॉर्म टेक्स्ट होती हैं, और हम उम्मीद कर सकते हैं कि हमारा मॉडल बेहतर अनुशंसा करने के लिए उनके पास मौजूद जानकारी का उपयोग करना सीख सकता है, खासकर कोल्ड-स्टार्ट या लॉन्ग टेल परिदृश्य में।

जबकि MovieLens डेटासेट हमें समृद्ध पाठ्य सुविधाएँ नहीं देता है, फिर भी हम मूवी शीर्षक का उपयोग कर सकते हैं। यह हमें इस तथ्य को पकड़ने में मदद कर सकता है कि बहुत समान शीर्षक वाली फिल्में एक ही श्रृंखला से संबंधित होने की संभावना है।

पाठ पर लागू करने के लिए हमें जो पहला परिवर्तन करने की आवश्यकता है, वह है टोकननाइज़ेशन (घटक शब्दों या शब्द-टुकड़ों में विभाजित करना), इसके बाद शब्दावली सीखना, इसके बाद एक एम्बेडिंग।

Keras tf.keras.layers.TextVectorization परत हमारे लिए पहले दो चरणों कर सकते हैं:

title_text = tf.keras.layers.TextVectorization()
title_text.adapt(ratings.map(lambda x: x["movie_title"]))

आइए इसे आजमाएं:

for row in ratings.batch(1).map(lambda x: x["movie_title"]).take(1):
  print(title_text(row))
tf.Tensor([[ 32 266 162   2 267 265  53]], shape=(1, 7), dtype=int64)

प्रत्येक शीर्षक का टोकन के अनुक्रम में अनुवाद किया जाता है, प्रत्येक टुकड़े के लिए एक जिसे हमने टोकन किया है।

हम यह सत्यापित करने के लिए सीखी गई शब्दावली की जाँच कर सकते हैं कि परत सही टोकननाइज़ेशन का उपयोग कर रही है:

title_text.get_vocabulary()[40:45]
['first', '1998', '1977', '1971', 'monty']

यह सही दिखता है: परत अलग-अलग शब्दों में शीर्षकों को टोकन कर रही है।

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

यह सब एक साथ डालें

इन घटकों के साथ, हम एक ऐसा मॉडल बना सकते हैं जो सभी प्रीप्रोसेसिंग को एक साथ करता है।

उपयोगकर्ता मॉडल

पूर्ण उपयोगकर्ता मॉडल निम्न जैसा दिख सकता है:

class UserModel(tf.keras.Model):

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

    self.user_embedding = tf.keras.Sequential([
        user_id_lookup,
        tf.keras.layers.Embedding(user_id_lookup.vocab_size(), 32),
    ])
    self.timestamp_embedding = tf.keras.Sequential([
      tf.keras.layers.Discretization(timestamp_buckets.tolist()),
      tf.keras.layers.Embedding(len(timestamp_buckets) + 2, 32)
    ])
    self.normalized_timestamp = tf.keras.layers.Normalization(
        axis=None
    )

  def call(self, inputs):

    # Take the input dictionary, pass it through each input layer,
    # and concatenate the result.
    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)

आइए इसे आजमाएं:

user_model = UserModel()

user_model.normalized_timestamp.adapt(
    ratings.map(lambda x: x["timestamp"]).batch(128))

for row in ratings.batch(1).take(1):
  print(f"Computed representations: {user_model(row)[0, :3]}")
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
Computed representations: [-0.04705765 -0.04739009 -0.04212048]

मूवी मॉडल

हम मूवी मॉडल के लिए भी ऐसा ही कर सकते हैं:

class MovieModel(tf.keras.Model):

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

    max_tokens = 10_000

    self.title_embedding = tf.keras.Sequential([
      movie_title_lookup,
      tf.keras.layers.Embedding(movie_title_lookup.vocab_size(), 32)
    ])
    self.title_text_embedding = tf.keras.Sequential([
      tf.keras.layers.TextVectorization(max_tokens=max_tokens),
      tf.keras.layers.Embedding(max_tokens, 32, mask_zero=True),
      # We average the embedding of individual words to get one embedding vector
      # per title.
      tf.keras.layers.GlobalAveragePooling1D(),
    ])

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

आइए इसे आजमाएं:

movie_model = MovieModel()

movie_model.title_text_embedding.layers[0].adapt(
    ratings.map(lambda x: x["movie_title"]))

for row in ratings.batch(1).take(1):
  print(f"Computed representations: {movie_model(row)[0, :3]}")
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
WARNING:tensorflow:vocab_size is deprecated, please use vocabulary_size.
Computed representations: [-0.01670959  0.02128791  0.04631067]

अगले कदम

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