Trouver les voisins les plus proches pour indexer et interroger les représentations vectorielles continues dans Spanner

Cette page explique comment créer un indice vectoriel et interroger des représentations vectorielles continues à l'aide des fonctions de distance cosinus approximative, de distance euclidienne approximative et de produit scalaire vectoriel approximatif. Vous pouvez utiliser ces pour trouver les voisins les plus proches (ANN) dans Spanner. Lorsqu'un ensemble de données est petit, vous pouvez utiliser l'algorithme des k plus proches voisins (KNN) pour trouver les vecteurs k les plus proches. Toutefois, à mesure que votre ensemble de données augmente, la latence et le coût d'une recherche KNN augmentent également. Vous pouvez utiliser une ANN pour trouver les k plus proches voisins approximatifs avec une latence et des coûts considérablement réduits.

Voisins les plus proches approximatifs

Dans une recherche ANN, les vecteurs k renvoyés ne sont pas les vrais k voisins les plus proches. Parfois, quelques vecteurs qui ne figurent pas parmi les k premiers k les plus proches les voisins sont renvoyés. C'est ce que l'on appelle la perte de rappel. Le niveau de perte de rappel acceptable pour vous dépend du cas d'utilisation, mais dans la plupart des cas, perdre un peu de rappel en échange d'une amélioration des performances de la base de données est un compromis acceptable.

Pour en savoir plus sur les fonctions de distance approximative Spanner, consultez les pages suivantes :

Index vectoriel

Spanner accélère les recherches vectorielles ANN à l'aide d'un index vectoriel spécialisé. Cet index s'appuie sur ScaNN (Scalable Nearest Neighbor), un algorithme de voisin le plus proche très efficace développé par Google Research.

L'index vectoriel utilise une structure arborescente pour partitionner les données et faciliter des recherches plus rapides. Spanner propose des configurations d'arborescences à deux et trois niveaux :

  • Arborescence à deux niveaux: les nœuds feuille (num_leaves) contiennent des groupes de de vecteurs étroitement liés et de leur centroïde correspondant. Le niveau racine se compose des centroïdes de tous les nœuds de feuille.
  • Arborescence à trois niveaux: conceptuelle semblable à une arborescence à deux niveaux, ajout d'une couche de ramification (num_branches), à partir de laquelle le nœud feuille les centroïdes sont ensuite partitionnés pour former le niveau racine (num_leaves).

En outre, vous devez créer votre index vectoriel avec une métrique de distance spécifique. Vous pouvez choisir la métrique de distance la plus adaptée à votre cas d'utilisation en définissant distance_type sur COSINE, DOT_PRODUCT ou EUCLIDEAN.

Pour en savoir plus, consultez Instructions VECTOR INDEX.

Limites

L'index vectoriel Spanner présente les limites suivantes:

  • ALTER VECTOR INDEX n'est pas compatible.

Créer un index vectoriel

Pour optimiser au mieux l'index vectoriel afin d'obtenir un bon rappel et de bonnes performances, nous vous recommandons de créer votre index vectoriel une fois que la plupart des lignes contenant des représentations vectorielles continues ont été écrites dans votre base de données. Vous devrez peut-être également reconstruire régulièrement l'index vectoriel après avoir inséré de nouvelles données. Pour en savoir plus, consultez Reconstruisez l'index vectoriel.

Pour créer un index vectoriel avec une arborescence à deux niveaux et 1 000 nœuds feuilles sur un Table Documents avec une colonne de représentation vectorielle continue DocEmbedding utilisant le cosinus distance:

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(DocEmbedding)
  OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);

Pour créer un index vectoriel avec une arborescence à trois niveaux et 1 000 000 nœuds feuilles:

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(NullableDocEmbedding)
  WHERE NullableDocEmbedding IS NOT NULL
  OPTIONS (distance_type = 'COSINE', tree_depth = 3, num_branches=1000, num_leaves = 1000000);

Si votre colonne de représentations vectorielles continues peut avoir une valeur nulle, vous devez la déclarer avec une Clause WHERE column_name IS NOT NULL:

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(NullableDocEmbedding)
  WHERE NullableDocEmbedding IS NOT NULL
  OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);

Interroger les embeddings vectoriels

Pour interroger un index vectoriel, utilisez l'une des trois fonctions de distance approximative:

  • APPROX_COSINE_DISTANCE
  • APPROX_EUCLIDEAN_DISTANCE
  • APPROX_DOT_PRODUCT

Lorsque vous utilisez les fonctions de distance approximative, certaines restrictions s'appliquent, entre autres : suivantes:

  • Vous devez fournir un indice de requête pour utiliser l'index vectoriel.
  • Vous devez utiliser une expression constante comme argument de la fonction de distance (par exemple, un paramètre ou un littéral).
  • La requête ou sous-requête dans laquelle la fonction de distance approximative est utilisée doit prennent une forme spécifique: la fonction de distance doit être la seule touche ORDER BY, et une limite doit être spécifiée.

Pour obtenir la liste détaillée des limites, reportez-vous au page de référence sur la fonction de distance approximative.

Exemple

Pour rechercher les 100 vecteurs les plus proches de [1.0, 2.0, 3.0]:

SELECT DocId
FROM Documents@{FORCE_INDEX=DocEmbeddingIndex}
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], DocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

Si la colonne de représentations vectorielles continues peut avoir une valeur nulle:

SELECT DocId
FROM Documents@{FORCE_INDEX=DocEmbeddingIndex}
WHERE NullableDocEmbedding IS NOT NULL
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], NullableDocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

Bonnes pratiques

Suivez ces bonnes pratiques pour optimiser vos index vectoriels et améliorer les résultats de vos requêtes.

Ajuster les options de la recherche vectorielle

La valeur optimale de la recherche vectorielle dépend du cas d'utilisation : le vecteur et sur les vecteurs de requête. Vous devrez peut-être effectuer un réglage itératif pour trouver les meilleures valeurs pour votre charge de travail spécifique.

Voici quelques consignes utiles à suivre lorsque vous choisissez des valeurs appropriées :

  • tree_depth (niveau de l'arborescence) : si la table indexée comporte moins de 10 millions de lignes, utilisez 2 comme tree_depth. Sinon, une tree_depth de 3 accepte des tables contenant jusqu'à 10 milliards de lignes.

  • num_leaves: utilise la racine carrée du nombre de lignes dans l'ensemble de données. Une valeur plus élevée peut augmenter le temps de création de l'index vectoriel. Évitez de définir num_leaves sur une valeur supérieure à table_row_count/1000, car cela entraîne des feuilles trop petites et de mauvaises performances.

  • num_leaves_to_search : cette option spécifie le nombre de nœuds de feuilles de l'index à rechercher. Augmenter num_leaves_to_search améliore le rappel, mais aussi augmente la latence et les coûts. Nous vous recommandons d'utiliser un nombre représentant 1% du total nombre de feuilles défini dans l'instruction CREATE VECTOR INDEX comme valeur pour num_leaves_to_search. Si vous utilisez une clause de filtre, augmentez cette valeur pour élargir la recherche.

Si le rappel est acceptable, mais que le coût de l'interrogation est trop élevé, ce qui entraîne un faible nombre maximal de RPS, essayez d'augmenter num_leaves en suivant ces étapes:

  1. Définissez num_leaves sur un multiple k de sa valeur d'origine (par exemple, 2 * sqrt(table_row_count)).
  2. Définissez num_leaves_to_search de sorte qu'il soit identique à la valeur "k" de sa valeur d'origine.
  3. Testez la réduction des num_leaves_to_search pour améliorer le coût et le nombre de RPS tout en maintenant le rappel.

Améliorer le rappel

Il existe plusieurs possibilités d'aggravation du rappel, dont les suivantes:

  • num_leaves_to_search est trop petit: il peut s'avérer plus difficile de trouver les voisins les plus proches pour certains vecteurs de requête. num_leaves_to_search pour rechercher plus de feuilles peut contribuer à améliorer le rappel. Récents les requêtes ont peut-être changé pour contenir davantage de ces vecteurs difficiles.

  • L'index vectoriel doit être reconstruit : la structure arborescente de l'index vectoriel est optimisée pour l'ensemble de données au moment de sa création et devient statique par la suite. Par conséquent, si des vecteurs très différents sont ajoutés après la création de l'index vectoriel initial, la structure arborescente peut être sous-optimale, ce qui entraîne un rappel moins efficace.

Recompiler l'index vectoriel

Pour reconstruire votre index vectoriel sans temps d'arrêt:

  1. Créer un index vectoriel sur la même colonne de représentation vectorielle continue que le vecteur actuel en mettant à jour les paramètres (par exemple, OPTIONS) le cas échéant.
  2. Une fois l'index créé, modifiez l'indice FORCE_INDEX. à pointer vers le nouvel index pour mettre à jour la requête de recherche vectorielle. Cela garantit que la requête utilise le nouvel index vectoriel. (Vous devrez peut-être également réajuster num_leaves_to_search dans votre nouvelle requête).
  3. Supprimez l'index vectoriel obsolète.

Étape suivante