Cercare con gli incorporamenti vettoriali
La pagina mostra come utilizzare Firestore per eseguire ricerche vettoriali K-Nearest Neighbor (KNN) utilizzando le seguenti tecniche:
- Memorizza i valori dei vettori
- Creare e gestire indici vettoriali KNN
- Esegui una query K-Nearest Neighbor (KNN) utilizzando una delle misure di distanza vettoriale supportate.
Memorizzare gli incorporamenti vettoriali
Puoi creare valori vettoriali come incorporamenti di testo dai tuoi dati Firestore e archiviarli nei documenti Firestore.
Operazione di scrittura con un vector embedding
L'esempio riportato di seguito mostra come archiviare un embedding vettoriale in un documento 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]) });
Vai
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();
Calcola gli incorporamenti vettoriali con una funzione Cloud Functions
Per calcolare e archiviare gli incorporamenti vettoriali ogni volta che un documento viene aggiornato o creato, puoi configurare una funzione Cloud Run:
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 indici vettoriali
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 vettoriali con Google Cloud CLI. Gli indici vettoriali possono anche essere gestiti con l'interfaccia a riga di comando di Firebase e Terraform.
Crea un indice vettoriale
Prima di creare un indice vettoriale, esegui l'upgrade all'ultima versione di Google Cloud CLI:
gcloud components update
Per creare un indice vettoriale, 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 che contiene l'incorporamento 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 nel seguente modo:{"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
Elenco di tutti gli indici vettoriali
gcloud
gcloud firestore indexes composite list --database=database-id
Sostituisci database-id con l'ID del database.
Eliminare un indice vettoriale
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.
Descrivere 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.
Eseguire una query del vicino più prossimo
Puoi eseguire una ricerca di similarità per trovare i vicini più prossimi di un incorporamento vettoriale. Le ricerche per similarità richiedono indici vettoriali. Se un indice non esiste, Firestore suggerisce di crearne uno utilizzando lagcloud CLId.
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();
Vai
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 vettoriali
Le query di ricerca dei vicini più prossimi supportano le seguenti opzioni per la distanza vettoriale:
EUCLIDEAN
: misura la distanza EUCLIDEA tra i vettori. Per saperne di più, vedi Euclideo.COSINE
: confronta i vettori in base all'angolo tra loro, il che ti consente di misurare la somiglianza non basata sulla grandezza dei vettori. Consigliamo di utilizzareDOT_PRODUCT
con vettori normalizzati unitari anziché la distanza COSINE, che è matematicamente equivalente con prestazioni migliori. Per saperne di più, consulta Similarità del coseno.DOT_PRODUCT
: simile aCOSINE
, ma influenzato dalla grandezza dei vettori. Per saperne di più, consulta Prodotto scalare.
Scegli la misura della distanza
A seconda che tutti gli incorporamenti vettoriali siano normalizzati o meno, puoi determinare quale misura di distanza utilizzare per trovare la misura di distanza. Un incorporamento vettoriale normalizzato ha una magnitudine (lunghezza) pari a 1.0.
Inoltre, se sai con quale misura di distanza è 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 incorporamenti vettoriali sono normalizzati, tutte e tre le misure di distanza forniscono gli stessi risultati di ricerca semantica. In sostanza, anche se ogni
misura di distanza restituisce un valore diverso, questi valori vengono ordinati allo stesso modo. Quando
gli incorporamenti vengono normalizzati, DOT_PRODUCT
è in genere il 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
può aiutarti a
ottimizzare il rendimento.
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 di distanza
perché il prodotto scalare non misura la distanza. A seconda
di come sono stati generati gli incorporamenti e del tipo di ricerca preferito,
la misura di distanza COSINE
o EUCLIDEAN
produce
risultati di ricerca soggettivamente migliori rispetto alle altre misure di distanza.
Potrebbe essere necessario sperimentare 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 non normalizzati
Se non sai 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
varia da 0
a 2
. Un risultato
vicino a 0
indica che i vettori sono molto simili.
Prefiltrare i documenti
Per prefiltrare i documenti prima di trovare i vicini più prossimi, puoi combinare una
ricerca per similarità con altri operatori di query. Sono supportati i filtri compositi and
e or
. 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();
Vai
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 vettoriale calcolata assegnando un nome di proprietà di output distance_result_field
alla query FindNearest
, come mostrato 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')); });
Vai
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 campo per restituire un sottoinsieme di campi del documento insieme a un distanceResultField
, devi includere anche il valore di distanceResultField
nella maschera di campo, 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' });
Vai
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 similarità che restituisce solo i documenti all'interno della soglia. Il comportamento del campo Soglia dipende dalla misura della distanza che scegli:
- Le distanze
EUCLIDEAN
eCOSINE
limitano la soglia ai documenti in cui la distanza è minore o uguale alla soglia specificata. Queste misure di distanza diminuiscono man mano che i vettori diventano più simili. DOT_PRODUCT
La distanza 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.
Il seguente esempio 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); });
Vai
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 supportata per l'incorporamento è 2048. Per archiviare indici più grandi, utilizza la riduzione della dimensionalità.
- Il numero massimo di documenti da restituire da una query di ricerca dei vicini più prossimi è 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 vettoriale.
Passaggi successivi
- Leggi le best practice per Firestore.
- Comprendere le letture e le scritture su larga scala.