وعده غذایی کارآمد

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

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

جستجوی تقریبی نزدیکترین همسایه (ANN) فناوری است که این امکان را فراهم می کند. در این آموزش، نحوه استفاده از ScaNN - آخرین بسته بازیابی نزدیکترین همسایه - برای مقیاس یکپارچه بازیابی TFRS به میلیون ها مورد را نشان خواهیم داد.

ScaNN چیست؟

ScaNN کتابخانه ای از Google Research است که جستجوی تشابه برداری متراکم را در مقیاس بزرگ انجام می دهد. با توجه به پایگاه داده ای از جاسازی های نامزد، ScaNN این جاسازی ها را به گونه ای نمایه می کند که امکان جستجوی سریع در زمان استنتاج را فراهم می کند. ScaNN از آخرین تکنیک‌های فشرده‌سازی برداری و الگوریتم‌هایی با دقت پیاده‌سازی شده برای دستیابی به بهترین معاوضه سرعت-دقت استفاده می‌کند. این می تواند تا حد زیادی از جستجوی brute force بهتر عمل کند در حالی که از نظر دقت اندکی را قربانی می کند.

ساخت یک مدل مبتنی بر ScaNN

سعی کنید از ScaNN در TFRS، ما یک MovieLens ساده مدل بازیابی ساخت، همانطور که ما در انجام اساسی بازیابی آموزش. اگر آن آموزش را دنبال کرده باشید، این بخش آشنا خواهد بود و می توان با خیال راحت از آن گذشت.

برای شروع، مجموعه داده های TFRS و TensorFlow را نصب کنید:

pip install -q tensorflow-recommenders
pip install -q --upgrade tensorflow-datasets

ما همچنین نیاز به نصب scann : این یک وابستگی اختیاری TFRS است، و بنابراین باید به صورت جداگانه نصب شود.

pip install -q scann

تمام واردات لازم را تنظیم کنید.

from typing import Dict, Text

import os
import pprint
import tempfile

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

و داده ها را بارگذاری کنید:

# Load the MovieLens 100K data.
ratings = tfds.load(
    "movielens/100k-ratings",
    split="train"
)

# Get the ratings data.
ratings = (ratings
           # Retain only the fields we need.
           .map(lambda x: {"user_id": x["user_id"], "movie_title": x["movie_title"]})
           # Cache for efficiency.
           .cache(tempfile.NamedTemporaryFile().name)
)

# Get the movies data.
movies = tfds.load("movielens/100k-movies", split="train")
movies = (movies
          # Retain only the fields we need.
          .map(lambda x: x["movie_title"])
          # Cache for efficiency.
          .cache(tempfile.NamedTemporaryFile().name))
2021-10-02 11:53:59.413405: E tensorflow/stream_executor/cuda/cuda_driver.cc:271] failed call to cuInit: CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected

قبل از اینکه بتوانیم یک مدل بسازیم، باید واژگان کاربر و فیلم را تنظیم کنیم:

user_ids = ratings.map(lambda x: x["user_id"])

unique_movie_titles = np.unique(np.concatenate(list(movies.batch(1000))))
unique_user_ids = np.unique(np.concatenate(list(user_ids.batch(1000))))
2021-10-02 11:54:00.296290: W tensorflow/core/kernels/data/cache_dataset_ops.cc:233] 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.
2021-10-02 11:54:04.003150: W tensorflow/core/kernels/data/cache_dataset_ops.cc:233] 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.

ما همچنین مجموعه های آموزشی و آزمایشی را تنظیم خواهیم کرد:

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)

تعریف مدل

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

class MovielensModel(tfrs.Model):

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

    embedding_dimension = 32

    # Set up a model for representing movies.
    self.movie_model = tf.keras.Sequential([
      tf.keras.layers.StringLookup(
        vocabulary=unique_movie_titles, mask_token=None),
      # We add an additional embedding to account for unknown tokens.
      tf.keras.layers.Embedding(len(unique_movie_titles) + 1, embedding_dimension)
    ])

    # Set up a model for representing users.
    self.user_model = tf.keras.Sequential([
      tf.keras.layers.StringLookup(
        vocabulary=unique_user_ids, mask_token=None),
        # We add an additional embedding to account for unknown tokens.
      tf.keras.layers.Embedding(len(unique_user_ids) + 1, embedding_dimension)
    ])

    # Set up a task to optimize the model and compute metrics.
    self.task = tfrs.tasks.Retrieval(
      metrics=tfrs.metrics.FactorizedTopK(
        candidates=movies.batch(128).cache().map(self.movie_model)
      )
    )

  def compute_loss(self, features: Dict[Text, tf.Tensor], training=False) -> 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,
    # getting embeddings back.
    positive_movie_embeddings = self.movie_model(features["movie_title"])

    # The task computes the loss and the metrics.

    return self.task(user_embeddings, positive_movie_embeddings, compute_metrics=not training)

تناسب و ارزیابی

یک مدل TFRS فقط یک مدل Keras است. ما می توانیم آن را کامپایل کنیم:

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

تخمین بزنید:

model.fit(train.batch(8192), epochs=3)
Epoch 1/3
10/10 [==============================] - 3s 223ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_5_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_10_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_50_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_100_categorical_accuracy: 0.0000e+00 - loss: 69808.9716 - regularization_loss: 0.0000e+00 - total_loss: 69808.9716
Epoch 2/3
10/10 [==============================] - 3s 222ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_5_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_10_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_50_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_100_categorical_accuracy: 0.0000e+00 - loss: 67485.8842 - regularization_loss: 0.0000e+00 - total_loss: 67485.8842
Epoch 3/3
10/10 [==============================] - 3s 220ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_5_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_10_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_50_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_100_categorical_accuracy: 0.0000e+00 - loss: 66311.9581 - regularization_loss: 0.0000e+00 - total_loss: 66311.9581
<keras.callbacks.History at 0x7fc02423c150>

و آن را ارزیابی کنید.

model.evaluate(test.batch(8192), return_dict=True)
3/3 [==============================] - 2s 246ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0011 - factorized_top_k/top_5_categorical_accuracy: 0.0095 - factorized_top_k/top_10_categorical_accuracy: 0.0222 - factorized_top_k/top_50_categorical_accuracy: 0.1261 - factorized_top_k/top_100_categorical_accuracy: 0.2363 - loss: 49466.8789 - regularization_loss: 0.0000e+00 - total_loss: 49466.8789
{'factorized_top_k/top_1_categorical_accuracy': 0.0010999999940395355,
 'factorized_top_k/top_5_categorical_accuracy': 0.009549999609589577,
 'factorized_top_k/top_10_categorical_accuracy': 0.022199999541044235,
 'factorized_top_k/top_50_categorical_accuracy': 0.1261499971151352,
 'factorized_top_k/top_100_categorical_accuracy': 0.23634999990463257,
 'loss': 28242.8359375,
 'regularization_loss': 0,
 'total_loss': 28242.8359375}

پیش بینی تقریبی

ساده ترین راه برای بازیابی کاندیداهای برتر در پاسخ به یک پرس و جو این است که این کار را از طریق brute force انجام دهید: امتیاز فیلم های کاربر را برای همه فیلم های ممکن محاسبه کنید، آنها را مرتب کنید و چند توصیه برتر را انتخاب کنید.

در TFRS، این است که از طریق انجام BruteForce لایه:

brute_force = tfrs.layers.factorized_top_k.BruteForce(model.user_model)
brute_force.index_from_dataset(
    movies.batch(128).map(lambda title: (title, model.movie_model(title)))
)
<tensorflow_recommenders.layers.factorized_top_k.BruteForce at 0x7fbfc1d4fe10>

هنگامی که ایجاد و جمعیت با نامزدها (از طریق index روش)، ما می توانیم آن را به پیش بینی کرد:

# Get predictions for user 42.
_, titles = brute_force(np.array(["42"]), k=3)

print(f"Top recommendations: {titles[0]}")
Top recommendations: [b'Homeward Bound: The Incredible Journey (1993)'
 b"Kid in King Arthur's Court, A (1995)" b'Rudy (1993)']

در مجموعه داده کوچکی از کمتر از 1000 فیلم، این بسیار سریع است:

%timeit _, titles = brute_force(np.array(["42"]), k=3)
983 µs ± 5.44 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

اما اگر کاندیداهای بیشتری داشته باشیم - میلیون ها نفر به جای هزاران چه اتفاقی می افتد؟

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

# Construct a dataset of movies that's 1,000 times larger. We 
# do this by adding several million dummy movie titles to the dataset.
lots_of_movies = tf.data.Dataset.concatenate(
    movies.batch(4096),
    movies.batch(4096).repeat(1_000).map(lambda x: tf.zeros_like(x))
)

# We also add lots of dummy embeddings by randomly perturbing
# the estimated embeddings for real movies.
lots_of_movies_embeddings = tf.data.Dataset.concatenate(
    movies.batch(4096).map(model.movie_model),
    movies.batch(4096).repeat(1_000)
      .map(lambda x: model.movie_model(x))
      .map(lambda x: x * tf.random.uniform(tf.shape(x)))
)

ما می توانیم یک ساخت BruteForce شاخص در این مجموعه داده بزرگتر:

brute_force_lots = tfrs.layers.factorized_top_k.BruteForce()
brute_force_lots.index_from_dataset(
    tf.data.Dataset.zip((lots_of_movies, lots_of_movies_embeddings))
)
<tensorflow_recommenders.layers.factorized_top_k.BruteForce at 0x7fbfc1d80610>

توصیه ها همچنان یکسان است

_, titles = brute_force_lots(model.user_model(np.array(["42"])), k=3)

print(f"Top recommendations: {titles[0]}")
Top recommendations: [b'Homeward Bound: The Incredible Journey (1993)'
 b"Kid in King Arthur's Court, A (1995)" b'Rudy (1993)']

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

%timeit _, titles = brute_force_lots(model.user_model(np.array(["42"])), k=3)
33 ms ± 245 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

با افزایش تعداد نامزدها، زمان مورد نیاز به صورت خطی افزایش می‌یابد: با 10 میلیون نامزد، خدمت به نامزدهای برتر 250 میلی‌ثانیه طول می‌کشد. این به وضوح برای یک سرویس زنده بسیار کند است.

اینجاست که مکانیسم های تقریبی وارد می شوند.

با استفاده از ScaNN در TFRS از طریق انجام tfrs.layers.factorized_top_k.ScaNN لایه. از رابط کاربری مشابه با سایر لایه های بالای k پیروی می کند:

scann = tfrs.layers.factorized_top_k.ScaNN(num_reordering_candidates=100)
scann.index_from_dataset(
    tf.data.Dataset.zip((lots_of_movies, lots_of_movies_embeddings))
)
<tensorflow_recommenders.layers.factorized_top_k.ScaNN at 0x7fbfc2571990>

توصیه ها (تقریبا!) یکسان است

_, titles = scann(model.user_model(np.array(["42"])), k=3)

print(f"Top recommendations: {titles[0]}")
Top recommendations: [b'Homeward Bound: The Incredible Journey (1993)'
 b"Kid in King Arthur's Court, A (1995)" b'Rudy (1993)']

اما محاسبه آنها بسیار بسیار سریعتر است:

%timeit _, titles = scann(model.user_model(np.array(["42"])), k=3)
4.35 ms ± 34.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

در این مورد، ما می‌توانیم 3 فیلم برتر از مجموعه‌ای 1 میلیونی را در حدود 2 میلی‌ثانیه بازیابی کنیم: 15 برابر سریع‌تر از محاسبه بهترین نامزدها از طریق brute force. مزیت روش های تقریبی برای مجموعه داده های بزرگتر حتی بیشتر می شود.

ارزیابی تقریب

هنگام استفاده از مکانیسم‌های تقریبی بازیابی K بالا (مانند ScaNN)، سرعت بازیابی اغلب به قیمت از دست دادن دقت تمام می‌شود. برای درک این مبادله، اندازه گیری معیارهای ارزیابی مدل در هنگام استفاده از ScaNN و مقایسه آنها با خط پایه مهم است.

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

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

# Override the existing streaming candidate source.
model.task.factorized_metrics = tfrs.metrics.FactorizedTopK(
    candidates=lots_of_movies_embeddings
)
# Need to recompile the model for the changes to take effect.
model.compile()

%time baseline_result = model.evaluate(test.batch(8192), return_dict=True, verbose=False)
CPU times: user 22min 5s, sys: 2min 7s, total: 24min 12s
Wall time: 51.9 s

با استفاده از ScaNN می توانیم همین کار را انجام دهیم:

model.task.factorized_metrics = tfrs.metrics.FactorizedTopK(
    candidates=scann
)
model.compile()

# We can use a much bigger batch size here because ScaNN evaluation
# is more memory efficient.
%time scann_result = model.evaluate(test.batch(8192), return_dict=True, verbose=False)
CPU times: user 10.5 s, sys: 3.26 s, total: 13.7 s
Wall time: 1.85 s

ارزیابی مبتنی بر ScaNN بسیار بسیار سریعتر است: بیش از ده برابر سریعتر است! این مزیت برای مجموعه داده‌های بزرگ‌تر حتی بزرگ‌تر می‌شود، و بنابراین برای مجموعه‌های داده بزرگ، ممکن است عاقلانه باشد که همیشه ارزیابی مبتنی بر ScaNN را برای بهبود سرعت توسعه مدل اجرا کنید.

اما در مورد نتایج چطور؟ خوشبختانه در این مورد نتایج تقریباً یکسان است:

print(f"Brute force top-100 accuracy: {baseline_result['factorized_top_k/top_100_categorical_accuracy']:.2f}")
print(f"ScaNN top-100 accuracy:       {scann_result['factorized_top_k/top_100_categorical_accuracy']:.2f}")
Brute force top-100 accuracy: 0.15
ScaNN top-100 accuracy:       0.27

این نشان می دهد که در این داده مصنوعی، ضرر کمی از تقریب وجود دارد. به طور کلی، همه روش‌های تقریبی، معاوضه سرعت-دقت را نشان می‌دهند. برای درک این در عمق بیشتر شما می توانید از اریک Bernhardsson از را معیار ANN .

استقرار مدل تقریبی

ScaNN مدل مبتنی بر به طور کامل به مدل TensorFlow یکپارچه، و خدمت به آن را به آسانی به عنوان خدمت هر مدل TensorFlow دیگر.

ما می توانیم آن را به عنوان نجات SavedModel شی

lots_of_movies_embeddings
<ConcatenateDataset shapes: (None, 32), types: tf.float32>
# We re-index the ScaNN layer to include the user embeddings in the same model.
# This way we can give the saved model raw features and get valid predictions
# back.
scann = tfrs.layers.factorized_top_k.ScaNN(model.user_model, num_reordering_candidates=1000)
scann.index_from_dataset(
    tf.data.Dataset.zip((lots_of_movies, lots_of_movies_embeddings))
)

# Need to call it to set the shapes.
_ = scann(np.array(["42"]))

with tempfile.TemporaryDirectory() as tmp:
  path = os.path.join(tmp, "model")
  tf.saved_model.save(
      scann,
      path,
      options=tf.saved_model.SaveOptions(namespace_whitelist=["Scann"])
  )

  loaded = tf.saved_model.load(path)
2021-10-02 11:55:53.875291: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
WARNING:absl:Found untraced functions such as query_with_exclusions while saving (showing 1 of 1). These functions will not be directly callable after loading.
INFO:tensorflow:Assets written to: /tmp/tmpm0piq8hx/model/assets
INFO:tensorflow:Assets written to: /tmp/tmpm0piq8hx/model/assets

و سپس آن را بارگذاری کرده و سرو کنید، دقیقاً همان نتایج را دریافت کنید:

_, titles = loaded(tf.constant(["42"]))

print(f"Top recommendations: {titles[0][:3]}")
Top recommendations: [b'Homeward Bound: The Incredible Journey (1993)'
 b"Kid in King Arthur's Court, A (1995)" b'Rudy (1993)']

مدل به دست آمده را می توان در هر سرویس پایتونی که TensorFlow و ScaNN نصب شده است، ارائه کرد.

همچنین می تواند با استفاده از یک نسخه سفارشی از تعمیر و نگهداری TensorFlow، در دسترس به عنوان یک ظرف کارگر بارانداز در خدمت کرده است کارگر بارانداز توپی . شما همچنین می توانید خود را تصویر از ساخت Dockerfile .

تنظیم ScaNN

اکنون بیایید به تنظیم لایه ScaNN خود بپردازیم تا تعادل عملکرد/دقت بهتری داشته باشیم. برای انجام این کار به طور موثر، ابتدا باید عملکرد و دقت پایه خود را اندازه گیری کنیم.

از بالا، ما قبلاً اندازه‌گیری تأخیر مدل خود را برای پردازش یک جستار منفرد (غیر دسته‌ای) داریم (اگرچه توجه داشته باشید که مقدار مناسبی از این تأخیر از اجزای غیر ScaNN مدل است).

اکنون باید دقت ScaNN را که از طریق یادآوری اندازه گیری می کنیم، بررسی کنیم. Recall@k از x% به این معنی است که اگر از نیروی brute برای بازیابی k همسایه های بالای واقعی استفاده کنیم و آن نتایج را با استفاده از ScaNN برای بازیابی k همسایه های بالا مقایسه کنیم، x% از نتایج ScaNN در نتایج brute force واقعی است. بیایید فراخوانی را برای جستجوگر ScaNN فعلی محاسبه کنیم.

اول، ما باید نیروی بی رحم، حقیقت زمینی top-k را تولید کنیم:

# Process queries in groups of 1000; processing them all at once with brute force
# may lead to out-of-memory errors, because processing a batch of q queries against
# a size-n dataset takes O(nq) space with brute force.
titles_ground_truth = tf.concat([
  brute_force_lots(queries, k=10)[1] for queries in
  test.batch(1000).map(lambda x: model.user_model(x["user_id"]))
], axis=0)

متغیر ما titles_ground_truth در حال حاضر شامل بالای-10 توصیه های فیلم بازگردانده شده توسط بازیابی حیوان نیروی. اکنون می توانیم همان توصیه ها را هنگام استفاده از ScaNN محاسبه کنیم:

# Get all user_id's as a 1d tensor of strings
test_flat = np.concatenate(list(test.map(lambda x: x["user_id"]).batch(1000).as_numpy_iterator()), axis=0)

# ScaNN is much more memory efficient and has no problem processing the whole
# batch of 20000 queries at once.
_, titles = scann(test_flat, k=10)

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

def compute_recall(ground_truth, approx_results):
  return np.mean([
      len(np.intersect1d(truth, approx)) / len(truth)
      for truth, approx in zip(ground_truth, approx_results)
  ])

این به ما recall@10 پایه را با پیکربندی ScaNN فعلی می‌دهد:

print(f"Recall: {compute_recall(titles_ground_truth, titles):.3f}")
Recall: 0.931

ما همچنین می توانیم تاخیر پایه را اندازه گیری کنیم:

%timeit -n 1000 scann(np.array(["42"]), k=10)
4.67 ms ± 25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

بیایید ببینیم که آیا می توانیم بهتر عمل کنیم!

برای انجام این کار، ما به مدلی نیاز داریم که نشان دهد دستگیره های تنظیم ScaNN چگونه بر عملکرد تأثیر می گذارد. مدل فعلی ما از الگوریتم درخت-AH ScaNN استفاده می کند. این الگوریتم پایگاه داده‌های جاسازی‌ها ("درخت") را پارتیشن بندی می‌کند و سپس با استفاده از AH که یک روال محاسباتی فاصله تقریبی بسیار بهینه شده است، امیدوارکننده‌ترین این پارتیشن‌ها را به دست می‌آورد.

پارامتر های پیش فرض برای TensorFlow توصیه، ScaNN Keras مجموعه لایه num_leaves=100 و num_leaves_to_search=10 . این به این معنی است که پایگاه داده ما به 100 زیرمجموعه مجزا تقسیم شده است و 10 مورد امیدوار کننده از این پارتیشن ها با AH امتیازدهی شده است. این به این معنی است که 10/100 = 10٪ از مجموعه داده با AH جستجو می شود.

اگر ما، مثلا، num_leaves=1000 و num_leaves_to_search=100 ، ما را نیز در جستجوی 10 درصد از پایگاه داده با AH باشد. با این حال، در مقایسه با تنظیم قبلی، 10 درصد ما جستجو شامل نامزدها با کیفیت بالاتر، به دلیل بالاتر num_leaves ما اجازه می دهد تا برای تصمیم گیری-ظریف دانه مورد چه بخش هایی از مجموعه داده ارزش جستجو.

این جای تعجب نیست که با num_leaves=1000 و num_leaves_to_search=100 ما یادآوری به مراتب بالاتر:

scann2 = tfrs.layers.factorized_top_k.ScaNN(
    model.user_model, 
    num_leaves=1000,
    num_leaves_to_search=100,
    num_reordering_candidates=1000)
scann2.index_from_dataset(
    tf.data.Dataset.zip((lots_of_movies, lots_of_movies_embeddings))
)

_, titles2 = scann2(test_flat, k=10)

print(f"Recall: {compute_recall(titles_ground_truth, titles2):.3f}")
Recall: 0.966

با این حال، به عنوان یک معامله، تاخیر ما نیز افزایش یافته است. این به این دلیل است که مرحله پارتیشن بندی گران تر شده است. scann میدارد بالای 10 از 100 پارتیشن در حالی که scann2 میدارد بالای 100 از 1000 پارتیشن. دومی می تواند گران تر باشد زیرا شامل 10 برابر بیشتر پارتیشن ها می شود.

%timeit -n 1000 scann2(np.array(["42"]), k=10)
4.86 ms ± 21.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

به طور کلی، تنظیم جستجوی ScaNN در مورد انتخاب مبادلات مناسب است. هر تغییر پارامتر به طور کلی جستجو را سریعتر و دقیق تر نمی کند. هدف ما تنظیم پارامترها برای تبادل بهینه بین این دو هدف متضاد است.

در مورد ما، scann2 به طور قابل توجهی بهبود یافته فراخوان بیش از scann در برخی از هزینه در زمان تاخیر. آیا می‌توانیم برخی از دکمه‌های دیگر را برای کاهش تأخیر و در عین حال حفظ بیشتر مزیت یادآوری خود به عقب برگردانیم؟

بیایید سعی کنیم 70/1000 = 7٪ از مجموعه داده را با AH جستجو کنیم، و فقط 400 نامزد نهایی را دوباره به دست آوریم:

scann3 = tfrs.layers.factorized_top_k.ScaNN(
    model.user_model,
    num_leaves=1000,
    num_leaves_to_search=70,
    num_reordering_candidates=400)
scann3.index_from_dataset(
    tf.data.Dataset.zip((lots_of_movies, lots_of_movies_embeddings))
)

_, titles3 = scann3(test_flat, k=10)
print(f"Recall: {compute_recall(titles_ground_truth, titles3):.3f}")
Recall: 0.957

scann3 ارائه می شود حدود 3 درصد افزایش جامعیت بیش از scann در حالی که همچنین ارائه زمان تاخیر پایین تر:

%timeit -n 1000 scann3(np.array(["42"]), k=10)
4.58 ms ± 37.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

این دستگیره ها را می توان برای بهینه سازی نقاط مختلف در امتداد مرز پارتو دقت-عملکرد بیشتر تنظیم کرد. الگوریتم‌های ScaNN می‌توانند به عملکرد پیشرفته‌ای در طیف وسیعی از اهداف فراخوان دست یابند.

خواندن بیشتر

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

بسیاری از تکنیک های تدریج مرتبط در مراجع از ما مقاله ICML 2020 ذکر شد، و دیگر پژوهش های مربوط به ScaNN است که در ذکر شده http://sanjivk.com/