На странице показано, как использовать 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 до последней версии:
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
с единичными нормализованными векторами вместо расстояния 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
. Дополнительные сведения о поддерживаемых фильтрах полей см. в разделе Операторы запроса .
Питон
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 .
- Понимание операций чтения и записи в масштабе .