Questa pagina mostra come utilizzare Cloud Firestore per eseguire ricerche vettoriali K-Nearest Neighbor (KNN) utilizzando le seguenti tecniche:
- Archivia i valori dei vettori
- Crea e gestisci gli indici vettoriali KNN
- Esegui una query K-Nearest Neighbor (KNN) utilizzando una delle misure di distanza vettoriale supportate
Prima di iniziare
Prima di archiviare gli embedding in Cloud Firestore, devi generare vector embedding. Cloud Firestore non genera gli embedding. Puoi utilizzare un servizio come Agent Platform per creare valori vettoriali, ad esempio text embedding dai tuoi dati Cloud Firestore. Puoi quindi archiviare questi embedding nei documenti Cloud Firestore.
Per saperne di più sugli embedding, consulta Cosa sono gli embedding?
Per scoprire come ottenere text embedding con Agent Platform, consulta Ottieni text embedding.
Archivia i vector embedding
I seguenti esempi mostrano come archiviare i vector embedding in Cloud Firestore.
Operazione di scrittura con un vector embedding
L'esempio seguente mostra come archiviare un vector embedding 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();
Calcola i vector embedding con una funzione Cloud Functions
Per calcolare e archiviare i vector embedding ogni volta che un documento viene aggiornato o creato, puoi configurare una funzione Cloud Functions:
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
Crea e gestisci gli 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.
Crea un indice vettoriale
Console Google Cloud
Per creare manualmente un nuovo indice dalla console Google Cloud:
- Nella console Google Cloud, vai alla pagina Database.
- Seleziona il database richiesto dall'elenco dei database.
- Nel menu di navigazione, fai clic su Indici, quindi 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 di vector embedding. Aggiungi i nomi di eventuali campi aggiuntivi che vuoi indicizzare e una modalità di indicizzazione per ogni campo.
Fai clic su Salva indice.
Il nuovo indice verrà visualizzato nell'elenco degli indici manuali e Cloud Firestore inizierà a crearlo. Al termine della creazione dell'indice, accanto a quest'ultimo verrà visualizzato un segno di spunta verde.
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.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, che include 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 dei vicini più prossimi.
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 vettoriali
Console Google Cloud
- Nella console Google Cloud, vai alla pagina Database.
- Seleziona il database richiesto dall'elenco dei database.
-
Nel menu di navigazione, fai clic su Indici, quindi 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.
Elimina un indice vettoriale
Console Google Cloud
- Nella console Google Cloud, vai alla pagina Database.
- Seleziona il database richiesto dall'elenco dei database.
-
Nel menu di navigazione, fai clic su Indici, quindi 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.
Esegui una query sui vicini più prossimi
Puoi eseguire una ricerca di somiglianza per trovare i vicini più prossimi di un vector embedding. Le ricerche di somiglianze richiedono indici vettoriali. Se non esiste un indice, Cloud Firestore suggerisce un indice da creare 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 tra vettori
Le query del vicino più prossimo supportano le seguenti opzioni per la distanza vettoriale:
EUCLIDEAN: misura la distanzaEUCLIDEANtra i vettori. Per saperne di più, consulta 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 per unità anziché la distanza COSINE, che è matematicamente equivalente con prestazioni migliori. Per saperne di più, consulta Cosine similarity.DOT_PRODUCT: simile aCOSINE, ma è influenzata dalla grandezza dei vettori. Per saperne di più, consulta Prodotto scalare.
Scegli la misura della distanza
A seconda che tutti i vector embedding siano normalizzati o meno, puoi determinare quale misura di distanza utilizzare per trovare la misura di distanza. Un vector embedding normalizzato ha una grandezza (lunghezza) esattamente pari a 1,0.
Inoltre, se sai con quale misura di distanza è stato addestrato il tuo modello, utilizza questa misura per calcolare la distanza tra i tuoi 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, sebbene ogni misura di distanza restituisca un valore diverso, questi valori vengono ordinati nello stesso modo. Quando gli embedding sono 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 alle prestazioni, DOT_PRODUCT potrebbe aiutarti a ottimizzarle.
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 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 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 meno
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.
Prefiltra i documenti
Per eseguire il pre-filtraggio dei documenti prima di trovare i vicini più prossimi, puoi combinare una ricerca di somiglianza 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();
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();
Recupera la distanza del vettore calcolata
Puoi recuperare la distanza vettoriale calcolata assegnando un nome alla proprietà di output distance_result_field nella 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")); }
Specifica una soglia di distanza
Puoi specificare una soglia di somiglianza che restituisce solo i documenti che rientrano nella soglia. Il comportamento del campo della soglia dipende dalla misura della distanza scelta:
- Le distanze
EUCLIDEANeCOSINElimitano la soglia ai documenti in cui la distanza è inferiore 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 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 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 di vicini più prossimi è 1000 (limitazione solo per la versione 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 Cloud Firestore.
- Scopri di più sulle letture e scritture su larga scala.