Поиск с векторными вложениями

На странице показано, как использовать Cloud Firestore для поиска векторов K-ближайшего соседа (KNN), используя следующие методы:

  • Сохранение векторных значений
  • Создание векторных индексов KNN и управление ими
  • Создайте запрос K-ближайшего соседа (KNN), используя одну из поддерживаемых мер векторного расстояния.

Сохранение векторных вложений

Вы можете создавать векторные значения, такие как встраивание текста , из данных Cloud Firestore и хранить их в документах Cloud Firestore .

Операция записи с векторным вложением

В следующем примере показано, как сохранить векторное внедрение в документ Cloud Firestore :

Питон
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])
});

Вычисление векторных вложений с помощью облачной функции

Чтобы вычислять и сохранять векторные внедрения при каждом обновлении или создании документа, вы можете настроить облачную функцию :

Питон
@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,
  });
}

Создание векторных индексов и управление ими

Прежде чем вы сможете выполнить поиск ближайшего соседа с векторными вложениями, вы должны создать соответствующий индекс. Следующие примеры демонстрируют, как создавать векторные индексы и управлять ими.

Создать векторный индекс

Прежде чем создавать векторный индекс, обновите Google Cloud CLI до последней версии:

gcloud components update

Чтобы создать векторный индекс, используйте 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

где:

  • collection-group — это идентификатор группы коллекций.
  • vector-field — это имя поля, которое содержит вложение вектора.
  • database-id — это идентификатор базы данных.
  • vector-configuration включает dimension вектора и тип индекса. dimension представляет собой целое число до 2048. Тип индекса должен быть flat . Отформатируйте конфигурацию индекса следующим образом: {"dimension":" DIMENSION ", "flat": "{}"} .

В следующем примере создается составной индекс, включающий векторный индекс для поля vector-field и возрастающий индекс для поля color . Вы можете использовать этот тип индекса для предварительной фильтрации данных перед поиском ближайшего соседа.

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

Список всех векторных индексов

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

Замените database-id на идентификатор базы данных.

Удаление векторного индекса

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

где:

  • index-id — это идентификатор индекса, который нужно удалить. Используйте indexes composite list для получения идентификатора индекса.
  • database-id — это идентификатор базы данных.

Описать векторный индекс

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

где:

  • index-id — это идентификатор описываемого индекса. Используйте или indexes composite list для получения идентификатора индекса.
  • database-id — это идентификатор базы данных.

Сделать запрос ближайшего соседа

Вы можете выполнить поиск по сходству, чтобы найти ближайших соседей векторного вложения. Для поиска по сходству требуются векторные индексы . Если индекс не существует, Cloud Firestore предлагает создать индекс с помощью gcloud CLI .

В следующем примере выполняется поиск 10 ближайших соседей вектора запроса.

Питон
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();

Векторные расстояния

Запросы ближайших соседей поддерживают следующие параметры векторного расстояния:

  • EUCLIDEAN : Измеряет ЕВКЛИДОВОЕ расстояние между векторами. Чтобы узнать больше, см. Евклид .
  • COSINE : сравнивает векторы по углу между ними, что позволяет измерить сходство, не основанное на величине векторов. Мы рекомендуем использовать DOT_PRODUCT с единичными нормализованными векторами вместо расстояния COSINE, что математически эквивалентно и обеспечивает лучшую производительность. Чтобы узнать больше, см. Сходство косинуса , чтобы узнать больше.
  • DOT_PRODUCT : аналогично COSINE , но на него влияет величина векторов. Дополнительные сведения см. в разделе Скалярное произведение .

Выберите меру расстояния

В зависимости от того, нормализованы ли все ваши векторные вложения, вы можете определить, какую меру расстояния использовать для ее нахождения. Нормализованное векторное вложение имеет величину (длину) ровно 1,0.

Кроме того, если вы знаете, с какой мерой расстояния была обучена ваша модель, используйте эту меру расстояния для вычисления расстояния между векторными внедрениями.

Нормализованные данные

Если у вас есть набор данных, в котором все векторные вложения нормализованы, то все три меры расстояния обеспечивают одинаковые результаты семантического поиска. По сути, хотя каждая мера расстояния возвращает разное значение, эти значения сортируются одинаково. Когда встраивания нормализованы, DOT_PRODUCT обычно является наиболее эффективным в вычислительном отношении, но в большинстве случаев разница незначительна. Однако, если ваше приложение очень чувствительно к производительности, DOT_PRODUCT может помочь в настройке производительности.

Ненормализованные данные

Если у вас есть набор данных, в котором векторные представления не нормализованы, то математически неправильно использовать DOT_PRODUCT в качестве меры расстояния, поскольку скалярное произведение не измеряет расстояние. В зависимости от того, как были созданы вложения и какой тип поиска является предпочтительным, мера расстояния COSINE или EUCLIDEAN дает результаты поиска, которые субъективно лучше, чем другие меры расстояния. Возможно, потребуется поэкспериментировать с COSINE или EUCLIDEAN , чтобы определить, что лучше всего подходит для вашего случая использования.

Не уверены, нормализованы или ненормализованы данные.

Если вы не уверены, нормализованы ли ваши данные, и хотите использовать DOT_PRODUCT , мы рекомендуем вместо этого использовать COSINE . COSINE похож на DOT_PRODUCT со встроенной нормализацией. Расстояние, измеренное с помощью COSINE находится в диапазоне от 0 до 2 . Результат, близкий к 0 указывает на то, что векторы очень похожи.

Предварительная фильтрация документов

Чтобы предварительно отфильтровать документы перед поиском ближайших соседей, вы можете объединить поиск по сходству с другими операторами запроса. Поддерживаются составные фильтры and и or . Дополнительные сведения о поддерживаемых фильтрах полей см. в разделе Операторы запроса .

Питон
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();

Получить рассчитанное векторное расстояние

Вы можете получить вычисленное векторное расстояние, назначив имя выходного свойства distance_result_field в запросе FindNearest , как показано в следующем примере:

Питон
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'));
});

Если вы хотите использовать маску поля для возврата подмножества полей документа вместе с distanceResultField , вам также необходимо включить значение distanceResultField в маску поля, как показано в следующем примере:

Питон
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'
    });

Укажите порог расстояния

Вы можете указать порог сходства, который будет возвращать только документы в пределах этого порога. Поведение поля порога зависит от выбранной вами меры расстояния:

  • Расстояния EUCLIDEAN и COSINE ограничивают порог для документов, в которых расстояние меньше или равно указанному порогу. Эти меры расстояния уменьшаются по мере того, как векторы становятся более похожими.
  • Расстояние DOT_PRODUCT ограничивает порог для документов, расстояние которых больше или равно указанному порогу. Расстояния скалярного произведения увеличиваются по мере того, как векторы становятся более похожими.

В следующем примере показано, как указать порог расстояния для возврата до 10 ближайших документов, находящихся на расстоянии не более 4,5 единиц, с использованием метрики расстояния EUCLIDEAN :

Питон
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);
});

Ограничения

При работе с векторными векторными представлениями обратите внимание на следующие ограничения:

  • Максимальное поддерживаемое измерение внедрения — 2048. Для хранения индексов большего размера используйте уменьшение размерности .
  • Максимальное количество документов, возвращаемых по запросу ближайшего соседа, — 1000.
  • Векторный поиск не поддерживает прослушиватели снимков в реальном времени .
  • Только клиентские библиотеки Python и Node.js поддерживают векторный поиск.

Что дальше