La pagina mostra come utilizzare Cloud Firestore per eseguire ricerche vettoriali K-Nearest Neighbor (KNN) utilizzando le seguenti tecniche:
- Memorizza i valori del vettore
- Creare e gestire gli indici di vettori KNN
- Esegui una query K-Nearest Neighbor (KNN) utilizzando una delle misure di distanza vettoriale supportate
Memorizza gli embedding vettoriali
Puoi creare valori vettoriali come gli incorporamenti di testo dai tuoi dati Cloud Firestore e archiviarli nei documenti Cloud Firestore.
Operazione di scrittura con un embedding vettoriale
L'esempio seguente mostra come archiviare un embedding vettoriale in un documento Cloud Firestore:
Python
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]) });
Go
Java
import com.google.cloud.firestore.CollectionReference; import com.google.cloud.firestore.DocumentReference; import com.google.cloud.firestore.FieldValue; import com.google.cloud.firestore.VectorQuery; CollectionReference coll = firestore.collection("coffee-beans"); Map<String, Object> docData = new HashMap<>(); docData.put("name", "Kahawa coffee beans"); docData.put("description", "Information about the Kahawa coffee beans."); docData.put("embedding_field", FieldValue.vector(new double[] {1.0, 2.0, 3.0})); ApiFuture<DocumentReference> future = coll.add(docData); DocumentReference documentReference = future.get();
Calcolo di embedding di vettori con una funzione Cloud
Per calcolare e memorizzare gli embedding vettoriali ogni volta che un documento viene aggiornato o creato, puoi configurare una funzione 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, }); }
Go
// Not yet supported in the Go client library
Java
// Not yet supported in the Java client library
Creare e gestire gli indici di vettori
Prima di poter eseguire una ricerca del risultato più simile con gli incorporamenti vettoriali, devi creare un indice corrispondente. Gli esempi riportati di seguito mostrano come creare e gestire gli indici di vettori con Google Cloud CLI. Gli indici vettoriali possono essere gestiti anche con Firebase CLI e Terraform.
Creare un indice di vettori
Prima di creare un indice di vettori, esegui l'upgrade alla versione più recente di Google Cloud CLI:
gcloud components update
Per creare un indice di vettori, utilizza gcloud firestore indexes composite create
:
gcloud
gcloud firestore indexes composite create \ --collection-group=collection-group \ --query-scope=COLLECTION \ --field-config field-path=vector-field,vector-config='vector-configuration' \ --database=database-id
dove:
- collection-group è l'ID del gruppo di raccolte.
- vector-field è il nome del campo contenente l'embedding vettoriale.
- database-id è l'ID del database.
- vector-configuration include il vettore
dimension
e il tipo di indice.dimension
è un numero intero fino a 2048. Il tipo di indice deve essereflat
. Formatta la configurazione dell'indice come segue:{"dimension":"DIMENSION", "flat": "{}"}
.
L'esempio seguente crea un indice composto, incluso un indice vettoriale per il campo vector-field
e un indice crescente per il campo color
. Puoi utilizzare questo tipo di indice per prefiltrare i dati prima di una ricerca del vicino più prossimo.
gcloud
gcloud firestore indexes composite create \ --collection-group=collection-group \ --query-scope=COLLECTION \ --field-config=order=ASCENDING,field-path="color" \ --field-config field-path=vector-field,vector-config='{"dimension":"1024", "flat": "{}"}' \ --database=database-id
Elenca tutti gli indici di vettore
gcloud
gcloud firestore indexes composite list --database=database-id
Sostituisci database-id con l'ID del database.
Eliminare un indice di vettori
gcloud
gcloud firestore indexes composite delete index-id --database=database-id
dove:
- index-id è l'ID dell'indice da eliminare.
Utilizza
indexes composite list
per recuperare l'ID indice. - database-id è l'ID del database.
Descrivi un indice vettoriale
gcloud
gcloud firestore indexes composite describe index-id --database=database-id
dove:
- index-id è l'ID dell'indice da descrivere. Utilizza o
indexes composite list
per recuperare l'ID indice. - database-id è l'ID del database.
Esegui una query sul vicino più prossimo
Puoi eseguire una ricerca di somiglianza per trovare i vicini più prossimi di un vettore. Le ricerche di similarità richiedono indici vettoriali. Se un indice non esiste, Cloud Firestore suggerisce un indice da creare utilizzando gcloud CLI.
Il seguente esempio trova i 10 vicini più prossimi del vettore di query.
Python
Node.js
import { Firestore, FieldValue, VectorQuery, VectorQuerySnapshot, } from "@google-cloud/firestore"; // Requires a single-field vector index const vectorQuery: VectorQuery = coll.findNearest({ vectorField: 'embedding_field', queryVector: [3.0, 1.0, 2.0], limit: 10, distanceMeasure: 'EUCLIDEAN' }); const vectorQuerySnapshot: VectorQuerySnapshot = await vectorQuery.get();
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery vectorQuery = coll.findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN); ApiFuture<VectorQuerySnapshot> future = vectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get();
Distanze tra vettori
Le query del vicino più vicino supportano le seguenti opzioni per la distanza del vettore:
EUCLIDEAN
: misura la distanza EUCLIDEA tra i vettori. Per saperne di più, consulta Euclidean.COSINE
: confronta i vettori in base all'angolo tra di loro, il che consente di misurare la somiglianza indipendentemente dalla grandezza dei vettori. Consigliamo di utilizzareDOT_PRODUCT
con vettori normalizzati per unità anziché la distanza COSINE, che è matematicamente equivalente con prestazioni migliori. Per scoprire di più, consulta Cosine similarity per scoprire di più.DOT_PRODUCT
: simile aCOSINE
, ma è influenzato dall'intensità dei vettori. Per saperne di più, consulta Prodotto scalare.
Scegli la misura della distanza
A seconda che tutti gli embedding vettoriali siano o meno normalizzati, puoi determinare quale misura della distanza utilizzare per trovare la misura della distanza. Un vettore incorporato normalizzato ha una grandezza (lunghezza) pari esattamente a 1,0.
Inoltre, se conosci la misura della distanza con cui è stato addestrato il modello, utilizzala per calcolare la distanza tra gli embedding dei vettori.
Dati normalizzati
Se hai un set di dati in cui tutti gli embedding vettoriali sono normalizzati, tutte e tre le misure di distanza forniscono gli stessi risultati di ricerca semantica. In sostanza, anche se ogni misura della distanza restituisce un valore diverso, questi valori vengono ordinati nello stesso modo. Quando gli embedding sono normalizzati, DOT_PRODUCT
è in genere la scelta più efficiente dal punto di vista computazionale, ma la differenza è trascurabile nella maggior parte dei casi. Tuttavia, se la tua applicazione è molto sensibile al rendimento, DOT_PRODUCT
potrebbe aiutarti a ottimizzarlo.
Dati non normalizzati
Se hai un set di dati in cui gli incorporamenti vettoriali non sono normalizzati,
non è matematicamente corretto utilizzare DOT_PRODUCT
come misura della distanza
perché il prodotto scalare non misura la distanza. A seconda di come sono stati generati gli embedding e del tipo di ricerca preferito, la misura della distanza COSINE
o EUCLIDEAN
produce risultati di ricerca soggettivamente migliori rispetto alle altre misure della distanza.
Potrebbe essere necessario eseguire esperimenti con COSINE
o EUCLIDEAN
per determinare quale sia la soluzione migliore per il tuo caso d'uso.
Non sai se i dati sono normalizzati o meno
Se non sai con certezza se i tuoi dati sono normalizzati e vuoi utilizzare
DOT_PRODUCT
, ti consigliamo di utilizzare COSINE
.
COSINE
è come DOT_PRODUCT
con la normalizzazione integrata.
La distanza misurata utilizzando COSINE
va da 0
a 2
. Un risultato prossimo a 0
indica che i vettori sono molto simili.
Filtrare i documenti in precedenza
Per prefiltrare i documenti prima di trovare i vicini più vicini, puoi combinare una ricerca di somiglianza con altri operatori di query. I filtri compositi and
e
or
sono supportati. Per ulteriori informazioni sui filtri dei campi supportati, consulta Operatori di query.
Python
Node.js
// Similarity search with pre-filter // Requires composite vector index const preFilteredVectorQuery: VectorQuery = coll .where("color", "==", "red") .findNearest({ vectorField: "embedding_field", queryVector: [3.0, 1.0, 2.0], limit: 5, distanceMeasure: "EUCLIDEAN", }); const vectorQueryResults = await preFilteredVectorQuery.get();
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery preFilteredVectorQuery = coll .whereEqualTo("color", "red") .findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN); ApiFuture<VectorQuerySnapshot> future = preFilteredVectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get();
Recuperare la distanza del vettore calcolata
Puoi recuperare la distanza del vettore calcolata assegnando un
nome della proprietà di output distance_result_field
alla query FindNearest
, come показано показано nell'esempio seguente:
Python
Node.js
const vectorQuery: VectorQuery = coll.findNearest( { vectorField: 'embedding_field', queryVector: [3.0, 1.0, 2.0], limit: 10, distanceMeasure: 'EUCLIDEAN', distanceResultField: 'vector_distance' }); const snapshot: VectorQuerySnapshot = await vectorQuery.get(); snapshot.forEach((doc) => { console.log(doc.id, ' Distance: ', doc.get('vector_distance')); });
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQueryOptions; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery vectorQuery = coll.findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN, VectorQueryOptions.newBuilder().setDistanceResultField("vector_distance").build()); ApiFuture<VectorQuerySnapshot> future = vectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get(); for (DocumentSnapshot document : vectorQuerySnapshot.getDocuments()) { System.out.println(document.getId() + " Distance: " + document.get("vector_distance")); }
Se vuoi utilizzare una maschera di campi per restituire un sottoinsieme di campi del documento insieme a un distanceResultField
, devi includere anche il valore di distanceResultField
nella maschera di campi, come mostrato nell'esempio seguente:
Python
Node.js
const vectorQuery: VectorQuery = coll .select('name', 'description', 'vector_distance') .findNearest({ vectorField: 'embedding_field', queryVector: [3.0, 1.0, 2.0], limit: 10, distanceMeasure: 'EUCLIDEAN', distanceResultField: 'vector_distance' });
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQueryOptions; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery vectorQuery = coll .select("name", "description", "vector_distance") .findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN, VectorQueryOptions.newBuilder() .setDistanceResultField("vector_distance") .build()); ApiFuture<VectorQuerySnapshot> future = vectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get(); for (DocumentSnapshot document : vectorQuerySnapshot.getDocuments()) { System.out.println(document.getId() + " Distance: " + document.get("vector_distance")); }
Specifica una soglia di distanza
Puoi specificare una soglia di somiglianza che restituisce solo i documenti al suo interno. Il comportamento del campo soglia dipende dalla misura della distanza selezionata:
- Le distanze
EUCLIDEAN
eCOSINE
limitano la soglia ai documenti in cui la distanza è minore o uguale alla soglia specificata. Queste misure della distanza diminuiscono man mano che i vettori diventano più simili. - La distanza
DOT_PRODUCT
limita la soglia ai documenti in cui la distanza è maggiore o uguale alla soglia specificata. Le distanze del prodotto scalare aumentano man mano che i vettori diventano più simili.
L'esempio seguente mostra come specificare una soglia di distanza per restituire fino a 10 documenti più vicini che si trovano a una distanza massima di 4,5 unità utilizzando la metrica di distanza EUCLIDEAN
:
Python
Node.js
const vectorQuery: VectorQuery = coll.findNearest({ vectorField: 'embedding_field', queryVector: [3.0, 1.0, 2.0], limit: 10, distanceMeasure: 'EUCLIDEAN', distanceThreshold: 4.5 }); const snapshot: VectorQuerySnapshot = await vectorQuery.get(); snapshot.forEach((doc) => { console.log(doc.id); });
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQueryOptions; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery vectorQuery = coll.findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN, VectorQueryOptions.newBuilder() .setDistanceThreshold(4.5) .build()); ApiFuture<VectorQuerySnapshot> future = vectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get(); for (DocumentSnapshot document : vectorQuerySnapshot.getDocuments()) { System.out.println(document.getId()); }
Limitazioni
Quando utilizzi gli embedding vettoriali, tieni presente le seguenti limitazioni:
- La dimensione massima dell'embedding supportata è 2048. Per archiviare indici più grandi, utilizza la riduzione della dimensionalità.
- Il numero massimo di documenti da restituire da una query di tipo nearest-neighbor è 1000.
- La ricerca vettoriale non supporta gli ascoltatori di snapshot in tempo reale.
- Solo le librerie client Python, Node.js, Go e Java supportano la ricerca di vettori.
Passaggi successivi
- Scopri le best practice per Cloud Firestore.
- Scopri di più sulle letture e sulle scritture su larga scala.