Polecanie filmów: odzyskiwanie

Zobacz na TensorFlow.org Wyświetl źródło na GitHub Pobierz notatnik

Rzeczywiste systemy rekomendujące często składają się z dwóch etapów:

  1. Etap wyszukiwania odpowiada za wybranie początkowego zestawu setek kandydatów spośród wszystkich możliwych kandydatów. Głównym celem tego modelu jest skuteczne wyeliminowanie wszystkich kandydatów, którymi użytkownik nie jest zainteresowany. Ponieważ model wyszukiwania może dotyczyć milionów kandydatów, musi być wydajny obliczeniowo.
  2. Na etapie rankingu wyniki modelu wyszukiwania są dostosowywane w celu wybrania najlepszej możliwej garstki rekomendacji. Jego zadaniem jest zawężenie zbioru pozycji, którymi użytkownik może być zainteresowany, do krótkiej listy prawdopodobnych kandydatów.

W tym samouczku skupimy się na pierwszym etapie, odzyskiwaniu. Jeśli jesteś zainteresowany w rankingu etapie przyjrzeć się naszym rankingu tutorialu.

Modele wyszukiwania często składają się z dwóch podmodeli:

  1. Model zapytania obliczający reprezentację zapytania (zwykle wektor osadzania o stałych wymiarach) przy użyciu funkcji zapytania.
  2. Model kandydujący obliczający reprezentację kandydującą (wektor o równej wielkości) przy użyciu cech kandydujących

Dane wyjściowe obu modeli są następnie mnożone razem, aby uzyskać wynik powinowactwa między zapytaniem a kandydatem, przy czym wyższe wyniki wyrażają lepsze dopasowanie między kandydatem a zapytaniem.

W tym samouczku zbudujemy i wytrenujemy taki dwuwieżowy model przy użyciu zestawu danych Movielens.

Mamy zamiar:

  1. Pobierz nasze dane i podziel je na zestaw treningowy i testowy.
  2. Implementuj model pobierania.
  3. Dopasuj i oceń to.
  4. Wyeksportuj go w celu wydajnego udostępniania, budując przybliżony indeks najbliższych sąsiadów (ANN).

Zbiór danych

W Movielens zbiór danych to klasyczny zestaw danych z GroupLens badania grupy na University of Minnesota. Zawiera zestaw ocen przyznanych filmom przez zestaw użytkowników i jest koniem pociągowym badań systemu rekomendacji.

Dane mogą być traktowane na dwa sposoby:

  1. Można to interpretować jako wyrażenie, które filmy użytkownicy oglądali (i oceniali), a których nie. Jest to forma niejawnej informacji zwrotnej, w której zegarki użytkowników informują nas, które rzeczy wolą widzieć, a których woleliby nie widzieć.
  2. Może być również postrzegany jako wyrażenie, jak bardzo użytkownikom podobały się filmy, które obejrzeli. Jest to forma bezpośredniej informacji zwrotnej: biorąc pod uwagę, że użytkownik obejrzał film, możemy z grubsza stwierdzić, jak bardzo mu się podobał, patrząc na przyznaną mu ocenę.

W tym samouczku skupiamy się na systemie wyszukiwania: modelu, który przewiduje zestaw filmów z katalogu, które użytkownik prawdopodobnie obejrzy. Często dane niejawne są tutaj bardziej przydatne, dlatego będziemy traktować Movielens jako system niejawny. Oznacza to, że każdy film oglądany przez użytkownika jest przykładem pozytywnym, a każdy film, którego nie widział, jest przykładem negatywnym.

Import

Najpierw usuńmy nasz import.

pip install -q tensorflow-recommenders
pip install -q --upgrade tensorflow-datasets
pip install -q scann
import os
import pprint
import tempfile

from typing import Dict, Text

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

Przygotowanie zbioru danych

Przyjrzyjmy się najpierw danym.

Używamy zestawu danych MovieLens z Tensorflow zbiorów danych . Ładowanie movielens/100k_ratings daje tf.data.Dataset obiekt zawierający dane ocen i załadunek movielens/100k_movies daje tf.data.Dataset obiekt zawierający tylko dane filmów.

Należy pamiętać, że od MovieLens zestaw danych nie ma predefiniowanych podziałów, wszystkie dane znajdują się pod train rozłamu.

# Ratings data.
ratings = tfds.load("movielens/100k-ratings", split="train")
# Features of all the available movies.
movies = tfds.load("movielens/100k-movies", split="train")
2021-08-24 11:23:07.684827: E tensorflow/stream_executor/cuda/cuda_driver.cc:271] failed call to cuInit: CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected
2021-08-24 11:23:07.684881: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:156] kernel driver does not appear to be running on this host (kokoro-gcp-ubuntu-prod-1865084408): /proc/driver/nvidia/version does not exist
2021-08-24 11:23:07.686220: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.

Zestaw danych ocen zwraca słownik zawierający identyfikator filmu, identyfikator użytkownika, przypisaną ocenę, sygnaturę czasową, informacje o filmie i informacje o użytkowniku:

for x in ratings.take(1).as_numpy_iterator():
  pprint.pprint(x)
2021-08-24 11:23:08.510902: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:185] None of the MLIR Optimization Passes are enabled (registered 2)
{'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-08-24 11:23:08.793892: 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.

Zestaw danych filmów zawiera identyfikator filmu, tytuł filmu i dane o gatunkach, do których należy. Zwróć uwagę, że gatunki są zakodowane za pomocą etykiet liczb całkowitych.

for x in movies.take(1).as_numpy_iterator():
  pprint.pprint(x)
{'movie_genres': array([4]),
 'movie_id': b'1681',
 'movie_title': b'You So Crazy (1994)'}
2021-08-24 11:23:09.008985: 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.

W tym przykładzie skupimy się na danych dotyczących ocen. Inne samouczki wyjaśniają, jak korzystać z danych informacji o filmie, aby poprawić jakość modelu.

Trzymamy tylko user_id i movie_title pól w zbiorze danych.

ratings = ratings.map(lambda x: {
    "movie_title": x["movie_title"],
    "user_id": x["user_id"],
})
movies = movies.map(lambda x: x["movie_title"])

Aby dopasować i ocenić model, musimy podzielić go na zestaw uczący i oceniający. W przemysłowym systemie rekomendującym byłoby to najprawdopodobniej zrobione w czasie: dane do czasu $T$ byłyby wykorzystywane do przewidywania interakcji po $T$.

W tym prostym przykładzie użyjmy jednak losowego podziału, umieszczając 80% ocen w zestawie pociągów i 20% w zestawie testowym.

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)

Przeanalizujmy również unikalne identyfikatory użytkowników i tytuły filmów obecne w danych.

Jest to ważne, ponieważ musimy być w stanie odwzorować surowe wartości naszych cech kategorycznych na wektory osadzenia w naszych modelach. Aby to zrobić, potrzebujemy słownika, który odwzorowuje surową wartość cechy na liczbę całkowitą w ciągłym zakresie: pozwala nam to wyszukać odpowiednie osadzenia w naszych tabelach osadzania.

movie_titles = movies.batch(1_000)
user_ids = ratings.batch(1_000_000).map(lambda x: x["user_id"])

unique_movie_titles = np.unique(np.concatenate(list(movie_titles)))
unique_user_ids = np.unique(np.concatenate(list(user_ids)))

unique_movie_titles[:10]
array([b"'Til There Was You (1997)", b'1-900 (1994)',
       b'101 Dalmatians (1996)', b'12 Angry Men (1957)', b'187 (1997)',
       b'2 Days in the Valley (1996)',
       b'20,000 Leagues Under the Sea (1954)',
       b'2001: A Space Odyssey (1968)',
       b'3 Ninjas: High Noon At Mega Mountain (1998)',
       b'39 Steps, The (1935)'], dtype=object)

Wdrażanie modelu

Wybór architektury naszego modelu jest kluczową częścią modelowania.

Ponieważ budujemy model pobierania z dwiema wieżami, możemy zbudować każdą wieżę osobno, a następnie połączyć je w ostateczny model.

Wieża zapytań

Zacznijmy od wieży zapytań.

Pierwszym krokiem jest podjęcie decyzji o wymiarowości zapytania i reprezentacji kandydatów:

embedding_dimension = 32

Wyższe wartości będą odpowiadały modelom, które mogą być dokładniejsze, ale będą też wolniejsze w dopasowaniu i bardziej podatne na nadmierne dopasowanie.

Drugim jest zdefiniowanie samego modelu. Tutaj mamy zamiar użyć Keras przerób warstw do identyfikatorów użytkowników najpierw przekonwertować do liczb całkowitych, a następnie przekonwertować do zanurzeń tych użytkowników za pośrednictwem Embedding warstwy. Zauważ, że jako słownik używamy listy unikalnych identyfikatorów użytkowników, którą obliczyliśmy wcześniej:

user_model = tf.keras.Sequential([
  tf.keras.layers.experimental.preprocessing.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)
])

Prosty wzór jak to dokładnie odpowiada klasycznym faktoryzacji macierzy podejścia. Podczas definiowania podklasę tf.keras.Model dla tego prostego modelu może być przesadą, możemy łatwo rozszerzyć go dowolnie skomplikowanego modelu przy użyciu standardowych komponentów Keras, tak długo jak będziemy zwracać embedding_dimension wyjście -Szeroki na końcu.

Wieża kandydata

To samo możemy zrobić z wieżą kandydata.

movie_model = tf.keras.Sequential([
  tf.keras.layers.experimental.preprocessing.StringLookup(
      vocabulary=unique_movie_titles, mask_token=None),
  tf.keras.layers.Embedding(len(unique_movie_titles) + 1, embedding_dimension)
])

Metryka

W naszych danych treningowych mamy pozytywne pary (użytkownik, film). Aby dowiedzieć się, jak dobry jest nasz model, musimy porównać wynik powinowactwa, który model oblicza dla tej pary, z wynikami wszystkich innych możliwych kandydatów: jeśli wynik dla pozytywnej pary jest wyższy niż dla wszystkich innych kandydatów, nasz model jest bardzo dokładny.

Aby to zrobić, możemy użyć tfrs.metrics.FactorizedTopK metrykę. Metryka ma jeden wymagany argument: zestaw danych kandydatów, które są używane jako niejawne wartości ujemne do oceny.

W naszym przypadku, to jest movies zbiór danych, przekształcony zanurzeń pośrednictwem naszego filmowego modelu:

metrics = tfrs.metrics.FactorizedTopK(
  candidates=movies.batch(128).map(movie_model)
)

Strata

Kolejnym składnikiem jest strata wykorzystywana do trenowania naszego modelu. TFRS ma kilka warstw strat i zadań, aby to ułatwić.

W tym przypadku będziemy korzystać z Retrieval obiektu zadanie: wygody owijarki który łączy ze sobą funkcję straty i metryczny obliczeń:

task = tfrs.tasks.Retrieval(
  metrics=metrics
)

Samo zadanie jest warstwą Keras, która przyjmuje zapytania i osadzania kandydatów jako argumenty i zwraca obliczoną stratę: użyjemy tego do zaimplementowania pętli szkoleniowej modelu.

Pełny model

Teraz możemy to wszystko złożyć w model. TFRS naraża klasę modelu podstawowego ( tfrs.models.Model ), który usprawnia budujących modele: wszystko, co musisz zrobić, to założyć składników w __init__ metody i wdrożenie compute_loss sposób, biorąc w surowych cech i powrocie wartość strat .

Model bazowy zajmie się następnie stworzeniem odpowiedniej pętli treningowej pasującej do naszego modelu.

class MovielensModel(tfrs.Model):

  def __init__(self, user_model, movie_model):
    super().__init__()
    self.movie_model: tf.keras.Model = movie_model
    self.user_model: tf.keras.Model = user_model
    self.task: tf.keras.layers.Layer = task

  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)

tfrs.Model klasa bazowa jest po prostu wygoda Klasa: pozwala nam obliczyć straty zarówno szkoleń i badań przeprowadzonych przy użyciu tej samej metody.

Pod maską to wciąż zwykły model Keras. Można osiągnąć tę samą funkcjonalność przez dziedziczenie z tf.keras.Model i przesłanianie train_step i test_step funkcji (patrz przewodnik dla szczegółów):

class NoBaseClassMovielensModel(tf.keras.Model):

  def __init__(self, user_model, movie_model):
    super().__init__()
    self.movie_model: tf.keras.Model = movie_model
    self.user_model: tf.keras.Model = user_model
    self.task: tf.keras.layers.Layer = task

  def train_step(self, features: Dict[Text, tf.Tensor]) -> tf.Tensor:

    # Set up a gradient tape to record gradients.
    with tf.GradientTape() as tape:

      # Loss computation.
      user_embeddings = self.user_model(features["user_id"])
      positive_movie_embeddings = self.movie_model(features["movie_title"])
      loss = self.task(user_embeddings, positive_movie_embeddings)

      # Handle regularization losses as well.
      regularization_loss = sum(self.losses)

      total_loss = loss + regularization_loss

    gradients = tape.gradient(total_loss, self.trainable_variables)
    self.optimizer.apply_gradients(zip(gradients, self.trainable_variables))

    metrics = {metric.name: metric.result() for metric in self.metrics}
    metrics["loss"] = loss
    metrics["regularization_loss"] = regularization_loss
    metrics["total_loss"] = total_loss

    return metrics

  def test_step(self, features: Dict[Text, tf.Tensor]) -> tf.Tensor:

    # Loss computation.
    user_embeddings = self.user_model(features["user_id"])
    positive_movie_embeddings = self.movie_model(features["movie_title"])
    loss = self.task(user_embeddings, positive_movie_embeddings)

    # Handle regularization losses as well.
    regularization_loss = sum(self.losses)

    total_loss = loss + regularization_loss

    metrics = {metric.name: metric.result() for metric in self.metrics}
    metrics["loss"] = loss
    metrics["regularization_loss"] = regularization_loss
    metrics["total_loss"] = total_loss

    return metrics

W tych samouczków, jednak będziemy trzymać się za pomocą tfrs.Model klasę bazową, aby utrzymać naszą uwagę na modelowanie i abstrakcyjny z dala niektóre boilerplate.

Dopasowanie i ocena

Po zdefiniowaniu modelu możemy użyć standardowych procedur dopasowania i oceny Keras, aby dopasować i ocenić model.

Najpierw stwórzmy egzemplarz modelu.

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

Następnie przetasuj, wsadowo i w pamięci podręcznej dane uczące i oceniające.

cached_train = train.shuffle(100_000).batch(8192).cache()
cached_test = test.batch(4096).cache()

Następnie wytrenuj model:

model.fit(cached_train, epochs=3)
Epoch 1/3
10/10 [==============================] - 6s 308ms/step - factorized_top_k/top_1_categorical_accuracy: 7.6250e-04 - factorized_top_k/top_5_categorical_accuracy: 0.0072 - factorized_top_k/top_10_categorical_accuracy: 0.0167 - factorized_top_k/top_50_categorical_accuracy: 0.0915 - factorized_top_k/top_100_categorical_accuracy: 0.1698 - loss: 69885.1129 - regularization_loss: 0.0000e+00 - total_loss: 69885.1129
Epoch 2/3
10/10 [==============================] - 3s 287ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0028 - factorized_top_k/top_5_categorical_accuracy: 0.0184 - factorized_top_k/top_10_categorical_accuracy: 0.0375 - factorized_top_k/top_50_categorical_accuracy: 0.1682 - factorized_top_k/top_100_categorical_accuracy: 0.2917 - loss: 67523.3707 - regularization_loss: 0.0000e+00 - total_loss: 67523.3707
Epoch 3/3
10/10 [==============================] - 3s 280ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0036 - factorized_top_k/top_5_categorical_accuracy: 0.0224 - factorized_top_k/top_10_categorical_accuracy: 0.0459 - factorized_top_k/top_50_categorical_accuracy: 0.1880 - factorized_top_k/top_100_categorical_accuracy: 0.3162 - loss: 66302.9609 - regularization_loss: 0.0000e+00 - total_loss: 66302.9609
<keras.callbacks.History at 0x7f95f02a2390>

W miarę trenowania modelu strata spada, a zestaw metryk pobierania o najwyższej wartości k jest aktualizowany. Mówią nam one, czy prawdziwy wynik pozytywny znajduje się w k najwyższej pozycji pobranych z całego zestawu kandydatów. Na przykład, miara dokładności kategorycznej z pierwszej piątki wynosząca 0,2 mówi nam, że przeciętnie prawdziwy wynik pozytywny znajduje się w pierwszej piątce wyszukanych elementów w 20% przypadków.

Zwróć uwagę, że w tym przykładzie oceniamy metryki podczas szkolenia, a także oceny. Ponieważ może to być dość powolne w przypadku dużych zestawów kandydatów, rozsądnym może być wyłączenie obliczania metryk podczas uczenia i uruchamianie go tylko podczas oceny.

Na koniec możemy ocenić nasz model na zestawie testowym:

model.evaluate(cached_test, return_dict=True)
5/5 [==============================] - 2s 155ms/step - factorized_top_k/top_1_categorical_accuracy: 7.5000e-04 - factorized_top_k/top_5_categorical_accuracy: 0.0099 - factorized_top_k/top_10_categorical_accuracy: 0.0226 - factorized_top_k/top_50_categorical_accuracy: 0.1245 - factorized_top_k/top_100_categorical_accuracy: 0.2324 - loss: 31079.0635 - regularization_loss: 0.0000e+00 - total_loss: 31079.0635
{'factorized_top_k/top_1_categorical_accuracy': 0.000750000006519258,
 'factorized_top_k/top_5_categorical_accuracy': 0.009850000031292439,
 'factorized_top_k/top_10_categorical_accuracy': 0.02264999970793724,
 'factorized_top_k/top_50_categorical_accuracy': 0.12449999898672104,
 'factorized_top_k/top_100_categorical_accuracy': 0.23235000669956207,
 'loss': 28244.771484375,
 'regularization_loss': 0,
 'total_loss': 28244.771484375}

Wydajność zestawu testowego jest znacznie gorsza niż wydajność treningu. Wynika to z dwóch czynników:

  1. Nasz model prawdopodobnie będzie działał lepiej na danych, które widział, po prostu dlatego, że może je zapamiętać. To zjawisko overfittingu jest szczególnie silne, gdy modele mają wiele parametrów. Może to być pośredniczone przez regularyzację modelu i wykorzystanie funkcji użytkownika i filmu, które pomagają modelowi lepiej uogólniać niewidoczne dane.
  2. Model ponownie poleca niektóre z obejrzanych już przez użytkowników filmów. Te znane, pozytywne zegarki mogą wypierać filmy testowe z najlepszych rekomendacji K.

Z drugim zjawiskiem można się uporać, wyłączając z rekomendacji testowych wcześniej oglądane filmy. To podejście jest stosunkowo powszechne w literaturze dotyczącej systemów rekomendujących, ale nie stosujemy go w tych samouczkach. Jeśli nie polecanie przeszłych zegarków jest ważne, powinniśmy oczekiwać, że odpowiednio określone modele będą uczyć się tego zachowania automatycznie na podstawie historii użytkowników i informacji kontekstowych w przeszłości. Ponadto często warto polecić ten sam przedmiot wielokrotnie (np. wiecznie popularny serial telewizyjny lub regularnie kupowany przedmiot).

Dokonywanie prognoz

Teraz, gdy mamy model, chcielibyśmy móc dokonywać prognoz. Możemy użyć tfrs.layers.factorized_top_k.BruteForce warstwę, aby to zrobić.

# Create a model that takes in raw query features, and
index = tfrs.layers.factorized_top_k.BruteForce(model.user_model)
# recommends movies out of the entire movies dataset.
index.index_from_dataset(
  tf.data.Dataset.zip((movies.batch(100), movies.batch(100).map(model.movie_model)))
)

# Get recommendations.
_, titles = index(tf.constant(["42"]))
print(f"Recommendations for user 42: {titles[0, :3]}")
Recommendations for user 42: [b'Bridges of Madison County, The (1995)'
 b'Father of the Bride Part II (1995)' b'Rudy (1993)']

Oczywiście, BruteForce warstwa będzie zbyt wolny, aby służyć model z wielu możliwych kandydatów. W poniższych sekcjach pokazano, jak przyspieszyć to za pomocą przybliżonego indeksu pobierania.

Porcja modelu

Po przeszkoleniu modelu potrzebujemy sposobu na jego wdrożenie.

W modelu z dwiema wieżami serwowanie składa się z dwóch elementów:

  • model zapytania obsługującego, uwzględniający cechy zapytania i przekształcający je w osadzanie zapytania, oraz
  • model obsługujący kandydata. Najczęściej ma to postać przybliżonego indeksu najbliższych sąsiadów (ANN), który umożliwia szybkie przybliżone wyszukiwanie kandydatów w odpowiedzi na zapytanie generowane przez model zapytań.

W TFRS oba komponenty można spakować w jeden eksportowalny model, co daje nam model, który pobiera surowy identyfikator użytkownika i zwraca tytuły najlepszych filmów dla tego użytkownika. Odbywa się to poprzez eksport modelu do SavedModel formacie, który sprawia, że jest to możliwe, aby służyć za pomocą TensorFlow serwowania .

Aby wdrożyć model takiego, po prostu wyeksportować BruteForce warstwę stworzyliśmy powyżej:

# Export the query model.
with tempfile.TemporaryDirectory() as tmp:
  path = os.path.join(tmp, "model")

  # Save the index.
  tf.saved_model.save(index, path)

  # Load it back; can also be done in TensorFlow Serving.
  loaded = tf.saved_model.load(path)

  # Pass a user id in, get top predicted movie titles back.
  scores, titles = loaded(["42"])

  print(f"Recommendations: {titles[0][:3]}")
2021-08-24 11:23:28.077112: 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/tmprdeui3rt/model/assets
INFO:tensorflow:Assets written to: /tmp/tmprdeui3rt/model/assets
Recommendations: [b'Bridges of Madison County, The (1995)'
 b'Father of the Bride Part II (1995)' b'Rudy (1993)']

Możemy również wyeksportować przybliżony indeks pobierania, aby przyspieszyć prognozy. Umożliwi to sprawne wyłonienie rekomendacji z zestawów dziesiątek milionów kandydatów.

Aby to zrobić, możemy użyć scann pakietu. Jest to opcjonalna zależność TFRS i mamy zainstalowany oddzielnie na początku tego tutoriala dzwoniąc !pip install -q scann .

Po zainstalowaniu możemy użyć TFRS ScaNN warstwy:

scann_index = tfrs.layers.factorized_top_k.ScaNN(model.user_model)
scann_index.index_from_dataset(
  tf.data.Dataset.zip((movies.batch(100), movies.batch(100).map(model.movie_model)))
)
2021-08-24 11:23:28.652143: I scann/partitioning/partitioner_factory_base.cc:71] Size of sampled dataset for training partition: 1682
2021-08-24 11:23:28.661092: I ./scann/partitioning/kmeans_tree_partitioner_utils.h:102] PartitionerFactory ran in 8.895297ms.
<tensorflow_recommenders.layers.factorized_top_k.ScaNN at 0x7f95375bfe10>

Warstwa ta wykona przybliżone wyszukiwań: To sprawia pobraniem nieco mniej dokładne, ale rzędy wielkości szybciej na dużych zbiorów kandydujących.

# Get recommendations.
_, titles = scann_index(tf.constant(["42"]))
print(f"Recommendations for user 42: {titles[0, :3]}")
Recommendations for user 42: [b'Sleepless in Seattle (1993)' b'Father of the Bride Part II (1995)'
 b'Hunchback of Notre Dame, The (1996)']

Eksportując go do serwowania jest tak proste jak eksportowania BruteForce warstwy:

# Export the query model.
with tempfile.TemporaryDirectory() as tmp:
  path = os.path.join(tmp, "model")

  # Save the index.
  tf.saved_model.save(
      index,
      path,
      options=tf.saved_model.SaveOptions(namespace_whitelist=["Scann"])
  )

  # Load it back; can also be done in TensorFlow Serving.
  loaded = tf.saved_model.load(path)

  # Pass a user id in, get top predicted movie titles back.
  scores, titles = loaded(["42"])

  print(f"Recommendations: {titles[0][:3]}")
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/tmpxlk0mik6/model/assets
INFO:tensorflow:Assets written to: /tmp/tmpxlk0mik6/model/assets
Recommendations: [b'Bridges of Madison County, The (1995)'
 b'Father of the Bride Part II (1995)' b'Rudy (1993)']

Aby dowiedzieć się więcej na temat używania i strojenie szybko przybliżone modele pobierania, rzucić okiem na naszej efektywnej porcji tutoriala.

Następne kroki

Na tym kończy się samouczek dotyczący pobierania.

Aby rozwinąć to, co jest tutaj prezentowane, spójrz na:

  1. Nauka modeli wielozadaniowych: wspólna optymalizacja pod kątem ocen i kliknięć.
  2. Korzystanie z metadanych filmu: budowanie bardziej złożonego modelu filmu w celu złagodzenia zimnego startu.