Wyszukiwanie z użyciem wektorów dystrybucyjnych wektorowych

Strona pokazuje, jak używać funkcji Cloud Firestore do wykonywania operacji K w najbliższym czasie wyszukiwanie wektorów sąsiada (KNN) przy użyciu następujących metod:

  • Zapisz wartości wektorów
  • Tworzenie indeksów wektorów KNN i zarządzanie nimi
  • Utwórz zapytanie KNN (k-najbliższy sąsiad) przy użyciu jednego z obsługiwanych wektorów miary odległości

Wektory dystrybucyjne sklepów

Możesz tworzyć wartości wektorowe, takie jak wektory dystrybucyjne tekstu Cloud Firestore i przechowuj je w dokumentach Cloud Firestore.

Operacja zapisu z wektorem dystrybucyjnym wektorowym

Na przykładzie poniżej pokazujemy, jak zapisać wektor dystrybucyjny w formacie Dokument Cloud Firestore:

Python
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector

firestore_client = firestore.Client()
collection = firestore_client.collection("coffee-beans")
doc = {
    "name": "Kahawa coffee beans",
    "description": "Information about the Kahawa coffee beans.",
    "embedding_field": Vector([1.0, 2.0, 3.0]),
}

collection.add(doc)
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])
});

Wektory dystrybucyjne obliczeniowe za pomocą funkcji w Cloud Functions

Aby obliczać i przechowywać wektory dystrybucyjne wektorów za każdym razem, gdy dokument jest aktualizowany lub 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,
  });
}

Tworzenie indeksów wektorów i zarządzanie nimi

Zanim będzie można przeprowadzić wyszukiwanie najbliższych sąsiadów z użyciem wektorów dystrybucyjnych wektorowych, musisz utworzyć odpowiedni indeks. Poniższe przykłady pokazują, tworzenia indeksów wektorowych i zarządzania nimi.

Utwórz indeks wektorów

Przed utworzeniem indeksu wektorów uaktualnij Google Cloud CLI do najnowszej wersji:

gcloud components update

Aby utworzyć indeks wektorów, użyj funkcji 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

gdzie:

  • collection-group to identyfikator grupy kolekcji.
  • vector-field to nazwa pola zawierającego wektor dystrybucyjny.
  • database-id to identyfikator bazy danych.
  • vector-configuration obejmuje wektor dimension i typ indeksu. dimension jest liczbą całkowitą do 2048. Typ indeksu musi być ustawiony na flat. Sformatuj konfigurację indeksu w ten sposób: {"dimension":"DIMENSION", "flat": "{}"}.

Poniższy przykład tworzy indeks złożony, w tym indeks wektorów dla pola vector-field i indeks rosnący dla pola color. Możesz użyć tego typu indeksu do wstępnego filtrowania dane przed wyszukiwaniem najbliższych sąsiadów.

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

Wyświetl wszystkie indeksy wektorów

gcloud
gcloud firestore indexes composite list --database=database-id

Zastąp database-id identyfikatorem bazy danych.

Usuń indeks wektorów

gcloud
gcloud firestore indexes composite delete index-id --database=database-id

gdzie:

  • index-id to identyfikator indeksu do usunięcia. Użyj narzędzia indexes composite list, aby pobrać identyfikator indeksu.
  • database-id to identyfikator bazy danych.

Opisz indeks wektorów

gcloud
gcloud firestore indexes composite describe index-id --database=database-id

gdzie:

  • index-id to identyfikator indeksu do opisania. Użyj lub indexes composite list, aby pobrać identyfikator indeksu.
  • database-id to identyfikator bazy danych.

Utwórz zapytanie o najbliższych sąsiadów

Możesz wyszukać podobieństwo, aby znaleźć najbliższych sąsiadów wektorów dystrybucyjnych. Wyszukiwanie podobieństw wymaga indeksów wektorów. Jeśli indeks nie istnieje, Cloud Firestore proponuje indeks do utworzenia przy użyciu funkcji gcloud CLI.

W tym przykładzie znaleziono 10 najbliższych sąsiadów wektora zapytania.

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

# Requires a single-field vector index
vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=5,
)
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();

Odległości wektorowe

Zapytania o najbliższy sąsiad obsługują następujące opcje odległości wektora:

  • EUCLIDEAN: mierzy odległość UECLIDEAN między wektorami. Więcej informacji: Euklidesowa.
  • COSINE: porównuje wektory na podstawie kąta między nimi, co pozwala mierz podobieństwo, które nie jest oparte na wielkości wektorów. Zalecamy używanie funkcji DOT_PRODUCT z wektorami znormalizowanymi jednostkami zamiast Odległość COSINE, która jest matematycznym odpowiednikiem z lepszym skuteczność reklam. Aby dowiedzieć się więcej, zobacz Podobieństwo cosinusowe, które ma być potrzebne i innych.
  • DOT_PRODUCT: podobny do COSINE, ale podlega wektory dystrybucyjne. Więcej informacji: Iloczyn skalarny.

Wybierz pomiar odległości

W zależności od tego, czy wszystkie wektory dystrybucyjne wektorów są znormalizowane, można wyznaczyć odległość, która będzie używana do pomiaru odległości. Znormalizowany magnituda (długość) wynosi dokładnie 1,0.

Jeśli dodatkowo wiesz, z jakim pomiarem odległości został wytrenowany model, użyj tego miary odległości do obliczenia odległości między wektorem wektory dystrybucyjne.

Znormalizowane dane

Jeśli masz zbiór danych, w którym wszystkie wektory dystrybucyjne wektorów są znormalizowane, to wszystkie trzy miary odległości dają te same semantyczne wyniki wyszukiwania. W gruncie rzeczy, chociaż każdy miara odległości zwraca inną wartość, ale wartości są sortowane w ten sam sposób. Kiedy wektory dystrybucyjne są znormalizowane, przy czym DOT_PRODUCT jest zwykle najkorzystniejszą metodą obliczeniową skutecznie, ale w większości przypadków różnica jest nieistotna. Jeśli jednak aplikacja jest wysoce wrażliwa na wydajność, DOT_PRODUCT może pomóc w i optymalizacji wydajności.

Nieznormalizowane dane

Jeśli masz zbiór danych, w którym wektory dystrybucyjne wektorów nie są znormalizowane, użycie DOT_PRODUCT jako odległości nie jest prawidłowe matematycznie ponieważ iloczyn skalarny nie mierzy odległości. W zależności na temat sposobu generowania wektorów dystrybucyjnych i preferowanego typu wyszukiwania miara odległości COSINE lub EUCLIDEAN daje które są subiektywnie lepsze niż inne pomiary odległości. Eksperymenty COSINE lub EUCLIDEAN mogą być konieczne do określenia, która z nich jest najlepsza w Twoim przypadku.

Nie ma pewności, czy dane są znormalizowane czy nieznormalizowane

Jeśli nie masz pewności, czy Twoje dane są znormalizowane, i chcesz użyć DOT_PRODUCT, zalecamy używanie zamiast niego COSINE. COSINE jest jak DOT_PRODUCT z wbudowaną normalizacją. Odległość mierzona za pomocą funkcji COSINE w zakresie od 0 do 2. Wynik bliski 0 wskazuje, że wektory są bardzo podobne.

Wstępnie filtruj dokumenty

Aby wstępnie przefiltrować dokumenty przed znalezieniem najbliższych sąsiadów, możesz połączyć podobieństwo z innymi operatorami zapytań. and i Obsługiwane są filtry złożone or. Więcej informacji o obsługiwanych filtrach pól znajdziesz w artykule Operatory zapytań.

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

# Similarity search with pre-filter
# Requires a composite vector index
vector_query = collection.where("color", "==", "red").find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=5,
)
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();

Pobierz obliczoną odległość wektora

Obliczoną odległość wektora można pobrać, przypisując wartość nazwa właściwości wyjściowej distance_result_field w zapytaniu FindNearest, w tym przykładzie:

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_result_field="vector_distance",
)

docs = vector_query.stream()

for doc in docs:
    print(f"{doc.id}, Distance: {doc.get('vector_distance')}")
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'));
});

Jeśli chcesz użyć maski pola do zwrócenia podzbioru pól dokumentu razem z funkcją distanceResultField, musisz też umieścić w masce pola wartość distanceResultField, jak pokazano w tym przykładzie:

Python
vector_query = collection.select(["color", "vector_distance"]).find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_result_field="vector_distance",
)
Node.js
const vectorQuery: VectorQuery = coll
    .select('color', 'vector_distance')
    .findNearest({
      vectorField: 'embedding_field',
      queryVector: [3.0, 1.0, 2.0],
      limit: 10,
      distanceMeasure: 'EUCLIDEAN',
      distanceResultField: 'vector_distance'
    });

Określ próg odległości

Możesz określić próg podobieństwa, który spowoduje zwrócenie tylko dokumentów z i konkretnego progu. Działanie pola progu zależy od miary odległości wybierz:

  • Odległość EUCLIDEAN i COSINE ograniczają próg do dokumentów, w których odległość jest mniejsza od określonego progu lub jej równa. Ta odległość miary maleją, gdy wektory stają się coraz bardziej podobne.
  • Odległość w polu DOT_PRODUCT ogranicza próg do dokumentów, w których odległość wynosi nie przekracza określonego progu. Odległość w formie iloczynu skalarnego rośnie, gdy wektory stają się coraz bardziej podobne.

Poniższy przykład pokazuje, jak określić próg odległości, aby otrzymać 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
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_threshold=4.5,
)

docs = vector_query.stream()

for doc in docs:
    print(f"{doc.id}")
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);
});

Ograniczenia

Korzystając z wektorów dystrybucyjnych wektorowych, pamiętaj o następujących ograniczeniach:

  • Maksymalny obsługiwany wymiar wektora dystrybucyjnego to 2048. Aby przechowywać większe indeksy, użyj funkcji redukcja wymiarów.
  • Maksymalna liczba dokumentów do zwrócenia z zapytania o najbliższych sąsiadów to 1000.
  • Wyszukiwanie wektorowe nie obsługuje detektorów zrzutów w czasie rzeczywistym.
  • Tylko biblioteki klienta Python i Node.js obsługują wyszukiwanie wektorowe.

Co dalej?