कुशल सेवा

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

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

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

स्कैनएन क्या है?

स्कैनएन गूगल रिसर्च की एक लाइब्रेरी है जो बड़े पैमाने पर सघन वेक्टर समानता खोज करती है। उम्मीदवार एम्बेडिंग के डेटाबेस को देखते हुए, स्कैनएन इन एम्बेडिंग को इस तरह से अनुक्रमित करता है जिससे उन्हें अनुमान के समय तेजी से खोजा जा सके। स्कैनएन सर्वोत्तम गति-सटीकता ट्रेडऑफ़ प्राप्त करने के लिए अत्याधुनिक वेक्टर संपीड़न तकनीकों और सावधानीपूर्वक कार्यान्वित एल्गोरिदम का उपयोग करता है। यह सटीकता के मामले में बहुत कम त्याग करते हुए पाशविक बल खोज को बहुत बेहतर कर सकता है।

एक स्कैनएन-संचालित मॉडल का निर्माण

TFRS में scann आज़माने के लिए, हम एक सरल 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}

अनुमानित भविष्यवाणी

किसी प्रश्न के उत्तर में शीर्ष उम्मीदवारों को पुनः प्राप्त करने का सबसे सरल तरीका यह है कि इसे क्रूर बल के माध्यम से किया जाए: सभी संभावित फिल्मों के लिए उपयोगकर्ता-मूवी स्कोर की गणना करें, उन्हें क्रमबद्ध करें, और कुछ शीर्ष अनुशंसाएं चुनें।

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 मिलीसेकंड लगेंगे। यह स्पष्ट रूप से एक लाइव सेवा के लिए बहुत धीमा है।

यह वह जगह है जहाँ अनुमानित तंत्र आते हैं।

TFRS में scann का उपयोग के माध्यम से पूरा किया है 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)

इस मामले में, हम लगभग 2 मिलीसेकंड में ~ 1 मिलियन के सेट में से शीर्ष 3 फिल्मों को पुनः प्राप्त कर सकते हैं: पाशविक बल के माध्यम से सर्वश्रेष्ठ उम्मीदवारों की गणना करने की तुलना में 15 गुना तेज। अनुमानित विधियों का लाभ बड़े डेटासेट के लिए और भी बड़ा हो जाता है।

सन्निकटन का मूल्यांकन

अनुमानित शीर्ष K पुनर्प्राप्ति तंत्र (जैसे कि ScaNN) का उपयोग करते समय, पुनर्प्राप्ति की गति अक्सर सटीकता की कीमत पर आती है। इस ट्रेड-ऑफ को समझने के लिए, स्कैनएन का उपयोग करते समय मॉडल के मूल्यांकन मेट्रिक्स को मापना और बेसलाइन के साथ उनकी तुलना करना महत्वपूर्ण है।

सौभाग्य से, TFRS इसे आसान बनाता है। हम केवल स्कैनएन का उपयोग करके मेट्रिक्स के साथ पुनर्प्राप्ति कार्य पर मेट्रिक्स को ओवरराइड करते हैं, मॉडल को फिर से संकलित करते हैं, और मूल्यांकन चलाते हैं।

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

# 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

हम स्कैन का उपयोग करके ऐसा ही कर सकते हैं:

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

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

लेकिन परिणामों के बारे में कैसे? सौभाग्य से, इस मामले में परिणाम लगभग समान हैं:

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 के बाहर की जाँच कर सकते हैं एएनएन मानक

अनुमानित मॉडल की तैनाती

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

ट्यूनिंग स्कैन

आइए अब बेहतर प्रदर्शन/सटीकता ट्रेडऑफ़ प्राप्त करने के लिए हमारी स्कैनन परत को ट्यून करने पर ध्यान दें। इसे प्रभावी ढंग से करने के लिए, हमें सबसे पहले अपने आधारभूत प्रदर्शन और सटीकता को मापने की आवश्यकता है।

ऊपर से, हमारे पास पहले से ही एक एकल (गैर-बैच) क्वेरी को संसाधित करने के लिए हमारे मॉडल की विलंबता का माप है (हालांकि ध्यान दें कि इस विलंबता की उचित मात्रा मॉडल के गैर-स्कैन घटकों से है)।

अब हमें स्कैन की सटीकता की जांच करने की आवश्यकता है, जिसे हम रिकॉल के माध्यम से मापते हैं। x% के एक रिकॉल @ k का अर्थ है कि यदि हम सच्चे शीर्ष k पड़ोसियों को पुनः प्राप्त करने के लिए पाशविक बल का उपयोग करते हैं, और उन परिणामों की तुलना शीर्ष k पड़ोसियों को पुनः प्राप्त करने के लिए ScaNN का उपयोग करने के लिए करते हैं, तो ScaNN के x% परिणाम वास्तविक पाशविक बल परिणामों में होते हैं। आइए वर्तमान स्कैनएन खोजकर्ता के लिए रिकॉल की गणना करें।

सबसे पहले, हमें पाशविक बल उत्पन्न करने की आवश्यकता है, जमीनी सच्चाई टॉप-के:

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

अगला, हम अपने फ़ंक्शन को परिभाषित करते हैं जो रिकॉल की गणना करता है। प्रत्येक क्वेरी के लिए, यह गणना करता है कि ब्रूट फोर्स और स्कैनएन परिणामों के प्रतिच्छेदन में कितने परिणाम हैं और इसे ब्रूट फोर्स परिणामों की संख्या से विभाजित करते हैं। सभी प्रश्नों पर इस मात्रा का औसत हमारा स्मरण है।

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

यह हमें मौजूदा ScaNN कॉन्फिगरेशन के साथ बेसलाइन रिकॉल@10 देता है:

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 के ट्री-एएच एल्गोरिथम का उपयोग करता है। यह एल्गोरिथम एम्बेडिंग के डेटाबेस ("पेड़") को विभाजित करता है और फिर एएच का उपयोग करके इन विभाजनों में से सबसे आशाजनक स्कोर करता है, जो एक अत्यधिक अनुकूलित अनुमानित दूरी गणना दिनचर्या है।

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% होगा। हालांकि, पहले की सेटिंग की तुलना में, 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 शीर्ष 100 में से 10 विभाजन उठाता है, जबकि scann2 शीर्ष 1000 विभाजन के 100 चुनता है। उत्तरार्द्ध अधिक महंगा हो सकता है क्योंकि इसमें 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)

सामान्य तौर पर, स्कैनएन खोज को ट्यून करना सही ट्रेडऑफ़ चुनने के बारे में है। प्रत्येक व्यक्तिगत पैरामीटर परिवर्तन आम तौर पर खोज को तेज़ और अधिक सटीक दोनों नहीं बना देगा; हमारा लक्ष्य इन दो परस्पर विरोधी लक्ष्यों के बीच बेहतर तरीके से व्यापार करने के लिए मापदंडों को ट्यून करना है।

हमारे मामले में, scann2 काफी अधिक याद सुधार scann विलंबता में कुछ क्षति उठानी पड़ी। क्या हम अपने अधिकांश रिकॉल लाभ को संरक्षित करते हुए विलंबता को कम करने के लिए कुछ अन्य नॉब्स को वापस डायल कर सकते हैं?

आइए AH के साथ 70/1000=7% डेटासेट खोजने का प्रयास करें, और केवल अंतिम 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 की वर्तमान परिमाणीकरण तकनीक में विस्तृत है इस पत्र , आईसीएमएल 2020 में प्रकाशित कागज भी साथ-साथ जारी किया गया था इस ब्लॉग लेख जो हमारे तकनीक का एक उच्च स्तरीय सिंहावलोकन देता है।

कई संबंधित परिमाणीकरण तकनीक हमारे आईसीएमएल 2020 कागज के संदर्भ में उल्लेख कर रहे हैं, और अन्य scann से संबंधित अनुसंधान पर सूचीबद्ध है http://sanjivk.com/