Effectuer une recherche vectorielle de similarité dans Spanner en trouvant les K voisins les plus proches

Cette page explique comment effectuer une recherche de vecteur de similarité dans Spanner à l'aide de la distance de cosinus, de la distance euclidienne et des fonctions de vecteur de produit scalaire pour trouver les voisins les plus proches de K. Avant de lire cette page, il est important que vous compreniez les concepts suivants:

  • Distance euclidienne : mesure la distance la plus courte entre deux vecteurs.
  • Distance cosinus : mesure le cosinus de l'angle entre deux vecteurs.
  • Produit scalaire: calcule le cosinus de l'angle multiplié par le produit des magnitudes de vecteur correspondantes. Si vous savez que toutes les représentations vectorielles continues de votre ensemble de données sont normalisées, vous pouvez utiliser DOT_PRODUCT() comme fonction de distance.
  • KNN (plus proche) voisins (KNN) : algorithme de machine learning supervisé utilisé pour résoudre les problèmes de classification ou de régression.

Vous pouvez utiliser les fonctions de distance vectorielle pour effectuer une recherche vectorielle de KNN (pour des cas d'utilisation tels que la recherche de similarité ou la génération augmentée de récupération). Spanner accepte les fonctions COSINE_DISTANCE(), EUCLIDEAN_DISTANCE() et DOT_PRODUCT(), qui opèrent sur des représentations vectorielles continues de vecteurs, ce qui vous permet de trouver le KNN de la représentation vectorielle continue de l'entrée.

Par exemple, après avoir généré et enregistré vos données Spanner opérationnelles sous forme de représentations vectorielles continues, vous pouvez fournir ces représentations vectorielles continues en tant que paramètre d'entrée dans votre requête pour trouver les vecteurs les plus proches dans l'espace à N dimensions, afin de rechercher des éléments sémantiquement similaires ou associés.

Les trois fonctions de distance utilisent les arguments vector1 et vector2, qui sont de type array<>, et doivent avoir les mêmes dimensions et avoir la même longueur. Pour en savoir plus sur ces fonctions, consultez les pages suivantes:

Examples

Les exemples suivants illustrent la recherche KNN, la recherche KNN sur des données partitionnées et l'utilisation d'un index secondaire avec KNN.

Tous les exemples utilisent EUCLIDEAN_DISTANCE(). Vous pouvez également utiliser COSINE_DISTANCE(). De plus, si toutes les représentations vectorielles continues de votre ensemble de données sont normalisées, vous pouvez utiliser DOT_PRODUCT() comme fonction de distance.

Prenons l'exemple d'une table Documents comportant une colonne (DocEmbedding) de représentations vectorielles continues de texte précalculées provenant de la colonne d'octets DocContents.

GoogleSQL

CREATE TABLE Documents (
UserId       INT64 NOT NULL,
DocId        INT64 NOT NULL,
Author       STRING(1024),
DocContents  BYTES,
DocEmbedding ARRAY<FLOAT32>
) PRIMARY KEY (UserId, DocId);

PostgreSQL

CREATE TABLE Documents (
UserId       bigint primary key,
DocId        bigint primary key,
Author       varchar(1024),
DocContents  bytea,
DocEmbedding float4[]
);

En supposant qu'une représentation vectorielle continue d'entrée pour "baseball, mais pas baseball professionnelle" correspond au tableau [0.3, 0.3, 0.7, 0.7], vous pouvez trouver les cinq documents les plus proches correspondants à l'aide de la requête suivante:

GoogleSQL

SELECT DocId, DocEmbedding FROM Documents
ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
ARRAY<FLOAT32>[0.3, 0.3, 0.7, 0.8])
LIMIT 5;

PostgreSQL

SELECT DocId, DocEmbedding FROM Documents
ORDER BY spanner.euclidean_distance(DocEmbedding,
'{0.3, 0.3, 0.7, 0.8}'::float4[])
LIMIT 5;

Voici les résultats attendus de cet exemple:

Documents
+---------------------------+-----------------+
| DocId                     | DocEmbedding    |
+---------------------------+-----------------+
| 24                        | [8, ...]        |
+---------------------------+-----------------+
| 25                        | [6, ...]        |
+---------------------------+-----------------+
| 26                        | [3.2, ...]      |
+---------------------------+-----------------+
| 27                        | [38, ...]       |
+---------------------------+-----------------+
| 14229                     | [1.6, ...]      |
+---------------------------+-----------------+

Exemple 2: Recherche KNN sur des données partitionnées

La requête de l'exemple précédent peut être modifiée en ajoutant des conditions à la clause WHERE afin de limiter la recherche vectorielle à un sous-ensemble de vos données. Une application courante consiste à effectuer une recherche dans des données partitionnées, telles que des lignes appartenant à un UserId spécifique.

GoogleSQL

SELECT UserId, DocId, DocEmbedding FROM Documents
WHERE UserId=18
ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
ARRAY<FLOAT32>[0.3, 0.3, 0.7, 0.8])
LIMIT 5;

PostgreSQL

SELECT UserId, DocId, DocEmbedding FROM Documents
WHERE UserId=18
ORDER BY spanner.euclidean_distance(DocEmbedding,
'{0.3, 0.3, 0.7, 0.8}'::float4[])
LIMIT 5;

Voici les résultats attendus de cet exemple:

Documents
+-----------+-----------------+-----------------+
| UserId    | DocId           | DocEmbedding    |
+-----------+-----------------+-----------------+
| 18        | 234             | [12, ...]       |
+-----------+-----------------+-----------------+
| 18        | 12              | [1.6, ...]      |
+-----------+-----------------+-----------------+
| 18        | 321             | [22, ...]       |
+-----------+-----------------+-----------------+
| 18        | 432             | [3, ...]        |
+-----------+-----------------+-----------------+

Exemple 3: Recherche KNN sur des plages d'index secondaires

Si le filtre de clause WHERE que vous utilisez ne fait pas partie de la clé primaire de la table, vous pouvez créer un index secondaire pour accélérer l'opération avec une analyse d'index uniquement.

GoogleSQL

CREATE INDEX DocsByAuthor
ON Documents(Author)
STORING (DocEmbedding);

SELECT Author, DocId, DocEmbedding FROM Documents
WHERE Author="Mark Twain"
ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
   <embeddings for "book about the time traveling American">)
LIMIT 5;

PostgreSQL

CREATE INDEX DocsByAuthor
ON Documents(Author)
INCLUDE (DocEmbedding);

SELECT Author, DocId, DocEmbedding FROM Documents
WHERE Author="Mark Twain"
ORDER BY spanner.euclidean_distance(DocEmbedding,
   <embeddings for "that book about the time traveling American">)
LIMIT 5;

Voici les résultats attendus de cet exemple:

Documents
+------------+-----------------+-----------------+
| Author     | DocId           | DocEmbedding    |
+------------+-----------------+-----------------+
| Mark Twain | 234             | [12, ...]       |
+------------+-----------------+-----------------+
| Mark Twain | 12              | [1.6, ...]      |
+------------+-----------------+-----------------+
| Mark Twain | 321             | [22, ...]       |
+------------+-----------------+-----------------+
| Mark Twain | 432             | [3, ...]        |
+------------+-----------------+-----------------+
| Mark Twain | 375             | [9, ...]        |
+------------+-----------------+-----------------+

Étapes suivantes