Cette page explique comment trouver des voisins les plus proches approximatifs (ANN), créer des index vectoriels et interroger des embeddings vectoriels à l'aide des fonctions de distance ANN suivantes dans Spanner:
APPROX_COSINE_DISTANCE
APPROX_EUCLIDEAN_DISTANCE
APPROX_DOT_PRODUCT
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 k vecteurs renvoyés ne sont pas les vrais k voisins les plus proches. Parfois, quelques vecteurs qui ne font pas partie des k voisins les plus proches sont renvoyés. C'est ce qu'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:
APPROX_COSINE_DISTANCE
dans GoogleSQLAPPROX_EUCLIDEAN_DISTANCE
dans GoogleSQLAPPROX_DOT_PRODUCT
dans GoogleSQL
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 recherche du voisin le plus proche très efficace développé par Google Research.
L'index vectoriel utilise une structure basée sur un arbre pour partitionner les données et faciliter les recherches plus rapides. Spanner propose des configurations d'arborescences à deux et trois niveaux:
- Configuration d'arborescence à deux niveaux: les nœuds feuilles (
num_leaves
) contiennent des groupes de vecteurs étroitement liés, ainsi que leur centroïde correspondant. Le niveau racine se compose des centroïdes de tous les nœuds de feuille. - Configuration d'arborescence à trois niveaux: concept semblable à celui d'une arborescence à deux niveaux, mais avec l'introduction d'une couche de branches supplémentaire (
num_branches
), à partir de laquelle les centroïdes de nœuds de feuille sont partitionnés pour former le niveau racine (num_leaves
).
De plus, 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 la section Instructions VECTOR INDEX
.
Limites
L'indice 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 la section Réorganiser l'index vectoriel.
Pour créer un indice vectoriel avec un arbre à deux niveaux et 1 000 nœuds de feuille sur une table Documents
avec une colonne d'embedding DocEmbedding
à l'aide de la distance cosinus:
CREATE VECTOR INDEX DocEmbeddingIndex
ON Documents(DocEmbedding)
OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);
Pour créer un index vectoriel avec un arbre à trois niveaux et 1 000 000 de 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 d'imbrication est nullable, 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 indice vectoriel, utilisez l'une des trois fonctions de distance approximative:
APPROX_COSINE_DISTANCE
APPROX_EUCLIDEAN_DISTANCE
APPROX_DOT_PRODUCT
Les restrictions suivantes s'appliquent lorsque vous utilisez les fonctions de distance approximative:
- 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 la sous-requête dans laquelle la fonction de distance approximative est utilisée doit prendre une forme spécifique: la fonction de distance doit être la seule clé
ORDER BY
, et une limite doit être spécifiée.
Pour obtenir une liste détaillée des limites, consultez la page de référence de 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 d'embedding 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.
Régler les options de recherche vectorielle
La valeur de recherche vectorielle la plus optimale dépend du cas d'utilisation, du jeu de données vectoriel et des 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, utilisez2
commetree_depth
. Sinon, unetree_depth
de3
accepte des tables pouvant contenir jusqu'à environ 10 milliards de lignes.num_leaves
: utilisez la racine carrée du nombre de lignes de l'ensemble de données. Une valeur plus élevée peut augmenter le temps de création de l'index vectoriel. Évitez de définirnum_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 feuilles de l'index à rechercher. L'augmentation denum_leaves_to_search
améliore le rappel, mais augmente également la latence et les coûts. Nous vous recommandons d'utiliser un nombre correspondant à 1% du nombre total de feuilles définies dans l'instructionCREATE VECTOR INDEX
comme valeur pournum_leaves_to_search
. Si vous utilisez une clause de filtre, augmentez cette valeur pour élargir la recherche.
Si vous obtenez un taux de rappel acceptable, mais que le coût des requêtes est trop élevé, ce qui entraîne un faible nombre maximal de requêtes par seconde, essayez d'augmenter num_leaves
en procédant comme suit:
- Définissez
num_leaves
sur un multiple k de sa valeur d'origine (par exemple,2 * sqrt(table_row_count)
). - Définissez
num_leaves_to_search
sur le même multiple k que sa valeur d'origine. - Essayez de réduire
num_leaves_to_search
pour améliorer les coûts et les RPS tout en conservant le rappel.
Améliorer la mémorisation
Plusieurs facteurs peuvent entraîner une dégradation de la mémoire, y compris les suivants:
num_leaves_to_search
est trop faible: vous pouvez avoir du mal à trouver les voisins les plus proches de certains vecteurs de requête. Par conséquent, augmenternum_leaves_to_search
pour rechercher plus de feuilles peut aider à améliorer le rappel. Les requêtes récentes peuvent avoir évolué 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 recompiler votre index vectoriel sans temps d'arrêt:
- Créez un nouvel index vectoriel sur la même colonne d'embedding que l'index vectoriel actuel, en mettant à jour les paramètres (par exemple,
OPTIONS
) si nécessaire. - Une fois la création de l'index terminée, modifiez l'indice
FORCE_INDEX
pour qu'il pointe vers le nouvel index afin de mettre à jour la requête de recherche vectorielle. Cela garantit que la requête utilise le nouvel index de vecteur. (Vous devrez peut-être également réajusternum_leaves_to_search
dans votre nouvelle requête.) - Supprimez l'index vectoriel obsolète.
Étape suivante
En savoir plus sur les fonctions
APPROXIMATE_COSINE_DISTANCE()
,APPROXIMATE_EUCLIDEAN_DISTANCE()
etAPPROXIMATE_DOT_PRODUCT()
de GoogleSQLEn savoir plus sur les instructions
VECTOR INDEX
GoogleSQL