Na tej stronie dowiesz się, jak używać Cloud Firestore do przeprowadzania wyszukiwania wektorowego z użyciem algorytmu K najbliższych sąsiadów (KNN) za pomocą tych technik:
- Przechowywanie wartości wektorów
- Tworzenie indeksów wektorowych KNN i zarządzanie nimi
- wykonać zapytanie o K-najbliższych sąsiadów (KNN) przy użyciu jednej z obsługiwanych miar odległości wektorowej;
Zanim zaczniesz
Zanim zapiszesz wektory dystrybucyjne w Cloud Firestore, musisz je wygenerować. Cloud Firestore nie generuje wektorów dystrybucyjnych. Możesz użyć usługi takiej jak Agent Platform, aby utworzyć wartości wektorowe, np. wektory dystrybucyjne tekstu z danych Cloud Firestore. Następnie możesz zapisać te wektory dystrybucyjne z powrotem w dokumentach Cloud Firestore.
Więcej informacji o wektorach dystrybucyjnych znajdziesz w artykule Czym są wektory dystrybucyjne?.
Aby dowiedzieć się, jak uzyskać wektory dystrybucyjne tekstu za pomocą Agent Platform, przeczytaj artykuł Uzyskiwanie wektorów dystrybucyjnych tekstu.
Przechowywanie wektorów dystrybucyjnych
Poniższe przykłady pokazują, jak przechowywać wektory dystrybucyjne w Cloud Firestore.
Operacja zapisu z wektorem dystrybucyjnym
Poniższy przykład pokazuje, jak przechowywać wektor dystrybucyjny w dokumencie 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();
Obliczanie wektorów dystrybucyjnych za pomocą funkcji w Cloud Functions
Aby obliczać i przechowywać wektory dystrybucyjne za każdym razem, gdy dokument jest aktualizowany lub tworzony, możesz skonfigurować funkcję w 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
Tworzenie indeksów wektorowych i zarządzanie nimi
Zanim będzie można przeprowadzić wyszukiwanie najbliższych sąsiadów za pomocą wektorów dystrybucyjnych, musisz utworzyć odpowiedni indeks. Poniższe przykłady pokazują, jak tworzyć indeksy wektorowe i nimi zarządzać za pomocą Google Cloud CLI i konsoli. Indeksami wektorowymi możesz też zarządzać za pomocą interfejsu wiersza poleceń Firebase i Terraform.
Utwórz indeks wektorowy
Konsola Google Cloud
Aby ręcznie utworzyć nowy indeks w konsoli Google Cloud:
- W konsoli Google Cloud otwórz stronę Bazy danych.
- Wybierz wymaganą bazę danych z listy baz danych.
- W menu nawigacyjnym kliknij Indeksy, a następnie kliknij kartę Ręczne.
- Kliknij Utwórz indeks.
Aby zindeksować pole wektora na potrzeby wyszukiwania wektorowego, wybierz Utwórz indeks wektorowy.
-
Wpisz identyfikator kolekcji. Wpisz ścieżkę pola wektora i liczbę wymiarów wektora dystrybucyjnego. Dodaj nazwy dodatkowych pól, które chcesz zindeksować, oraz tryb indeksowania dla każdego pola.
Kliknij Zapisz indeks.
Nowy indeks pojawi się na liście indeksów ręcznych, a Cloud Firestore rozpocznie jego tworzenie. Gdy indeks zostanie utworzony, pojawi się obok niego zielony znacznik wyboru.
gcloud
Zanim utworzysz indeks wektorowy, uaktualnij Google Cloud CLI do najnowszej wersji:
gcloud components update
Aby utworzyć indeks wektorowy, użyj 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
gdzie:
- collection-group to identyfikator grupy kolekcji.
- vector-field to nazwa pola zawierającego wektor dystrybucyjny.
- database-id to identyfikator bazy danych.
- vector-configuration zawiera wektor
dimensioni typ indeksu.dimensionto liczba całkowita do 2048. Typ indeksu musi byćflat. Sformatuj konfigurację indeksu w ten sposób:{"dimension":"DIMENSION", "flat": "{}"}.
Poniższy przykład tworzy indeks złożony, w tym indeks wektorowy dla pola vector-field i indeks rosnący dla pola color. Możesz użyć tego typu indeksu, aby filtrowanie wstępne danych przed wyszukiwaniem najbliższych sąsiadów.
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
Wyświetlanie listy wszystkich indeksów wektorowych
Konsola Google Cloud
- W konsoli Google Cloud otwórz stronę Bazy danych.
- Wybierz wymaganą bazę danych z listy baz danych.
-
W menu nawigacyjnym kliknij Indeksy, a następnie kliknij kartę Ręczne.
Tabela indeksów zawiera listę wszystkich indeksów bazy danych. Indeksy wektorowe zawierają pole wektora z ikoną .
gcloud
Aby wyświetlić listę wszystkich indeksów i pobrać ich identyfikatory:
gcloud firestore indexes composite list --database=database-id
Zastąp database-id identyfikatorem bazy danych.
Możesz użyć identyfikatora indeksu, aby wyświetlić więcej informacji o indeksie:
gcloud firestore indexes composite describe index-id --database=database-id
gdzie:
- index-id to identyfikator indeksu, który chcesz opisać.
- database-id to identyfikator bazy danych.
Usuwanie indeksu wektorowego
Konsola Google Cloud
- W konsoli Google Cloud otwórz stronę Bazy danych.
- Wybierz wymaganą bazę danych z listy baz danych.
-
W menu nawigacyjnym kliknij Indeksy, a następnie kliknij kartę Ręczne.
- Na liście indeksów ręcznych kliknij przycisk Więcej obok indeksu, który chcesz usunąć. Kliknij Usuń.
- Potwierdź, że chcesz usunąć ten indeks, klikając Usuń indeks w alercie.
gcloud
gcloud firestore indexes composite delete index-id --database=database-id
gdzie:
- index-id to identyfikator indeksu, który chcesz usunąć.
Aby pobrać identyfikator indeksu, użyj
indexes composite list. - database-id to identyfikator bazy danych.
Tworzenie zapytania o najbliższych sąsiadów
Możesz przeprowadzić wyszukiwanie na podstawie podobieństwa, aby znaleźć najbliższych sąsiadów wektora dystrybucyjnego. Wyszukiwanie podobieństwa wymaga indeksów wektorowych. Jeśli indeks nie istnieje, Cloud Firestore sugeruje indeks do utworzenia za pomocą gcloud CLI.
W tym przykładzie znajdowanych jest 10 najbliższych sąsiadów wektora zapytania.
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();
Odległości wektorów
Zapytania o najbliższego sąsiada obsługują te opcje odległości wektorowej:
EUCLIDEAN: mierzy odległośćEUCLIDEANmiędzy wektorami. Więcej informacji znajdziesz w artykule o odległości euklidesowej.COSINE: porównuje wektory na podstawie kąta między nimi, co pozwala mierzyć podobieństwo, które nie jest oparte na wielkości wektorów. Zamiast odległości COSINE zalecamy używanieDOT_PRODUCTz wektorami znormalizowanymi jednostkowo, co jest matematycznie równoważne i zapewnia lepszą wydajność. Więcej informacji znajdziesz w artykule na temat podobieństwa cosinusowego.DOT_PRODUCT: podobna doCOSINE, ale zależy od wielkości wektorów. Więcej informacji znajdziesz w artykule Iloczyn skalarny.
Wybierz miarę odległości
W zależności od tego, czy wszystkie wektory dystrybucyjne są znormalizowane, możesz określić, której miary odległości użyć, aby znaleźć miarę odległości. Znormalizowany wektor dystrybucyjny ma wielkość (długość) dokładnie 1,0.
Jeśli wiesz, jaka miara odległości została użyta do wytrenowania modelu, użyj jej do obliczenia odległości między wektorami dystrybucyjnymi.
Znormalizowane dane
Jeśli masz zbiór danych, w którym wszystkie wektory dystrybucyjne są znormalizowane, wszystkie 3 miary odległości dają takie same wyniki wyszukiwania semantycznego. W zasadzie, chociaż każda miara odległości zwraca inną wartość, wartości te są sortowane w ten sam sposób. Gdy wektory dystrybucyjne są znormalizowane, DOT_PRODUCT jest zwykle najbardziej wydajną metodą obliczeniową, ale w większości przypadków różnica jest nieznaczna. Jeśli jednak aplikacja jest bardzo wrażliwa na wydajność, DOT_PRODUCT może pomóc w jej dostrojeniu.
Dane nieznormalizowane
Jeśli masz zbiór danych, w którym wektory dystrybucyjne nie są znormalizowane, używanie DOT_PRODUCT jako miary odległości jest matematycznie niepoprawne, ponieważ iloczyn skalarny nie mierzy odległości. W zależności od tego, jak wygenerowano wektory dystrybucyjne i jaki typ wyszukiwania jest preferowany, miara odległości COSINE lub EUCLIDEAN daje wyniki wyszukiwania, które są subiektywnie lepsze niż inne miary odległości.
Aby określić, która opcja jest najlepsza w Twoim przypadku, konieczne może być eksperymentowanie z COSINE lub EUCLIDEAN.
Nie masz pewności, czy dane są znormalizowane czy nieznormalizowane
Jeśli nie masz pewności, czy dane są znormalizowane, i chcesz użyć parametru DOT_PRODUCT, zalecamy użycie parametru COSINE.
COSINE jest jak DOT_PRODUCT z wbudowaną normalizacją.
Odległość mierzona za pomocą COSINE wynosi od 0 do 2. Wynik bliski 0 wskazuje, że wektory są bardzo podobne.
Wstępne filtrowanie dokumentów
Aby wstępnie przefiltrować dokumenty przed znalezieniem najbliższych sąsiadów, możesz połączyć wyszukiwanie podobieństwa z innymi operatorami zapytań. Obsługiwane są filtry wieloskładnikowe and i or. Więcej informacji o obsługiwanych filtrach pól znajdziesz w artykule Operatory zapytań.
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();
Pobieranie obliczonej odległości wektora
Obliczoną odległość wektora możesz pobrać, przypisując nazwę właściwości wyjściowej distance_result_field w zapytaniu FindNearest, jak pokazano w tym przykładzie:
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")); }
Jeśli chcesz użyć maski pola, aby zwrócić podzbiór pól dokumentu wraz z wartością distanceResultField, musisz też uwzględnić wartość distanceResultField w masce pola, jak w tym przykładzie:
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")); }
Określanie progu odległości
Możesz określić próg podobieństwa, który zwraca tylko dokumenty mieszczące się w tym progu. Zachowanie pola progu zależy od wybranej miary odległości:
- Odległości
EUCLIDEANiCOSINEograniczają próg do dokumentów, w których odległość jest mniejsza lub równa określonemu progowi. Te miary odległości maleją, gdy wektory stają się bardziej podobne. DOT_PRODUCT– odległość ogranicza próg do dokumentów, w których odległość jest większa niż określony próg lub mu równa. Odległości w formie iloczynu skalarnego zwiększają się, gdy wektory stają się bardziej podobne.
Poniższy przykład pokazuje, jak określić próg odległości, aby zwrócić maksymalnie 10 najbliższych dokumentów, które są oddalone o maksymalnie 4,5 jednostki, przy użyciu wskaźnika odległości 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()); }
Ograniczenia
Podczas pracy z wektorami dystrybucyjnymi wektorów pamiętaj o tych ograniczeniach:
- Maksymalny obsługiwany wymiar wektora dystrybucyjnego to 2048. Aby przechowywać większe indeksy, użyj redukcji wymiarowości.
- Maksymalna liczba dokumentów zwracanych przez zapytanie o najbliższego sąsiada to 1000 (ograniczenie tylko w wersji Standard).
- Wyszukiwanie wektorowe nie obsługuje detektorów zrzutów w czasie rzeczywistym.
- Wyszukiwanie wektorowe obsługują tylko biblioteki klienta Python, Node.js, Go i Java.
Co dalej?
- Poznaj sprawdzone metody dotyczące Cloud Firestore.
- Dowiedz się, jak działają odczyty i zapisy na dużą skalę.