Eseguire ricerche con vector embedding
Questa pagina mostra come utilizzare Firestore per eseguire ricerche vettoriali del vicino più prossimo (KNN) utilizzando le seguenti tecniche:
- Archiviare i valori dei vettori
- Creare e gestire gli indici vettoriali KNN
- Eseguire una query del vicino più prossimo (KNN) utilizzando una delle misure di distanza vettoriale supportate
Prima di iniziare
Prima di archiviare gli embedding in Firestore, devi generare vector embedding. Firestore non genera gli embedding. Puoi utilizzare un servizio come Vertex AI per creare valori vettoriali, ad esempio text embedding dai dati di Firestore. Puoi quindi archiviare questi embedding nei documenti di Firestore.
Per saperne di più sugli embedding, vedi Che cosa sono gli embedding?
Per scoprire come ottenere text embedding con Vertex AI, vedi Ottenere text embedding.
Archiviare vector embedding
Gli esempi seguenti mostrano come archiviare vector embedding in Firestore.
Operazione di scrittura con un vector embedding
L'esempio seguente mostra come archiviare un vector embedding in un documento di 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();
Calcolare vector embedding con una Cloud Function
Per calcolare e archiviare vector embedding ogni volta che un documento viene aggiornato o creato, puoi configurare una Cloud Run Function:
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 vicino più prossimo con i vector embedding, devi creare un indice corrispondente. Gli esempi seguenti mostrano come creare e gestire gli indici vettoriali con Google Cloud CLI e la console. Puoi anche gestire gli indici vettoriali con l'interfaccia a riga di comando di Firebase e Terraform.
Creare un indice vettoriale
Google Cloud Console
Per creare manualmente un nuovo indice dalla Google Cloud console:
- Nella Google Cloud console, vai alla pagina Database.
- Seleziona il database richiesto dall'elenco dei database.
- Nel menu di navigazione, fai clic su Indici e poi sulla scheda Manuale.
- Fai clic su Crea indice.
Per indicizzare un campo vettoriale per le ricerche vettoriali, seleziona Crea indice vettoriale.
-
Inserisci un ID raccolta. Inserisci un percorso del campo vettoriale e il numero di dimensioni del vector embedding. Aggiungi i nomi di tutti i campi aggiuntivi che vuoi indicizzare e una modalità di indice per ogni campo.
Fai clic su Salva indice.
Il nuovo indice verrà visualizzato nell'elenco degli indici manuali e Firestore inizierà a crearlo. Al termine della creazione dell'indice, vedrai un segno di spunta verde accanto all'indice.
gcloud
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 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 il vector embedding.
- database-id è l'ID del database.
- vector-configuration include il vettore
dimensione il tipo di indice. Ladimensionè 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 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
Elencare tutti gli indici vettoriali
Google Cloud Console
- Nella Google Cloud console, vai alla pagina Database.
- Seleziona il database richiesto dall'elenco dei database.
-
Nel menu di navigazione, fai clic su Indici e poi sulla scheda Manuale.
La tabella degli indici elenca tutti gli indici del database. Gli indici vettoriali includono un campo vettoriale con un'icona .
gcloud
Per elencare tutti gli indici e recuperare gli ID indice:
gcloud firestore indexes composite list --database=database-id
Sostituisci database-id con l'ID del database.
Puoi utilizzare l'ID indice per visualizzare ulteriori dettagli su un indice:
gcloud firestore indexes composite describe index-id --database=database-id
dove:
- index-id è l'ID dell'indice da descrivere.
- database-id è l'ID del database.
Eliminare un indice vettoriale
Google Cloud Console
- Nella Google Cloud console, vai alla pagina Database.
- Seleziona il database richiesto dall'elenco dei database.
-
Nel menu di navigazione, fai clic su Indici e poi sulla scheda Manuale.
- Nell'elenco degli indici manuali, fai clic sul pulsante Altro per l'indice che vuoi eliminare. Fai clic su Elimina.
- Conferma di voler eliminare questo indice facendo clic su Elimina indice nell'avviso.
gcloud
gcloud firestore indexes composite delete index-id --database=database-id
dove:
- index-id è l'ID dell'indice da eliminare.
Utilizza
indexes composite listper recuperare l'ID indice. - database-id è l'ID del database.
Eseguire una query del vicino più prossimo
Puoi eseguire una ricerca di somiglianza per trovare i vicini più prossimi di un vector embedding. Le ricerche di somiglianza richiedono indici vettoriali. Se non esiste un indice, Firestore suggerisce di crearne uno utilizzando gcloud CLI.
L'esempio seguente 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 vettoriali
Le query del vicino più prossimo supportano le seguenti opzioni per la distanza vettoriale:
EUCLIDEAN: misura la distanzaEUCLIDEANtra i vettori. Per saperne di più, vedi Distanza euclidea.COSINE: confronta i vettori in base all'angolo tra loro, il che ti consente di misurare la somiglianza non basata sulla grandezza dei vettori. Ti consigliamo di utilizzareDOT_PRODUCTcon vettori normalizzati unitari anziché la distanza COSINE, che è matematicamente equivalente con prestazioni migliori. Per saperne di più, vedi Somiglianza del coseno.DOT_PRODUCT: simile aCOSINE, ma è influenzato dalla grandezza dei vettori. Per saperne di più, vedi Prodotto scalare.
Scegliere la misura di distanza
A seconda che tutti i vector embedding siano normalizzati o meno, puoi determinare la misura di distanza da utilizzare per trovare la misura di distanza. Un vector embedding normalizzato ha una grandezza (lunghezza) pari esattamente a 1,0.
Inoltre, se conosci la misura di distanza con cui è stato addestrato il modello, utilizzala per calcolare la distanza tra i vector embedding.
Dati normalizzati
Se hai un set di dati in cui tutti i vector embedding 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 nello stesso modo. Quando gli embedding 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 potrebbe aiutarti a ottimizzare il rendimento.
Dati non normalizzati
Se hai un set di dati in cui i vector embedding 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 embedding 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 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 invece COSINE.
COSINE è simile a 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 di somiglianza con altri operatori di query. Sono supportati i filtri composti and e or. Per ulteriori informazioni sui filtri di campo supportati, vedi 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 vettoriale 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')); });
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 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' });
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")); }
Specificare una soglia di distanza
Puoi specificare una soglia di somiglianza che restituisce solo i documenti all'interno della soglia. Il comportamento del campo soglia dipende dalla misura di distanza scelta:
- Le distanze
EUCLIDEANeCOSINElimitano 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. - La distanza
DOT_PRODUCTlimita 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 distano al massimo 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 lavori con i vector embedding, tieni presente le seguenti limitazioni:
- La dimensione massima supportata per l'embedding è 2048. Per archiviare indici più grandi, utilizza la riduzione della dimensionalità.
- Il numero massimo di documenti da restituire da una query del vicino più prossimo è 1000 (solo per l'edizione Standard).
- La ricerca vettoriale non supporta i listener 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.
- Scopri di più su letture e scritture su larga scala.