Auf der Seite wird gezeigt, wie Sie mit Cloud Firestore KNN-Vektorsuchen (K-Nearest-Neighbor) mit den folgenden Methoden ausführen:
- Vektorwerte speichern
- KNN-Vektorindizes erstellen und verwalten
- KNN-Abfrage (K-Nearest-Neighbor) mit einer der unterstützten Vektormesswerte für die Entfernung ausführen
Vektoreinbettungen speichern
Sie können Vektorwerte wie Text-Embeddings aus Ihren Cloud Firestore-Daten erstellen und in Cloud Firestore-Dokumenten speichern.
Schreibvorgang mit einer Vektoreinbettung
Das folgende Beispiel zeigt, wie eine Vektor-Embedding in einem Cloud Firestore-Dokument gespeichert wird:
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();
Vektor-Embeddings mit einer Cloud-Funktion berechnen
Wenn Sie Vektor-Embeddings jedes Mal berechnen und speichern möchten, wenn ein Dokument aktualisiert oder erstellt wird, können Sie eine Cloud Functions-Funktion einrichten:
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
Vektorindexe erstellen und verwalten
Bevor Sie mit Ihren Vektoreinbettungen eine Suche nach dem nächsten Nachbarn durchführen können, müssen Sie einen entsprechenden Index erstellen. In den folgenden Beispielen wird gezeigt, wie Sie Vektorindexe mit der Google Cloud CLI erstellen und verwalten. Vektorindexe können auch mit der Firebase CLI und Terraform verwaltet werden.
Vektorindex erstellen
Bevor Sie einen Vektorindex erstellen, aktualisieren Sie auf die neueste Version der Google Cloud CLI:
gcloud components update
Verwenden Sie gcloud firestore indexes composite create
, um einen Vektorindex zu erstellen:
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
Dabei gilt:
- collection-group ist die ID der Sammlungsgruppe.
- vector-field ist der Name des Felds, das die Vektor-Embeddings enthält.
- database-id ist die ID der Datenbank.
- vector-configuration enthält den Vektor
dimension
und den Indextyp.dimension
ist eine Ganzzahl bis 2.048. Der Indextyp mussflat
sein. Formatieren Sie die Indexkonfiguration so:{"dimension":"DIMENSION", "flat": "{}"}
.
Im folgenden Beispiel wird ein zusammengesetzter Index erstellt, der einen Vektorindex für das Feld vector-field
und einen aufsteigenden Index für das Feld color
enthält. Mit diesem Indextyp können Sie Daten vor einer Suche nach dem nächsten Nachbarn filtern.
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
Alle Vektorindexe auflisten
gcloud
gcloud firestore indexes composite list --database=database-id
Ersetzen Sie database-id durch die ID der Datenbank.
Vektorindex löschen
gcloud
gcloud firestore indexes composite delete index-id --database=database-id
Dabei gilt:
- index-id ist die ID des zu löschenden Index.
Verwenden Sie
indexes composite list
, um die Index-ID abzurufen. - database-id ist die ID der Datenbank.
Vektorindex beschreiben
gcloud
gcloud firestore indexes composite describe index-id --database=database-id
Dabei gilt:
- index-id ist die ID des zu beschreibenden Index. Verwenden Sie oder
indexes composite list
, um die Index-ID abzurufen. - database-id ist die ID der Datenbank.
Abfrage nach nächsten Nachbarn
Sie können eine Ähnlichkeitssuche durchführen, um die nächsten Nachbarn einer Vektoreinbettung zu ermitteln. Für Ähnlichkeitssuchen sind Vektorindizes erforderlich. Wenn kein Index vorhanden ist, schlägt Cloud Firestore einen Index vor, der mithilfe der gcloud CLI erstellt werden kann.
Im folgenden Beispiel werden die 10 nächsten Nachbarn des Abfragevektors ermittelt.
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();
Vektorabstände
Für Abfragen nach dem nächsten Nachbarn werden die folgenden Optionen für die Vektordistanz unterstützt:
EUCLIDEAN
: Misst die EUKLIDISCHE Distanz zwischen den Vektoren. Weitere Informationen finden Sie unter Euklidischer Abstand.COSINE
: Vektoren werden anhand des Winkels zwischen ihnen verglichen. So lässt sich die Ähnlichkeit unabhängig von der Größe der Vektoren messen. Wir empfehlen,DOT_PRODUCT
mit normalisierten Vektoren anstelle der KOSINUS-Distanz zu verwenden. Dies ist mathematisch äquivalent und bietet eine bessere Leistung. Weitere Informationen finden Sie unter Kosinusialhörigkeit.DOT_PRODUCT
: Ähnlich wieCOSINE
, wird aber von der Größe der Vektoren beeinflusst. Weitere Informationen finden Sie unter Skalarprodukt.
Maßeinheit für die Entfernung auswählen
Je nachdem, ob alle Ihre Vektoreinbettungen normalisiert sind, können Sie festlegen, welche Distanzmessung verwendet werden soll. Eine normalisierte Vektoreinbettung hat eine Größe (Länge) von genau 1,0.
Wenn Sie außerdem wissen, mit welchem Distanzmaß Ihr Modell trainiert wurde, verwenden Sie dieses Maß, um die Distanz zwischen Ihren Vektoreinbettungen zu berechnen.
Normalisierte Daten
Wenn Sie einen Datensatz haben, in dem alle Vektor-Ebenen normalisiert sind, liefern alle drei Abstandsmaße dieselben semantischen Suchergebnisse. Obwohl jedes Maß für die Entfernung einen anderen Wert zurückgibt, werden diese Werte im Grunde auf dieselbe Weise sortiert. Wenn Einbettungen normalisiert sind, ist DOT_PRODUCT
in der Regel die leistungseffizienteste Option. Der Unterschied ist jedoch in den meisten Fällen vernachlässigbar. Wenn Ihre Anwendung jedoch sehr leistungsabhängig ist, kann DOT_PRODUCT
bei der Leistungsoptimierung helfen.
Nicht normalisierte Daten
Wenn Sie einen Datensatz haben, in dem Vektoreinbettungen nicht normalisiert sind, ist es mathematisch nicht korrekt, DOT_PRODUCT
als Maß für die Entfernung zu verwenden, da mit dem Skalarprodukt keine Entfernung gemessen wird. Je nachdem, wie die Einbettungen generiert wurden und welche Art von Suche bevorzugt wird, liefern entweder die COSINE
- oder die EUCLIDEAN
-Distanzmessung Suchergebnisse, die subjektiv besser sind als die der anderen Distanzmessungen.
Es kann sein, dass Sie mit COSINE
oder EUCLIDEAN
experimentieren müssen, um herauszufinden, welche Option für Ihren Anwendungsfall am besten geeignet ist.
Nicht sicher, ob die Daten normalisiert sind
Wenn Sie nicht sicher sind, ob Ihre Daten normalisiert sind, und DOT_PRODUCT
verwenden möchten, empfehlen wir stattdessen COSINE
.
COSINE
ist wie DOT_PRODUCT
, jedoch mit integrierter Normalisierung.
Die mit COSINE
gemessene Entfernung liegt zwischen 0
und 2
. Ein Ergebnis, das nahe an 0
liegt, weist darauf hin, dass die Vektoren sehr ähnlich sind.
Dokumente vorfiltern
Wenn Sie Dokumente vor dem Finden der nächsten Nachbarn vorfiltern möchten, können Sie eine Ähnlichkeitssuche mit anderen Abfrageoperatoren kombinieren. Die zusammengesetzten Filter and
und or
werden unterstützt. Weitere Informationen zu unterstützten Feldfiltern finden Sie unter Abfrageoperatoren.
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();
Berechnete Vektordistanz abrufen
Sie können die berechnete Vektordistanz abrufen, indem Sie der FindNearest
-Abfrage einen distance_result_field
-Ausgabeeigenschaftsnamen zuweisen, wie im folgenden Beispiel gezeigt:
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")); }
Wenn Sie mithilfe eines Feldmaskens eine Teilmenge der Dokumentfelder zusammen mit einem distanceResultField
zurückgeben möchten, müssen Sie auch den Wert von distanceResultField
in die Feldmaske aufnehmen, wie im folgenden Beispiel gezeigt:
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")); }
Einen Entfernungsgrenzwert angeben
Sie können einen Ähnlichkeitsgrenzwert angeben, mit dem nur Dokumente innerhalb des Grenzwerts zurückgegeben werden. Das Verhalten des Schwellenwertfelds hängt von der ausgewählten Entfernungsmessung ab:
- Bei den Entfernungen
EUCLIDEAN
undCOSINE
wird der Grenzwert auf Dokumente beschränkt, bei denen die Entfernung kleiner oder gleich dem angegebenen Grenzwert ist. Diese Distanzmaße nehmen ab, je ähnlicher die Vektoren sind. - Mit
DOT_PRODUCT
distance wird der Grenzwert auf Dokumente beschränkt, bei denen der Abstand mindestens dem angegebenen Grenzwert entspricht. Die Punktprodukt-Distanzen steigen, je ähnlicher die Vektoren sind.
Im folgenden Beispiel wird gezeigt, wie Sie einen Grenzwert für die Entfernung angeben, um mithilfe des Entfernungsmesswerts EUCLIDEAN
bis zu zehn der nächstgelegenen Dokumente zurückzugeben, die maximal 4,5 Einheiten entfernt sind:
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()); }
Beschränkungen
Beachten Sie bei der Arbeit mit Vektoreinbettungen die folgenden Einschränkungen:
- Die maximal unterstützte Einbettungsdimension ist 2.048. Wenn Sie größere Indexe speichern möchten, verwenden Sie die Dimensionseinschränkung.
- Die maximale Anzahl von Dokumenten, die bei einer Abfrage vom Typ „Nächster Nachbar“ zurückgegeben werden, beträgt 1.000.
- Die Vektorsuche unterstützt keine Echtzeit-Snapshot-Listener.
- Nur die Python-, Node.js-, Go- und Java-Clientbibliotheken unterstützen die Vektorsuche.