Effectuer une recherche à l'aide de représentations vectorielles continues de vecteurs

Cette page explique comment utiliser Firestore pour effectuer des recherches sur le vecteur KNN (plus proche) à l'aide des techniques suivantes:

  • Stocker les valeurs vectorielles
  • Créer et gérer des index vectoriels KNN
  • Exécuter une requête KNN à l'aide de l'une des fonctions de distance vectorielles acceptées

Stocker des représentations vectorielles continues de vecteurs

Vous pouvez créer des valeurs vectorielles telles que des représentations vectorielles continues de texte à partir de vos données Firestore, et les stocker dans des documents Firestore.

Opération d'écriture avec une représentation vectorielle continue de vecteur

L'exemple suivant montre comment stocker une représentation vectorielle continue de vecteur dans un document Firestore:

Python
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector

collection = firestore_client.collection("coffee-beans")
doc = {
  "name": "Kahawa coffee beans"
  "description": "Information about the Kahawa coffee beans."
  "embedding_field": Vector([1.0 , 2.0, 3.0])
}

collection.add(doc)
    
Node.js
import {
  Firestore,
  FieldValue,
} from "@google-cloud/firestore";

const db = new Firestore();
const coll = db.collection('coffee-beans');
await coll.add({
  name: "Kahawa coffee beans",
  description: "Information about the Kahawa coffee beans.",
  embedding_field: FieldValue.vector([1.0 , 2.0, 3.0])
});
    

Calculer des représentations vectorielles continues de vecteurs avec une fonction Cloud

Pour calculer et stocker des représentations vectorielles continues lorsqu'un document est mis à jour ou créé, vous pouvez configurer une fonction Cloud:

Python
@functions_framework.cloud_event
def store_embedding(cloud_event) -> None:
  """Triggers by a change to a Firestore document.
  """
  firestore_payload = firestore.DocumentEventData()
  payload = firestore_payload._pb.ParseFromString(cloud_event.data)

  collection_id, doc_id = from_payload(payload)
  # Call a function to calculate the embedding
  embedding = calculate_embedding(payload)
  # Update the document
  doc = firestore_client.collection(collection_id).document(doc_id)
  doc.set({"embedding_field": embedding}, merge=True)
    
Node.js
/**
 * A vector embedding will be computed from the
 * value of the `content` field. The vector value
 * will be stored in the `embedding` field. The
 * field names `content` and `embedding` are arbitrary
 * field names chosen for this example.
 */
async function storeEmbedding(event: FirestoreEvent<any>): Promise<void> {
  // Get the previous value of the document's `content` field.
  const previousDocumentSnapshot = event.data.before as QueryDocumentSnapshot;
  const previousContent = previousDocumentSnapshot.get("content");

  // Get the current value of the document's `content` field.
  const currentDocumentSnapshot = event.data.after as QueryDocumentSnapshot;
  const currentContent = currentDocumentSnapshot.get("content");

  // Don't update the embedding if the content field did not change
  if (previousContent === currentContent) {
    return;
  }

  // Call a function to calculate the embedding for the value
  // of the `content` field.
  const embeddingVector = calculateEmbedding(currentContent);

  // Update the `embedding` field on the document.
  await currentDocumentSnapshot.ref.update({
    embedding: embeddingVector,
  });
}
    

Créer et gérer des index vectoriels

Avant de pouvoir effectuer une recherche des plus proches voisins avec vos représentations vectorielles continues, vous devez créer un index correspondant. Les exemples suivants montrent comment créer et gérer des index vectoriels.

Créer un index vectoriel à champ unique

Pour créer un index vectoriel à champ unique, utilisez gcloud alpha firestore indexes composite create:

gcloud
gcloud alpha firestore indexes composite create \
--collection-group=collection-group \
--query-scope=COLLECTION \
--field-config field-path=vector-field,vector-config='vector-configuration' \
--database=database-id
    

où :

  • collection-group est l'ID du groupe de collections.
  • vector-field est le nom du champ contenant la représentation vectorielle continue de vecteur.
  • database-id est l'ID de la base de données.
  • vector-configuration inclut le vecteur dimension et le type d'index. dimension est un entier inférieur ou égal à 2 048. Le type d'index doit être flat. Mettez en forme la configuration d'index comme suit: {"dimension":"DIMENSION", "flat": "{}"}.

Créer un index vectoriel composite

L'exemple suivant crée un index vectoriel composite pour le champ color et un champ de représentation vectorielle continue de vecteur.

gcloud
gcloud alpha firestore indexes composite create \
--collection-group=collection-group \
--query-scope=COLLECTION \
--field-config=order=ASCENDING,field-path="color" \
--field-config field-path=field,vector-config='{"dimension":"1024", "flat": "{}"}' \
--database=database-id
    

Répertorier tous les index vectoriels

gcloud
gcloud alpha firestore indexes composite list --database=database-id

Remplacez database-id par l'ID de la base de données.

Supprimer un index vectoriel

gcloud
gcloud alpha firestore indexes composite delete index-id --database=database-id
    

où :

  • index-id est l'ID de l'index à supprimer. Utilisez indexes composite list pour récupérer l'ID d'index.
  • database-id est l'ID de la base de données.

Décrire un index vectoriel

gcloud
gcloud alpha firestore indexes composite describe index-id --database=database-id
    

où :

  • index-id est l'ID de l'index à décrire. Utilisez ou indexes composite list pour récupérer l'ID d'index.
  • database-id est l'ID de la base de données.

Créer une requête de voisinage le plus proche

Vous pouvez effectuer une recherche de similarité pour trouver les voisins les plus proches d'une représentation vectorielle continue. Les recherches de similarité nécessitent des index vectoriels. S'il n'existe aucun index, Firestore suggère un index à créer à l'aide de la gcloud CLI.

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure

collection = collection("coffee-beans")

// Requires vector index
collection.find_nearest(
   vector_field="embedding_field",
   query_vector=Vector([3.0, 1.0, 2.0]),
   distance_measure=DistanceMeasure.EUCLIDEAN,
   limit=5)
    
Node.js
import {
  Firestore,
  FieldValue,
  VectorQuery,
  VectorQuerySnapshot,
} from "@google-cloud/firestore";

// Requires single-field vector index
const vectorQuery: VectorQuery = coll.findNearest('embedding_field', FieldValue.vector([3.0, 1.0, 2.0]), {
  limit: 5,
  distanceMeasure: 'EUCLIDEAN'
});

const vectorQuerySnapshot: VectorQuerySnapshot = await vectorQuery.get();
    

Distances des vecteurs

Les requêtes les plus proches acceptent les options suivantes pour la distance des vecteurs:

  • EUCLIDEAN: mesure la distance EUCLIDEAN entre les vecteurs. Pour en savoir plus, consultez la page Euclidienne.
  • COSINE: compare les vecteurs en fonction de l'angle entre eux, ce qui vous permet de mesurer la similarité qui n'est pas basée sur la magnitude des vecteurs. Nous vous recommandons d'utiliser DOT_PRODUCT avec des vecteurs normalisés unitaires plutôt que la distance COSINE, ce qui est mathématiquement équivalent avec de meilleures performances. Pour en savoir plus, consultez la section Similarité cosinus.
  • DOT_PRODUCT: semblable à COSINE, mais affecté par l'amplitude des vecteurs. Pour en savoir plus, consultez Produit Dot.

Préfiltrer les données

Pour préfiltrer les données avant de trouver les voisins les plus proches, vous pouvez combiner une recherche de similarité avec d'autres filtres, à l'exception des filtres d'inégalité. Les filtres composites and et or sont acceptés. Pour les filtres de champ, les filtres suivants sont acceptés:

  • == equal to (égal à)
  • in
  • array_contains
  • array_contains_any
Python
// Similarity search with pre-filter
// Requires composite vector index
collection.where("color", "==", "red").find_nearest(
   vector_field="embedding_field",
   query_vector=Vector([3.0, 1.0, 2.0]),
   distance_measure=DistanceMeasure.EUCLIDEAN,
   limit=5)
    
Node.js
// Similarity search with pre-filter
// Requires composite vector index
const preFilteredVectorQuery: VectorQuery = coll
  .where("color", "==", "red")
  .findNearest("embedding_field", FieldValue.vector([3.0, 1.0, 2.0]), {
    limit: 5,
    distanceMeasure: "EUCLIDEAN",
  });

vectorQueryResults = await preFilteredVectorQuery.get();
    

Limites

Lorsque vous travaillez avec des représentations vectorielles continues, tenez compte des limites suivantes:

  • La dimension de représentation vectorielle continue maximale acceptée est 2 048. Pour stocker des index plus importants, utilisez la réduction de dimensionnalité.
  • Le nombre maximal de documents à renvoyer à partir d'une requête du plus proche voisin est de 1 000.
  • La recherche vectorielle n'est pas compatible avec les écouteurs d'instantanés en temps réel.
  • Vous ne pouvez pas utiliser de filtres d'inégalité pour préfiltrer les données.
  • Seules les bibliothèques clientes Python et Node.js sont compatibles avec la recherche vectorielle.

Étapes suivantes