Recommander des films : récupération

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHub Télécharger le cahier

Les systèmes de recommandation du monde réel sont souvent composés de deux étapes :

  1. L'étape de récupération est responsable de la sélection d'un ensemble initial de centaines de candidats parmi tous les candidats possibles. L'objectif principal de ce modèle est d'éliminer efficacement tous les candidats qui ne intéressent pas l'utilisateur. Étant donné que le modèle de récupération peut traiter des millions de candidats, il doit être efficace en termes de calcul.
  2. L'étape de classement prend les sorties du modèle de récupération et les affine pour sélectionner la meilleure poignée possible de recommandations. Sa tâche consiste à réduire l'ensemble d'éléments susceptibles d'intéresser l'utilisateur à une liste restreinte de candidats probables.

Dans ce tutoriel, nous allons nous concentrer sur la première étape, la récupération. Si vous êtes intéressé par la phase de classement, consultez notre classement tutoriel.

Les modèles de récupération sont souvent composés de deux sous-modèles :

  1. Un modèle de requête calculant la représentation de la requête (normalement un vecteur d'intégration à dimension fixe) à l'aide de fonctionnalités de requête.
  2. Un modèle candidat calculant la représentation candidate (un vecteur de taille égale) à l'aide des caractéristiques candidates

Les sorties des deux modèles sont ensuite multipliées ensemble pour donner un score d'affinité requête-candidat, avec des scores plus élevés exprimant une meilleure correspondance entre le candidat et la requête.

Dans ce didacticiel, nous allons créer et entraîner un tel modèle à deux tours à l'aide de l'ensemble de données Movielens.

Nous allons :

  1. Obtenez nos données et divisez-les en un ensemble d'entraînement et de test.
  2. Mettre en œuvre un modèle de récupération.
  3. Ajustez-le et évaluez-le.
  4. Exportez-le pour un service efficace en créant un index approximatif des voisins les plus proches (ANN).

L'ensemble de données

L'ensemble de données MovieLens est un ensemble de données classique des GroupLens groupe de recherche à l'Université du Minnesota. Il contient un ensemble de notes attribuées aux films par un ensemble d'utilisateurs et constitue un outil de recherche de système de recommandation.

Les données peuvent être traitées de deux manières :

  1. Il peut être interprété comme exprimant les films que les utilisateurs ont regardés (et notés) et ceux qu'ils n'ont pas vus. Il s'agit d'une forme de retour implicite, où les montres des utilisateurs nous indiquent ce qu'ils préfèrent voir et ce qu'ils préfèrent ne pas voir.
  2. Cela peut également être considéré comme exprimant à quel point les utilisateurs ont aimé les films qu'ils ont regardés. C'est une forme de retour explicite : étant donné qu'un utilisateur a regardé un film, on peut dire à peu près combien il a aimé en regardant la note qu'il a donnée.

Dans ce tutoriel, nous nous concentrons sur un système de récupération : un modèle qui prédit un ensemble de films du catalogue que l'utilisateur est susceptible de regarder. Souvent, les données implicites sont plus utiles ici, et nous allons donc traiter Movielens comme un système implicite. Cela signifie que chaque film qu'un utilisateur a regardé est un exemple positif, et chaque film qu'il n'a pas vu est un exemple implicitement négatif.

Importations

Éliminons d'abord nos importations.

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

Préparation du jeu de données

Voyons d'abord les données.

Nous utilisons l'ensemble de données MovieLens de tensorflow datasets . Chargement movielens/100k_ratings donne un tf.data.Dataset objet contenant les données de notation et le chargement movielens/100k_movies donne un tf.data.Dataset objet contenant uniquement les données de films.

Notez que depuis le jeu de données MovieLens ne dispose pas des scissions prédéfinies, toutes les données sont en train de Split.

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

L'ensemble de données de classement renvoie un dictionnaire de l'identifiant du film, de l'identifiant de l'utilisateur, de la note attribuée, de l'horodatage, des informations sur le film et des informations sur l'utilisateur :

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.

L'ensemble de données de films contient l'identifiant du film, le titre du film et des données sur les genres auxquels il appartient. Notez que les genres sont codés avec des étiquettes entières.

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.

Dans cet exemple, nous allons nous concentrer sur les données d'évaluation. D'autres didacticiels explorent également comment utiliser les données d'informations sur le film pour améliorer la qualité du modèle.

Nous ne gardons que le user_id et movie_title champs dans l'ensemble de données.

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

Pour ajuster et évaluer le modèle, nous devons le diviser en un ensemble de formation et d'évaluation. Dans un système de recommandation industriel, cela se ferait très probablement par temps : les données jusqu'au temps $T$ seraient utilisées pour prédire les interactions après $T$.

Dans cet exemple simple, cependant, utilisons une répartition aléatoire, mettant 80% des notes dans l'ensemble de train et 20% dans l'ensemble de test.

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)

Déterminons également les identifiants d'utilisateur uniques et les titres de films présents dans les données.

Ceci est important car nous devons être en mesure de mapper les valeurs brutes de nos caractéristiques catégorielles aux vecteurs intégrés dans nos modèles. Pour ce faire, nous avons besoin d'un vocabulaire qui mappe une valeur de caractéristique brute à un entier dans une plage contiguë : cela nous permet de rechercher les plongements correspondants dans nos tables de plongement.

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)

Implémentation d'un modèle

Le choix de l'architecture de notre modèle est un élément clé de la modélisation.

Parce que nous construisons un modèle de récupération à deux tours, nous pouvons construire chaque tour séparément, puis les combiner dans le modèle final.

La tour d'interrogation

Commençons par la tour de requête.

La première étape consiste à décider de la dimensionnalité de la requête et des représentations candidates :

embedding_dimension = 32

Des valeurs plus élevées correspondront à des modèles qui peuvent être plus précis, mais seront également plus lents à s'adapter et plus sujets au surajustement.

La seconde est de définir le modèle lui-même. Ici, nous allons utiliser des couches de pré - traitement KERAS premier utilisateur à convertir ids en entiers, puis convertir ceux incorporations utilisateur via une Embedding couche. Notez que nous utilisons la liste des identifiants d'utilisateur uniques que nous avons calculés précédemment comme vocabulaire :

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

Un modèle simple comme cela correspond exactement à un classique factorisation matrice approche. Lors de la définition d' une sous - classe de tf.keras.Model pour ce modèle simple pourrait être surpuissant, nous pouvons facilement l' étendre à un modèle arbitrairement complexe en utilisant des composants standards KERAS, aussi longtemps que nous revenons d' une embedding_dimension sortie -Wide à la fin.

La tour des candidats

Nous pouvons faire la même chose avec la tour candidate.

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

Métrique

Dans nos données d'entraînement, nous avons des paires positives (utilisateur, film). Pour déterminer la qualité de notre modèle, nous devons comparer le score d'affinité que le modèle calcule pour cette paire aux scores de tous les autres candidats possibles : si le score de la paire positive est plus élevé que pour tous les autres candidats, notre modèle est très précis.

Pour ce faire, nous pouvons utiliser la tfrs.metrics.FactorizedTopK métrique. La métrique a un argument obligatoire : l'ensemble de données des candidats qui sont utilisés comme négatifs implicites pour l'évaluation.

Dans notre cas, c'est le movies ensemble de données, converti en incorporations via notre modèle de film:

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

Perte

Le composant suivant est la perte utilisée pour entraîner notre modèle. TFRS a plusieurs couches et tâches de perte pour rendre cela facile.

Dans ce cas, nous utilisons la Retrieval objet tâche: commodité emballage que liasses la fonction de perte et calcul métrique:

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

La tâche elle-même est une couche Keras qui prend la requête et les plongements candidats comme arguments, et renvoie la perte calculée : nous l'utiliserons pour implémenter la boucle d'apprentissage du modèle.

Le modèle complet

Nous pouvons maintenant tout assembler dans un modèle. TFRS expose une classe de modèle de base ( tfrs.models.Model ) qui rationalise la construction de modèles: tout ce que nous devons faire est de mettre en place les composants dans la __init__ méthode, et mettre en œuvre la compute_loss méthode, en prenant les fonctions premières et de retourner une valeur de perte .

Le modèle de base s'occupera ensuite de créer la boucle d'entraînement appropriée pour s'adapter à notre modèle.

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)

La tfrs.Model classe de base est une classe tout simplement pratique: il nous permet de calculer les pertes de formation et de test en utilisant la même méthode.

Sous le capot, c'est toujours un modèle Keras sobre. Vous pouvez obtenir la même fonctionnalité en héritant de tf.keras.Model et en remplaçant les train_step et test_step fonctions (voir le guide pour les détails):

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

Dans ces tutoriels, cependant, nous restons fidèles à l' aide de la tfrs.Model classe de base pour continuer à nous concentrer sur la modélisation et abstraire certains des passe- partout.

Ajustement et évaluation

Après avoir défini le modèle, nous pouvons utiliser les routines d'ajustement et d'évaluation Keras standard pour ajuster et évaluer le modèle.

Commençons par instancier le modèle.

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

Ensuite, mélangez, regroupez et cachez les données d'entraînement et d'évaluation.

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

Ensuite, entraînez le modèle :

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>

Au fur et à mesure que le modèle s'entraîne, la perte diminue et un ensemble de métriques de récupération top-k est mis à jour. Ceux-ci nous indiquent si le vrai positif se trouve dans le top-k des éléments récupérés de l'ensemble des candidats. Par exemple, une métrique de précision catégorielle parmi les 5 premières de 0,2 nous indiquerait qu'en moyenne, le vrai positif se trouve dans les 5 éléments les plus récupérés 20 % du temps.

Notez que, dans cet exemple, nous évaluons les métriques pendant la formation ainsi que l'évaluation. Étant donné que cela peut être assez lent avec de grands ensembles de candidats, il peut être prudent de désactiver le calcul des métriques lors de la formation et de ne l'exécuter que lors de l'évaluation.

Enfin, nous pouvons évaluer notre modèle sur l'ensemble de test :

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}

Les performances de l'ensemble de test sont bien pires que les performances d'entraînement. Cela est dû à deux facteurs:

  1. Notre modèle est susceptible de mieux fonctionner sur les données qu'il a vues, simplement parce qu'il peut les mémoriser. Ce phénomène de surapprentissage est particulièrement fort lorsque les modèles ont de nombreux paramètres. Il peut être médié par la régularisation du modèle et l'utilisation de fonctionnalités utilisateur et vidéo qui aident le modèle à mieux se généraliser aux données invisibles.
  2. Le modèle recommande à nouveau certains des films déjà regardés par les utilisateurs. Ces montres positives connues peuvent évincer les films de test des meilleures recommandations K.

Le deuxième phénomène peut être combattu en excluant les films déjà vus des recommandations de test. Cette approche est relativement courante dans la littérature sur les systèmes de recommandation, mais nous ne la suivons pas dans ces tutoriels. S'il est important de ne pas recommander les montres passées, nous devrions nous attendre à ce que des modèles correctement spécifiés apprennent automatiquement ce comportement à partir de l'historique des utilisateurs passés et des informations contextuelles. De plus, il est souvent approprié de recommander le même article plusieurs fois (par exemple, une série télévisée à feuilles persistantes ou un article acheté régulièrement).

Faire des prédictions

Maintenant que nous avons un modèle, nous aimerions pouvoir faire des prédictions. Nous pouvons utiliser la tfrs.layers.factorized_top_k.BruteForce couche pour ce faire.

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

Bien sûr, la BruteForce couche va être trop lent pour servir un modèle avec de nombreux candidats possibles. Les sections suivantes montrent comment accélérer cette opération en utilisant un index de récupération approximatif.

Modèle au service

Une fois le modèle formé, nous avons besoin d'un moyen de le déployer.

Dans un modèle de récupération à deux tours, la diffusion comporte deux composants :

  • un modèle de requête de service, prenant en compte les caractéristiques de la requête et les transformant en une intégration de requête, et
  • un modèle de candidat au service. Cela prend le plus souvent la forme d'un index approximatif des voisins les plus proches (ANN) qui permet une recherche approximative rapide des candidats en réponse à une requête produite par le modèle de requête.

Dans TFRS, les deux composants peuvent être regroupés dans un seul modèle exportable, ce qui nous donne un modèle qui prend l'identifiant utilisateur brut et renvoie les titres des meilleurs films pour cet utilisateur. Cela se fait via l' exportation du modèle à un SavedModel format, ce qui permet de servir en utilisant tensorflow service .

Pour déployer un modèle comme celui - ci, nous avons simplement exporter la BruteForce couche que nous avons créé ci - dessus:

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

Nous pouvons également exporter un index de récupération approximatif pour accélérer les prédictions. Cela permettra de faire émerger efficacement des recommandations à partir d'ensembles de dizaines de millions de candidats.

Pour ce faire, nous pouvons utiliser le scann package. Ceci est une dépendance facultative de TFRS, et nous avons installé séparément au début de ce tutoriel en appelant !pip install -q scann .

Une fois installé , nous pouvons utiliser la TFRS ScaNN couche:

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>

Cette couche effectue des recherches approximatives: ce qui rend la récupération un peu moins précis, mais les ordres de grandeur plus rapide sur les grands ensembles candidats.

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

Exportation pour servir est aussi facile que l' exportation de la BruteForce couche:

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

Pour en savoir plus sur l' utilisation et l' optimisation des modèles de récupération rapide approximatives, jetez un oeil à notre portion efficace tutoriel.

Prochaines étapes

Ceci conclut le didacticiel de récupération.

Pour développer ce qui est présenté ici, jetez un œil à:

  1. Apprentissage de modèles multitâches : optimisation conjointe des notes et des clics.
  2. Utilisation des métadonnées de film : création d'un modèle de film plus complexe pour atténuer le démarrage à froid.