На этой странице показано, как использовать Cloud Firestore для выполнения векторного поиска K-ближайших соседей (KNN) с помощью следующих методов:
- Сохранение значений векторов
- Создание векторных индексов KNN и управление ими
- Выполните запрос K-ближайших соседей (KNN), используя одну из поддерживаемых мер расстояния между векторами.
Подготовка
Прежде чем сохранять эмбеддинги в Cloud Firestore, необходимо сгенерировать векторные эмбеддинги. Cloud Firestore не генерирует эмбеддинги. Для создания векторных значений, например текстовых эмбеддингов из данных Cloud Firestore, можно использовать такой сервис, как Agent Platform. Затем вы можете сохранить эти эмбеддинги обратно в документы Cloud Firestore.
Подробнее о встраиваниях…
Чтобы узнать, как получить текстовые эмбеддинги с помощью Agent Platform, прочитайте статью Как получить текстовые эмбеддинги.
Хранение векторных встраиваний
В примерах ниже показано, как хранить векторные эмбеддинги в Cloud Firestore.
Операция записи с векторным встраиванием
В следующем примере показано, как сохранить векторное встраивание в документе 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]) });
Проложить маршрут
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();
Как вычислять векторные представления с помощью функции Cloud Functions
Чтобы вычислять и сохранять векторные эмбеддинги при каждом обновлении или создании документа, можно настроить функцию 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, }); }
Проложить маршрут
// Not yet supported in the Go client library
Java
// Not yet supported in the Java client library
Создание векторных индексов и управление ими
Прежде чем выполнять поиск ближайших соседей с помощью векторных вложений, необходимо создать соответствующий индекс. В следующих примерах показано, как создавать векторные индексы и управлять ими с помощью Google Cloud CLI и консоли. Вы также можете управлять векторными индексами с помощью интерфейса командной строки Firebase и Terraform.
Создание векторного индекса
Консоль Google Cloud
Чтобы вручную создать индекс в консоли Google Cloud, выполните следующие действия:
- В консоли Google Cloud перейдите на страницу Databases (Базы данных).
- Выберите нужную базу данных из списка.
- В меню навигации нажмите Индексы, а затем выберите вкладку Вручную.
- Нажмите Create Index (Создать индекс).
Чтобы проиндексировать векторное поле для векторного поиска, выберите Создать векторный индекс.
-
Введите идентификатор коллекции. Укажите путь к векторному полю и количество измерений векторного эмбеддинга. Добавьте названия дополнительных полей, которые нужно индексировать, и режим индексирования для каждого поля.
Нажмите Save Index (Сохранить индекс).
Новый индекс появится в списке индексов, созданных вручную, и Cloud Firestore начнет его создание. Когда индекс будет создан, рядом с ним появится зеленая галочка.
gcloud
Прежде чем создавать векторный индекс, обновите Google Cloud CLI до последней версии:
gcloud components update
Чтобы создать векторный индекс, используйте 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
Значения:
- collection-group – идентификатор группы коллекций.
- vector-field – название поля, содержащего векторное встраивание.
- database-id – идентификатор базы данных.
- vector-configuration включает вектор
dimensionи тип индекса.dimension– целое число, не превышающее 2048. Тип индекса должен бытьflat. Конфигурация индекса должна иметь следующий формат:{"dimension":"DIMENSION", "flat": "{}"}.
В следующем примере создается составной индекс, включающий векторный индекс для поля vector-field и индекс по возрастанию для поля color. Этот тип индекса можно использовать для предварительной фильтрации данных перед поиском ближайших соседей.
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
Как получить список всех векторных индексов
Консоль Google Cloud
- В консоли Google Cloud перейдите на страницу Databases (Базы данных).
- Выберите нужную базу данных из списка.
-
В меню навигации нажмите Индексы, а затем выберите вкладку Вручную.
В таблице индексов перечислены все индексы базы данных. Векторные индексы включают векторное поле со значком .
gcloud
Чтобы получить список всех индексов и их идентификаторов, выполните следующие действия:
gcloud firestore indexes composite list --database=database-id
Замените database-id на идентификатор базы данных.
Чтобы посмотреть подробные сведения об индексе, используйте его идентификатор:
gcloud firestore indexes composite describe index-id --database=database-id
Значения:
- index-id – идентификатор индекса, который нужно описать.
- database-id – идентификатор базы данных.
Как удалить векторный индекс
Консоль Google Cloud
- В консоли Google Cloud перейдите на страницу Databases (Базы данных).
- Выберите нужную базу данных из списка.
-
В меню навигации нажмите Индексы и откройте вкладку Вручную.
- В списке индексов, созданных вручную, нажмите кнопку Ещё рядом с нужным индексом. Нажмите Удалить.
- Подтвердите удаление индекса, нажав Удалить индекс в оповещении.
gcloud
gcloud firestore indexes composite delete index-id --database=database-id
Значения:
- index-id – идентификатор индекса, который нужно удалить.
Используйте
indexes composite list, чтобы получить идентификатор индекса. - database-id – идентификатор базы данных.
Как выполнить запрос ближайшего соседа
Вы можете выполнить поиск по сходству, чтобы найти ближайших соседей векторного встраивания. Для поиска по сходству требуются векторные индексы. Если индекс не существует, Cloud Firestore предлагает создать его с помощью gcloud CLI.
В следующем примере находятся 10 ближайших соседей вектора запроса.
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();
Проложить маршрут
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();
Расстояния между векторами
Запросы ближайших соседей поддерживают следующие варианты расстояния между векторами:
EUCLIDEAN– измеряет расстояние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. Подробнее об операторах запросов…
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();
Проложить маршрут
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();
Получить рассчитанное расстояние между векторами
Вы можете получить вычисленное расстояние между векторами, назначив имя свойства выходных данных distance_result_field в запросе FindNearest, как показано в следующем примере:
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')); });
Проложить маршрут
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")); }
Если вы хотите использовать маску поля, чтобы получить подмножество полей документа вместе с distanceResultField, то в маску поля также необходимо включить значение distanceResultField, как показано в следующем примере:
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' });
Проложить маршрут
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")); }
Укажите пороговое расстояние
Вы можете указать уровень сходства, чтобы возвращались только документы в пределах этого уровня. Поведение поля порогового значения зависит от выбранной меры расстояния:
- Расстояния
EUCLIDEANиCOSINEограничивают пороговое значение документами, расстояние до которых меньше или равно указанному пороговому значению. Эти расстояния уменьшаются по мере того, как векторы становятся более похожими. - Расстояние
DOT_PRODUCTограничивает пороговое значение документами, расстояние до которых больше или равно указанному пороговому значению. Расстояния скалярного произведения увеличиваются по мере того, как векторы становятся более похожими.
В следующем примере показано, как указать пороговое расстояние, чтобы вернуть до 10 ближайших документов, находящихся на расстоянии не более 4,5 единиц, используя метрику расстояния 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); });
Проложить маршрут
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()); }
Ограничения
При работе с векторными эмбеддингами учитывайте следующие ограничения:
- Максимальный поддерживаемый размер встраивания – 2048. Чтобы хранить более крупные индексы, используйте уменьшение размерности.
- Максимальное количество документов, возвращаемых по запросу ближайшего соседа, составляет 1000 (ограничение только для версии Standard).
- Векторный поиск не поддерживает прослушиватели снимков в реальном времени.
- Векторный поиск поддерживают только клиентские библиотеки Python, Node.js, Go и Java.
Дальнейшие действия
- Ознакомьтесь с рекомендациями по работе с Cloud Firestore.
- Подробнее о чтении и записи в больших масштабах…