Verimli servis

TensorFlow.org'da görüntüleyin Google Colab'da çalıştırın Kaynağı GitHub'da görüntüleyin Not defterini indir

Alım modeller genellikle milyonlarca veya adayların milyonlarca hatta yüz milyonlarca kişi arasında ilk aday bir avuç yüzey inşa edilir. Kullanıcının bağlamına ve davranışına tepki verebilmek için, bunu anında, milisaniyeler içinde yapabilmeleri gerekir.

Yaklaşık en yakın komşu araması (YSA) bunu mümkün kılan teknolojidir. Bu öğreticide, TFRS alımını sorunsuz bir şekilde milyonlarca öğeye ölçeklendirmek için en gelişmiş en yakın komşu alma paketi olan ScanNN'nin nasıl kullanılacağını göstereceğiz.

ScanNN nedir?

ScanNN, büyük ölçekte yoğun vektör benzerlik araması yapan bir Google Research kitaplığıdır. Bir aday yerleştirme veri tabanı verildiğinde, ScanNN bu yerleştirmeleri çıkarım zamanında hızlı bir şekilde aranmalarına izin verecek şekilde indeksler. ScanNN, en iyi hız-doğruluk dengesini elde etmek için son teknoloji vektör sıkıştırma tekniklerini ve dikkatle uygulanan algoritmaları kullanır. Doğruluk açısından çok az taviz verirken kaba kuvvet aramasından büyük ölçüde daha iyi performans gösterebilir.

ScanNN destekli bir model oluşturma

TFRS Scann denemek için, biz de yaptığı gibi basit MovieLens alma modeli inşa edeceğiz temel alma öğretici. Bu öğreticiyi izlediyseniz, bu bölüm size tanıdık gelecek ve güvenle atlanabilir.

Başlamak için TFRS ve TensorFlow Veri Kümelerini yükleyin:

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

Biz de yüklemeniz gerekir scann : Bu TFRS'nin isteğe bağlı bağımlılık, ve ihtiyaçları böylece ayrı ayrı kurulacak.

pip install -q scann

Gerekli tüm içe aktarmaları ayarlayın.

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

Ve verileri yükleyin:

# 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

Bir model oluşturmadan önce, kullanıcı ve film sözlüklerini kurmamız gerekiyor:

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.

Ayrıca eğitim ve test setlerini de kuracağız:

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)

Model tanımı

Sadece olduğu gibi temel alma öğretici, basit iki kuleli modeli oluşturmak.

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)

Montaj ve değerlendirme

Bir TFRS modeli sadece bir Keras modelidir. Onu derleyebiliriz:

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

Tahmin et:

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>

Ve değerlendirin.

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}

Yaklaşık tahmin

Bir sorguya yanıt olarak en iyi adayları almanın en basit yolu, bunu kaba kuvvetle yapmaktır: olası tüm filmler için kullanıcı film puanlarını hesaplamak, sıralamak ve en iyi birkaç öneriyi seçmek.

TFRS, bu aracılığıyla gerçekleştirilir BruteForce tabakasının:

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>

Oluşturulan ve (aracılığıyla adaylarla doldurulur kez index yöntemine), tahminleri çıkmak diyebilirsiniz:

# 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'den az filmden oluşan küçük bir veri kümesinde bu çok hızlıdır:

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

Ama daha fazla adayımız olursa ne olur - binlerce yerine milyonlarca?

Tüm filmlerimizi birden çok kez indeksleyerek bunu simüle edebiliriz:

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

Biz inşa edebilirsiniz BruteForce bu büyük veri kümesi üzerinde endeksi:

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>

tavsiyeler hala aynı

_, 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)']

Ama çok daha uzun sürüyorlar. 1 milyon filmlik bir aday setiyle, kaba kuvvet tahmini oldukça yavaşlıyor:

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

Aday sayısı arttıkça ihtiyaç duyulan süre doğrusal olarak artar: 10 milyon adayla en iyi adaylara hizmet vermek 250 milisaniye sürer. Bu, canlı bir hizmet için açıkça çok yavaş.

Yaklaşık mekanizmaların devreye girdiği yer burasıdır.

TFRS Scann kullanma yoluyla gerçekleştirilir tfrs.layers.factorized_top_k.ScaNN tabakasının. Diğer üst k katmanlarla aynı arayüzü takip eder:

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>

Öneriler (yaklaşık!) aynı

_, 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)']

Ancak bunları hesaplamak çok, çok daha hızlıdır:

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

Bu durumda, ~1 milyonluk bir diziden ilk 3 filmi yaklaşık 2 milisaniyede alabiliriz: En iyi adayları kaba kuvvetle hesaplamaktan 15 kat daha hızlı. Yaklaşık yöntemlerin avantajı, daha büyük veri kümeleri için daha da büyür.

Yaklaşımın değerlendirilmesi

Yaklaşık üst K alma mekanizmalarını (ScaNN gibi) kullanırken, alma hızı genellikle doğruluk pahasına gelir. Bu ödünleşimi anlamak için, ScanNN kullanırken modelin değerlendirme metriklerini ölçmek ve bunları temel ile karşılaştırmak önemlidir.

Neyse ki, TFRS bunu kolaylaştırıyor. ScaNN kullanarak metriklerle alma görevindeki metrikleri geçersiz kılıyoruz, modeli yeniden derliyoruz ve değerlendirmeyi çalıştırıyoruz.

Karşılaştırma yapmak için önce temel sonuçları çalıştıralım. Orijinal film seti yerine genişletilmiş aday setini kullandıklarından emin olmak için yine de ölçümlerimizi geçersiz kılmamız gerekiyor:

# 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

Aynısını ScanNN kullanarak da yapabiliriz:

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

ScanNN tabanlı değerlendirme çok, çok daha hızlıdır: on kattan daha hızlıdır! Bu avantaj, daha büyük veri kümeleri için daha da büyüyecek ve bu nedenle, büyük veri kümeleri için, model geliştirme hızını iyileştirmek için her zaman ScanNN tabanlı değerlendirmeyi çalıştırmak ihtiyatlı olabilir.

Peki ya sonuçlar? Neyse ki, bu durumda sonuçlar neredeyse aynı:

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

Bu, bu yapay veri üzerinde, yaklaşımdan çok az kayıp olduğunu göstermektedir. Genel olarak, tüm yaklaşık yöntemler hız-doğruluk değiş tokuşu sergiler. Erik Bernhardsson en göz atabilirsiniz daha derinlemesine Bunu anlamak için YSA kriterler .

Yaklaşık modeli dağıtma

ScaNN tabanlı modeli tam TensorFlow modelleri entegre ve kolay başka TensorFlow modelini sunan gibidir çekiyor.

Biz olarak kaydedebilirsiniz SavedModel nesnesi

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

ve sonra yükleyin ve servis yapın, tam olarak aynı sonuçları geri alın:

_, 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)']

Ortaya çıkan model, TensorFlow ve ScanNN'nin kurulu olduğu herhangi bir Python hizmetinde sunulabilir.

Aynı zamanda bir Docker kapsayıcı olarak kullanılabilir TensorFlow Sunum özelleştirilmiş bir sürümünü kullanarak servis edilebilir Docker Hub . Ayrıca gelen görüntü kendiniz oluşturabilirsiniz Dockerfile .

ScanNN'yi Ayarlama

Şimdi, daha iyi bir performans/doğruluk dengesi elde etmek için ScanNN katmanımızı ayarlamaya bakalım. Bunu etkin bir şekilde yapabilmek için öncelikle temel performansımızı ve doğruluğumuzu ölçmemiz gerekir.

Yukarıdan, modelimizin tek (toplu olmayan) bir sorguyu işleme gecikmesinin bir ölçümüne zaten sahibiz (bu gecikmenin makul bir miktarının modelin ScanNN olmayan bileşenlerinden geldiğini unutmayın).

Şimdi, geri çağırma yoluyla ölçtüğümüz ScanNN'nin doğruluğunu araştırmamız gerekiyor. %x'lik bir geri çağırma @k, gerçek en üstteki k komşuyu almak için kaba kuvvet kullanırsak ve bu sonuçları aynı zamanda en üstteki k komşuyu almak için ScaNN kullanmakla karşılaştırırsak, ScaNN sonuçlarının %x'inin gerçek kaba kuvvet sonuçlarında olacağı anlamına gelir. Mevcut ScanNN arayıcı için geri çağırmayı hesaplayalım.

İlk olarak, kaba kuvvet, temel gerçek top-k'yi oluşturmamız gerekiyor:

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

Bizim değişken titles_ground_truth şimdi kaba kuvvet alma tarafından döndürülen üst-10 film öneriler içerir. Şimdi aynı önerileri ScanNN kullanırken de hesaplayabiliriz:

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

Ardından, geri çağırmayı hesaplayan fonksiyonumuzu tanımlıyoruz. Her sorgu için kaba kuvvet ile ScanNN sonuçlarının kesişiminde kaç tane sonuç olduğunu sayar ve bunu kaba kuvvet sonuçlarının sayısına böler. Bu miktarın tüm sorgulardaki ortalaması geri çağrımızdır.

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

Bu bize mevcut ScanNN yapılandırmasıyla temel geri çağırma@10 sağlar:

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

Temel gecikmeyi de ölçebiliriz:

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

Bakalım daha iyisini yapabilecek miyiz!

Bunu yapmak için, ScanNN'nin ayar düğmelerinin performansı nasıl etkilediğine dair bir modele ihtiyacımız var. Mevcut modelimiz, ScanNN'nin ağaç-AH algoritmasını kullanır. Bu algoritma, gömme veritabanını ("ağaç") bölümlere ayırır ve daha sonra, yüksek düzeyde optimize edilmiş bir yaklaşık mesafe hesaplama rutini olan AH'yi kullanarak bu bölümlerin en umut verici olanını puanlar.

TensorFlow çok öneride Scann Keras katman setleri için varsayılan parametreleri num_leaves=100 ve num_leaves_to_search=10 . Bu, veritabanımızın 100 ayrık alt kümeye bölündüğü ve bu bölümlerin en umut verici 10 tanesinin AH ile puanlandığı anlamına gelir. Bu, veri kümesinin %10/100=%10'unun AH ile arandığı anlamına gelir.

Mecbur kalırsak, diyelim ki, num_leaves=1000 ve num_leaves_to_search=100 , biz de AH ile veritabanının% 10 aramaktayız. Daha yüksek Ancak, önceki ayara göre, biz aramak istiyorsunuz% 10, daha yüksek kaliteli adayları içerecek num_leaves veri kümesi parçaları değerinde arama ne hakkında ince taneli kararlar bizi tanır.

Daha sonra hiçbir sürpriz olduğunu ile num_leaves=1000 ve num_leaves_to_search=100 önemli ölçüde daha yüksek hatırlama olsun:

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

Ancak, bir değiş tokuş olarak gecikme süremiz de arttı. Bunun nedeni, bölümleme adımının daha pahalı hale gelmesidir; scann ise top 10 100 bölümleri seçer scann2 üst 1000 bölümleri 100 seçer. İkincisi daha pahalı olabilir çünkü 10 kat daha fazla bölüme bakmayı gerektirir.

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

Genel olarak, ScanNN aramasını ayarlamak, doğru ödünleşimleri seçmekle ilgilidir. Her bir bağımsız parametre değişikliği genellikle aramayı hem daha hızlı hem de daha doğru yapmaz; amacımız, bu iki çelişen hedef arasında en uygun şekilde dengeyi sağlamak için parametreleri ayarlamaktır.

Bizim durumumuzda, scann2 anlamlı üzerinde hatırlama geliştirilmiş scann gecikme bazı maliyetle. Geri çağırma avantajımızın çoğunu korurken gecikmeyi azaltmak için diğer bazı düğmeleri geri çevirebilir miyiz?

AH ile veri kümesinin %70/1000=%7'sini aramayı ve yalnızca son 400 adayı yeniden puanlamayı deneyelim:

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 fazla% 3 mutlak hatırlama kazanç hakkında sunar scann daha düşük bekleme süreleri sırasında:

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

Bu düğmeler, doğruluk-performans pareto sınırı boyunca farklı noktalar için optimize etmek üzere daha fazla ayarlanabilir. ScanNN'nin algoritmaları, çok çeşitli geri çağırma hedeflerinde son teknoloji performansa ulaşabilir.

daha fazla okuma

ScanNN, sonuçlarına ulaşmak için gelişmiş vektör niceleme teknikleri ve yüksek düzeyde optimize edilmiş uygulama kullanır. Vektör niceleme alanı, çeşitli yaklaşımlarla zengin bir tarihe sahiptir. Scann mevcut nicemleme tekniği de ayrıntılı olarak verilmiştir Bu yazıda kağıt da birlikte serbest bırakıldı ICML 2020 de yayınlanan, bu blog makalesinde bizim tekniğinin yüksek düzeyde bilgi verir.

Birçok ilgili nicemleme teknikleri bizim ICML 2020 kağıdın referanslarında anılan ve diğer Scann ilgili araştırma listelenir http://sanjivk.com/