Cette page explique comment trouver les plus proches voisins 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 K-nearest neighbors (KNN) pour trouver les k-vecteurs les plus proches exacts. Toutefois, à mesure que votre ensemble de données augmente, la latence et le coût d'une recherche KNN augmentent également. Vous pouvez utiliser ANN pour trouver les k-plus proches voisins approximatifs avec une latence et des coûts considérablement réduits.
K plus proches voisins approximatifs
Dans une recherche ANN, les k vecteurs renvoyés ne sont pas les k voisins les plus proches, car la recherche ANN calcule des distances approximatives et peut ne pas examiner tous les vecteurs de l'ensemble de données. Il arrive que quelques vecteurs qui ne figurent pas parmi les k-voisins les plus proches soient renvoyés. C'est ce qu'on appelle la perte de couverture. La perte de rappel acceptable 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 en utilisant un index vectoriel spécialisé. Cet index s'appuie sur Scalable Nearest Neighbor (ScaNN) de Google Research, un algorithme de recherche du plus proche voisin très efficace.
L'index vectoriel utilise une structure arborescente pour partitionner les données et faciliter des recherches plus rapides. Spanner propose des configurations d'arborescence à deux et trois niveaux :
- Configuration de l'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 feuilles. - Configuration d'arborescence à trois niveaux : semblable à une arborescence à deux niveaux, mais avec une couche de branche supplémentaire (
num_branches
) à partir de laquelle les centroïdes des nœuds feuilles sont partitionnés pour former le niveau racine (num_leaves
).
Spanner choisit un index pour vous. Toutefois, si vous savez qu'un index spécifique fonctionne mieux, vous pouvez utiliser l'indication FORCE_INDEX
pour choisir l'index vectoriel le plus adapté à votre cas d'utilisation.
Pour en savoir plus, consultez la section Instructions VECTOR INDEX
.
Limites
- Vous ne pouvez pas pré-fractionner les index vectoriels. Pour en savoir plus, consultez Présentation du pré-fractionnement.
Créer un index vectoriel
Pour optimiser le rappel et les performances d'un index vectoriel, nous vous recommandons de :
Créez votre index vectoriel une fois que la plupart des lignes avec des embeddings ont été écrites dans votre base de données. Vous devrez peut-être aussi reconstruire périodiquement l'index vectoriel après avoir inséré de nouvelles données. Pour en savoir plus, consultez Reconstruire l'index vectoriel.
Utilisez la clause
STORING
pour stocker une copie d'une colonne dans l'index vectoriel. Si une valeur de colonne est stockée dans l'index vectoriel, Spanner effectue le filtrage au niveau de la feuille de l'index pour améliorer les performances des requêtes. Nous vous recommandons de stocker une colonne si elle est utilisée dans une condition de filtrage. Pour en savoir plus sur l'utilisation deSTORING
dans un index, consultez Créer un index pour les analyses d'index uniquement.
Lorsque vous créez votre table, la colonne d'intégration doit être un tableau de type de données FLOAT32
(recommandé) ou FLOAT64
, et comporter une annotation vector_length indiquant la dimension des vecteurs.
L'instruction LDD suivante crée une table Documents
avec une colonne d'embedding DocEmbedding
avec une longueur de vecteur :
CREATE TABLE Documents (
DocId INT64 NOT NULL,
...
DocEmbedding ARRAY<FLOAT32>(vector_length=>128),
) PRIMARY KEY (DocId);
Une fois que vous avez rempli votre table Documents
, vous pouvez créer un index vectoriel avec un arbre à deux niveaux et 1 000 nœuds feuilles sur une table Documents
avec une colonne d'intégration DocEmbedding
à l'aide de la distance cosinus :
CREATE VECTOR INDEX DocEmbeddingIndex
ON Documents(DocEmbedding)
STORING (WordCount)
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)
STORING (WordCount)
WHERE NullableDocEmbedding IS NOT NULL
OPTIONS (distance_type = 'COSINE', tree_depth = 3, num_branches=1000, num_leaves = 1000000);
Si votre colonne d'intégration n'est pas marquée comme NOT NULL
dans la définition de table, vous devez la déclarer avec une clause WHERE COLUMN_NAME IS NOT NULL
:
CREATE VECTOR INDEX DocEmbeddingIndex
ON Documents(NullableDocEmbedding)
STORING (WordCount)
WHERE NullableDocEmbedding IS NOT NULL
OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);
Filtrer un index vectoriel
Vous pouvez également créer un index vectoriel filtré pour trouver les éléments les plus similaires de votre base de données qui correspondent à la condition de filtre. Un index vectoriel filtré indexe de manière sélective les lignes qui répondent aux conditions de filtre spécifiées, ce qui améliore les performances de recherche.
Dans l'exemple suivant, la table Documents
comporte une colonne appelée Category
.
Dans notre recherche vectorielle, nous souhaitons indexer la catégorie "Tech". Nous créons donc une colonne générée qui renvoie NULL si la condition de catégorie n'est pas remplie.
CREATE TABLE Documents (
DocId INT64 NOT NULL,
Category STRING(MAX),
NullIfFiltered BOOL AS (IF(Category = 'Tech', TRUE, NULL)) HIDDEN,
DocEmbedding ARRAY<FLOAT32>(vector_length=>128),
) PRIMARY KEY (DocId);
Ensuite, nous créons un index vectoriel avec un filtre. L'index vectoriel TechDocEmbeddingIndex
n'indexe que les documents de la catégorie "Tech".
CREATE VECTOR INDEX TechDocEmbeddingIndex
ON Documents(DocEmbedding)
STORING(NullIfFiltered)
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
OPTIONS (...);
Lorsque Spanner exécute la requête suivante, qui comporte des filtres correspondant à TechDocEmbeddingIndex
, il sélectionne automatiquement TechDocEmbeddingIndex
et l'accélère. La requête ne recherche que les documents de la catégorie "Tech". Vous pouvez également utiliser {@FORCE_INDEX=TechDocEmbeddingIndex}
pour forcer Spanner à utiliser explicitement TechDocEmbeddingIndex
.
SELECT *
FROM Documents
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
ORDER BY APPROX_(....)
LIMIT 10;
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
Les restrictions suivantes s'appliquent lorsque vous utilisez les fonctions de distance approximative :
- La fonction de distance approximative doit calculer la distance entre une colonne d'embedding et une expression constante (par exemple, un paramètre ou un littéral).
- La sortie de la fonction de distance approximative doit être utilisée dans une clause
ORDER BY
comme seule clé de tri, et unLIMIT
doit être spécifié après leORDER BY
. - La requête doit filtrer explicitement les lignes qui ne sont pas indexées. Dans la plupart des cas, cela signifie que la requête doit inclure une clause
WHERE <column_name> IS NOT NULL
qui correspond à la définition de l'index vectoriel, sauf si la colonne est déjà marquée commeNOT NULL
dans la définition de la table.
Pour obtenir la liste détaillée des limites, consultez la page de référence sur la fonction de distance approximative.
Exemples
Pour rechercher les 100 vecteurs les plus proches de [1.0, 2.0, 3.0]
:
SELECT DocId
FROM Documents
WHERE WordCount > 1000
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
WHERE NullableDocEmbedding IS NOT NULL AND WordCount > 1000
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 des requêtes.
Régler les options de recherche vectorielle
La valeur de recherche vectorielle la plus optimale dépend du cas d'utilisation, de l'ensemble de données vectorielles 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 pour choisir les valeurs appropriées :
tree_depth
(niveau de l'arborescence) : si la table indexée comporte moins de 10 millions de lignes, utilisez untree_depth
de2
. Sinon, untree_depth
de3
accepte les tables comportant jusqu'à environ 10 milliards de lignes.num_leaves
: utilisez 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éfinirnum_leaves
sur une valeur supérieure àtable_row_count/1000
, car cela entraînerait 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 une valeur pournum_leaves_to_search
qui correspond à 1 % du nombre total de feuilles défini dans l'instructionCREATE VECTOR INDEX
. Si vous utilisez une clause de filtre, augmentez cette valeur pour élargir la recherche.
Si vous obtenez un 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 suivant ces étapes :
- 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 de sa valeur d'origine. - Essayez de réduire
num_leaves_to_search
pour améliorer le coût et les RPS tout en conservant le rappel.
Améliorer le rappel
Plusieurs raisons peuvent expliquer la dégradation du rappel, y compris les suivantes :
num_leaves_to_search
est trop petit : vous aurez peut-être plus de mal à trouver les voisins les plus proches pour certains vecteurs de requête. Augmenternum_leaves_to_search
pour rechercher davantage de feuilles peut aider à améliorer le rappel. Il est possible que les requêtes récentes aient évolué pour contenir davantage de ces vecteurs complexes.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 la création et reste 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 plus faible.
Recompiler l'index vectoriel
Pour recompiler votre index vectoriel sans temps d'arrêt :
- Créez un index vectoriel sur la même colonne d'embedding que l'index vectoriel actuel, en mettant à jour les paramètres (par exemple,
OPTIONS
) selon vos besoins. - Une fois l'index créé, utilisez l'indication
FORCE_INDEX
pour pointer vers le nouvel index afin de mettre à jour la requête de recherche vectorielle. afin 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.
Étapes suivantes
En savoir plus sur les fonctions GoogleSQL
APPROXIMATE_COSINE_DISTANCE()
,APPROXIMATE_EUCLIDEAN_DISTANCE()
etAPPROXIMATE_DOT_PRODUCT()
En savoir plus sur les instructions
VECTOR INDEX
GoogleSQLConsultez Premiers pas avec Spanner Vector Search pour obtenir un exemple détaillé d'utilisation d'ANN.