На странице показано, как использовать Cloud Firestore для выполнения векторного поиска методом K-ближайших соседей (KNN) с использованием следующих методов:
- Сохранить векторные значения
- Создание и управление векторными индексами KNN
- Создайте запрос K-ближайших соседей (KNN), используя одну из поддерживаемых векторных мер расстояния.
Хранить векторные вложения
Вы можете создавать векторные значения, такие как встроенные текстовые данные, из данных Cloud Firestore и сохранять их в документах Cloud Firestore .
Операция записи с векторным вложением
В следующем примере показано, как сохранить векторное вложение в документе Cloud Firestore :
Питон
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]) });
Идти
Ява
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();
Вычислите векторные вложения с помощью облачной функции
Для расчета и сохранения векторных вложений при каждом обновлении или создании документа вы можете настроить облачную функцию :
Питон
@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, }); }
Идти
// Not yet supported in the Go client library
Ява
// Not yet supported in the Java client library
Создание и управление векторными индексами
Прежде чем выполнять поиск ближайшего соседа с использованием векторных вложений, необходимо создать соответствующий индекс. В следующих примерах показано, как создавать и управлять векторными индексами с помощью Google Cloud CLI . Векторными индексами также можно управлять с помощью Firebase CLI и Terraform .
Создать векторный индекс
Перед созданием векторного индекса обновите 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 ближайших соседей вектора запроса.
Питон
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();
Идти
Ява
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();
Векторные расстояния
Запросы ближайшего соседа поддерживают следующие параметры векторного расстояния:
-
EUCLIDEAN
: измеряет ЕВКЛИДОВО расстояние между векторами. Подробнее см. в разделе Евклидова . -
COSINE
: сравнивает векторы по углу между ними, что позволяет измерять сходство, не основываясь на величине векторов. Мы рекомендуем использоватьDOT_PRODUCT
с векторами, нормализованными на единицу, вместо расстояния по КОСИНУСУ, что математически эквивалентно и обеспечивает более высокую производительность. Подробнее см. в статье « Сходство по косинусу» . -
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
. Подробнее о поддерживаемых фильтрах полей см. в разделе «Операторы запросов» .
Питон
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();
Идти
Ява
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();
Получить рассчитанное векторное расстояние
Вы можете получить вычисленное расстояние по вектору, назначив имя выходного свойства distance_result_field
в запросе FindNearest
, как показано в следующем примере:
Питон
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')); });
Идти
Ява
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")); }
Если вы хотите использовать маску поля для возврата подмножества полей документа вместе с distanceResultField
, то необходимо также включить значение distanceResultField
в маску поля, как показано в следующем примере:
Питон
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' });
Идти
Ява
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")); }
Укажите порог расстояния
Вы можете указать порог сходства, который вернет только документы, соответствующие этому порогу. Поведение поля порога зависит от выбранной меры расстояния:
-
EUCLIDEAN
иCOSINE
расстояния ограничивают порог документами, расстояние между которыми меньше или равно заданному порогу. Эти показатели расстояния уменьшаются по мере повышения сходства векторов. - Расстояние
DOT_PRODUCT
ограничивает порог документами, где расстояние больше или равно указанному порогу. Расстояния скалярного произведения увеличиваются по мере того, как векторы становятся более похожими.
В следующем примере показано, как указать пороговое значение расстояния для возврата до 10 ближайших документов, находящихся на расстоянии не более 4,5 единиц, с использованием метрики расстояния EUCLIDEAN
:
Питон
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); });
Идти
Ява
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()); }
Ограничения
При работе с векторными вложениями обратите внимание на следующие ограничения:
- Максимальная поддерживаемая размерность вложения — 2048. Для хранения больших индексов используйте уменьшение размерности .
- Максимальное количество документов, возвращаемых по запросу ближайшего соседа, составляет 1000.
- Векторный поиск не поддерживает прослушиватели снимков в реальном времени .
- Векторный поиск поддерживают только клиентские библиотеки Python, Node.js, Go и Java.
Что дальше?
- Ознакомьтесь с рекомендациями по использованию Cloud Firestore .
- Понимание масштабных операций чтения и записи .