На странице показано, как использовать 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
, но зависит от величины векторов. Чтобы узнать больше, см. Dot product .
Выберите меру расстояния
В зависимости от того, нормализованы ли все ваши векторные вложения, вы можете определить, какую меру расстояния использовать для нахождения меры расстояния. Нормализованное векторное вложение имеет величину (длину) ровно 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 .
- Понимание операций чтения и записи в масштабе .