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

استفاده از ویژگی های جانبی: پیش پردازش ویژگی

مشاهده در TensorFlow.org در Google Colab اجرا کنید مشاهده منبع در GitHub دانلود دفترچه یادداشت

یکی از مزایای بزرگ استفاده از چارچوب یادگیری عمیق برای ساختن مدلهای پیشنهادی ، آزادی ایجاد نمایندگیهای غنی و قابل انعطاف است.

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

مثلا:

  • شناسه های کاربر و مورد ممکن است رشته ای (عنوان ، نام کاربری) یا عدد صحیح بزرگ و غیر هم پیوسته (شناسه پایگاه داده) باشد.
  • توضیحات مورد می تواند متن خام باشد.
  • برچسب های زمانی متقابل می توانند برچسب های تایم خام Unix باشند.

این موارد باید به طور مناسب تغییر شکل داده شود تا در مدل های ساختمانی مفید واقع شود:

  • شناسه های کاربر و مورد باید به بردارهای تعبیه شده ترجمه شوند: نمایش های عددی با ابعاد بالا که در حین آموزش تنظیم می شوند تا به مدل کمک کنند تا هدف خود را بهتر پیش بینی کند.
  • متن خام باید رمزگذاری شود (به قسمتهای کوچکتر مانند کلمات منفرد تقسیم شود) و به تعبیه شده ترجمه شود.
  • ویژگی های عددی باید عادی شوند تا مقادیر آنها در یک فاصله کوچک در حدود 0 قرار داشته باشد.

خوشبختانه ، با استفاده از TensorFlow می توانیم چنین پیش پردازشی را به جای یک مرحله پیش پردازش جداگانه ، در مدل خود قرار دهیم. این نه تنها راحت است ، بلکه اطمینان حاصل می کند که قبل از پردازش ما در حین آموزش و در حین خدمت دقیقاً یکسان است. این باعث می شود که استفاده از مدل هایی که حتی شامل پیش پردازش بسیار پیچیده ای هستند نیز ایمن و آسان باشد.

در این آموزش ، ما در مورد توصیه کنندگان و پیش پردازشی که باید در مجموعه داده MovieLens انجام دهیم تمرکز خواهیم کرد . اگر به یک آموزش بزرگتر و بدون تمرکز بر سیستم توصیه می کنید ، نگاهی به راهنمای کامل پیش پردازش Keras بیاندازید.

مجموعه داده 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)
{'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'}

در اینجا چند ویژگی اصلی وجود دارد:

  • عنوان فیلم به عنوان شناسه فیلم مفید است.
  • شناسه کاربر به عنوان شناسه کاربر مفید است.
  • مهر زمان به ما امکان می دهد تا تأثیر زمان را مدلسازی کنیم.

دو مورد اول ویژگیهای طبقه ای است. مهر زمان یک ویژگی پیوسته است.

تبدیل ویژگی های دسته بندی به تعبیه شده

یک ویژگی طبقه ای ویژگی است که کمیتی مداوم را بیان نمی کند ، بلکه یکی از مجموعه مقادیر ثابت را به خود اختصاص می دهد.

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

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

استفاده از ویژگی های طبقه ای خام و تبدیل آنها به تعبیه شده به طور معمول یک مرحله دو مرحله ای است:

  1. در مرحله اول ، ما باید مقادیر خام را به طیف وسیعی از اعداد صحیح مجاور ترجمه کنیم ، به طور معمول با ایجاد یک نقشه برداری ("واژگان" نامیده می شود) که نقشه مقادیر خام ("جنگ ستارگان") را به اعداد صحیح ترسیم می کند (مثلا 15)
  2. ثانیا ، ما باید این اعداد صحیح را گرفته و به تعبیه شده تبدیل کنیم.

تعریف واژگان

اولین قدم تعریف واژگان است. ما می توانیم این کار را به راحتی با استفاده از لایه های پیش پردازش Keras انجام دهیم.

import numpy as np
import tensorflow as tf

movie_title_lookup = tf.keras.layers.experimental.preprocessing.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)']

هنگامی که این مورد را بدست آوردیم می توانیم از لایه برای ترجمه نشانه های خام به تعبیه شناسه ها استفاده کنیم:

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

توجه داشته باشید که واژگان لایه شامل یک (یا بیشتر!) نشانه های ناشناخته (یا "خارج از واژگان" ، OOV) است. این واقعاً مفید است: این بدان معنی است که لایه می تواند مقادیر دسته بندی شده را که در واژگان نیستند اداره کند. از نظر عملی ، این بدان معنی است که مدل می تواند حتی با استفاده از ویژگی هایی که در حین ساخت واژگان دیده نشده است ، به یادگیری و توصیه های خود ادامه دهد.

استفاده از هش ویژگی

در واقع ، لایه StringLookup به ما امکان پیکربندی چندین شاخص OOV را می دهد. اگر این کار را انجام دهیم ، هر مقدار خام که در واژگان نباشد ، به طور قطعی به یکی از شاخص های OOV رسانده می شود. هر چه این شاخص ها بیشتر باشد ، کمتر احتمال دارد که دو مقدار مختلف از ویژگی های خام با شاخص OOV یکسان شوند. در نتیجه ، اگر چنین شاخص هایی به اندازه کافی داشته باشیم ، مدل باید بتواند همانند یک مدل با واژگان صریح آموزش دهد بدون اینکه نیازی به حفظ لیست نشانه ها نباشد.

ما می توانیم این مسئله را به حد منطقی خود برسانیم و کاملاً به هش کردن ویژگی و بدون هیچ واژگی متکی باشیم. این در لایه tf.keras.layers.experimental.preprocessing.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.experimental.preprocessing.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 برای تبدیل موارد به جاسازی استفاده کنیم.

یک لایه تعبیه شده دارای دو بعد است: بعد اول به ما می گوید که چند دسته مشخص را می توانیم تعبیه کنیم. مورد دوم به ما می گوید که بردار نشان دهنده هر یک از آنها چه اندازه می تواند باشد.

هنگام ایجاد لایه تعبیه شده برای عناوین فیلم ، ما می خواهیم اولین مقدار را به اندازه واژگان عنوان خود (یا تعداد سطل های هش) تنظیم کنیم. مورد دوم به خود ما بستگی دارد: هرچه بزرگتر باشد ، ظرفیت مدل بالاتر است ، اما سرعت تناسب و سرویس دهی آن کندتر است.

movie_title_embedding = tf.keras.layers.Embedding(
    # Let's use the explicit vocabulary lookup.
    input_dim=movie_title_lookup.vocab_size(),
    output_dim=32
)

ما می توانیم این دو را با هم در یک لایه قرار دهیم که متن خام را در بر می گیرد و تعبیه شده را تولید می کند.

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.00969323,  0.00489185, -0.04925991, -0.01755667, -0.03242903,
         0.048209  , -0.00058778,  0.03010542,  0.04593058,  0.00638406,
        -0.01864717, -0.02462925,  0.0124921 , -0.02785023,  0.00886985,
         0.02368834, -0.00750258,  0.03731059, -0.0210851 ,  0.02141182,
         0.04444833, -0.04175395,  0.03945151, -0.04986783, -0.04204438,
        -0.00832187, -0.02516021, -0.03831302, -0.04065866, -0.02181952,
        -0.01168469,  0.00944215]], dtype=float32)>

ما می توانیم همین کار را با تعبیه های کاربر انجام دهیم:

user_id_lookup = tf.keras.layers.experimental.preprocessing.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])

عادی سازی ویژگی های مداوم

ویژگی های مداوم نیز به عادی سازی نیاز دارند. به عنوان مثال ، ویژگی timestamp برای استفاده مستقیم در یک مدل عمیق خیلی بزرگ است:

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

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

استاندارد سازی

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

این کار با استفاده از لایه tf.keras.layers.experimental.preprocessing.Normalization به راحتی قابل دستیابی است:

timestamp_normalization = tf.keras.layers.experimental.preprocessing.Normalization()
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.84293705]].
Normalized timestamp: [[-1.47352]].
Normalized timestamp: [[-0.27203262]].

گسسته سازی

یکی دیگر از تحولات رایج ، تبدیل یک ویژگی پیوسته به تعدادی از ویژگی های طبقه بندی شده است. اگر دلایلی داشته باشیم که اثر یک ویژگی مداوم نیست ، این منطقی است.

برای انجام این کار ، ابتدا باید مرز سطل هایی را که برای تمیز کردن استفاده خواهیم کرد ، تعیین کنیم. ساده ترین راه شناسایی حداقل و حداکثر مقدار ویژگی و تقسیم فاصله حاصل به طور مساوی است:

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.experimental.preprocessing.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)}.")
WARNING:tensorflow:AutoGraph could not transform <function <lambda> at 0x7f79859db950> and will run it as-is.
Cause: could not parse the source code:

for timestamp in ratings.take(1).map(lambda x: x["timestamp"]).batch(1).as_numpy_iterator():

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
WARNING:tensorflow:AutoGraph could not transform <function <lambda> at 0x7f79859db950> and will run it as-is.
Cause: could not parse the source code:

for timestamp in ratings.take(1).map(lambda x: x["timestamp"]).batch(1).as_numpy_iterator():

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
WARNING: AutoGraph could not transform <function <lambda> at 0x7f79859db950> and will run it as-is.
Cause: could not parse the source code:

for timestamp in ratings.take(1).map(lambda x: x["timestamp"]).batch(1).as_numpy_iterator():

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
Timestamp embedding: [[-0.02747613 -0.00615388 -0.00518906  0.03686668 -0.03123426 -0.03459657
  -0.01413486  0.02942265 -0.02695948 -0.03725258  0.01880633  0.00245643
  -0.03751732 -0.04196785 -0.03264894 -0.022715   -0.00240433 -0.04320173
  -0.03503735 -0.04935455 -0.00380387 -0.02850516  0.04652354  0.03587868
   0.04568979 -0.04698962  0.00158113  0.02843685 -0.03367974  0.02686216
   0.03084537 -0.03416919]].

پردازش ویژگی های متن

همچنین ممکن است بخواهیم ویژگی های متن را به مدل خود اضافه کنیم. معمولاً مواردی از قبیل توضیحات محصول متن آزاد فرم هستند و ما می توانیم امیدوار باشیم که مدل ما بتواند از اطلاعات موجود در آنها برای توصیه های بهتر استفاده کند ، خصوصاً در سناریوی دم سرد یا دم بلند.

در حالی که مجموعه داده MovieLens ویژگی های متنی غنی به ما نمی دهد ، ما هنوز هم می توانیم از عناوین فیلم استفاده کنیم. این ممکن است به ما کمک کند این واقعیت را درک کنیم که فیلم هایی با عناوین بسیار مشابه احتمالاً متعلق به همان مجموعه ها هستند.

اولین تحولی که ما باید در متن اعمال کنیم توکن سازی است (تقسیم به کلمات سازنده یا کلمات) و به دنبال آن یادگیری واژگان و به دنبال آن تعبیه می شود.

لایه Keras tf.keras.layers.experimental.preprocessing.TextVectorization می تواند دو مرحله اول را برای ما انجام دهد:

title_text = tf.keras.layers.experimental.preprocessing.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))
WARNING:tensorflow:AutoGraph could not transform <function <lambda> at 0x7f792c070158> and will run it as-is.
Cause: could not parse the source code:

for row in ratings.batch(1).map(lambda x: x["movie_title"]).take(1):

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
WARNING:tensorflow:AutoGraph could not transform <function <lambda> at 0x7f792c070158> and will run it as-is.
Cause: could not parse the source code:

for row in ratings.batch(1).map(lambda x: x["movie_title"]).take(1):

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
WARNING: AutoGraph could not transform <function <lambda> at 0x7f792c070158> and will run it as-is.
Cause: could not parse the source code:

for row in ratings.batch(1).map(lambda x: x["movie_title"]).take(1):

This error may be avoided by creating the lambda in a standalone statement.

To silence this warning, decorate the function with @tf.autograph.experimental.do_not_convert
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']

این درست به نظر می رسد: لایه عناوین را به کلمات جداگانه تبدیل می کند.

برای پایان پردازش ، اکنون باید متن را تعبیه کنیم. از آنجا که هر عنوان شامل چندین کلمه است ، برای هر عنوان چندین تعبیه خواهیم کرد. برای استفاده در مدل donwstream اینها معمولاً به یک تعبیه شده فشرده می شوند. مدل هایی مانند RNN یا Transformers در اینجا مفید هستند ، اما به طور متوسط ​​قرار دادن تمام کلمات در کنار هم نقطه شروع خوبی است.

همه اش را بگذار کنار هم

با در اختیار داشتن این م componentsلفه ها ، ما می توانیم مدلی بسازیم که تمام پیش پردازش ها را با هم انجام دهد.

مدل کاربر

مدل کامل کاربر ممکن است به صورت زیر باشد:

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.experimental.preprocessing.Discretization(timestamp_buckets.tolist()),
      tf.keras.layers.Embedding(len(timestamp_buckets) + 2, 32)
    ])
    self.normalized_timestamp = tf.keras.layers.experimental.preprocessing.Normalization()

  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"]),
        self.normalized_timestamp(inputs["timestamp"])
    ], 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]}")
Computed representations: [-0.02975558  0.03509596 -0.02994236]

مدل فیلم

ما می توانیم همین کار را برای مدل فیلم انجام دهیم:

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.experimental.preprocessing.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]}")
Computed representations: [-0.02882576 -0.00666581 -0.00883247]

مراحل بعدی

با استفاده از دو مدل بالا ، ما اولین قدم ها را برای نشان دادن ویژگی های غنی در یک مدل توصیه شده برداشته ایم: برای ادامه این کار و بررسی چگونگی استفاده از این موارد برای ساخت یک مدل موثر از توصیه های عمیق ، به آموزش توصیه کنندگان عمیق نگاهی بیندازید.