使用向量嵌入項目搜尋

本頁面說明如何使用 Cloud Firestore 執行 K-nearest 使用以下技術進行鄰 (KNN) 向量搜尋:

  • 儲存向量值
  • 建立及管理 KNN 向量索引
  • 使用支援的向量執行 K-nearest-Neighbor (KNN) 查詢 距離測量

儲存向量嵌入

您可以建立向量值,如文字嵌入Cloud Firestore 資料並儲存在 Cloud Firestore 份文件中。

使用向量嵌入功能寫入運算

以下範例說明如何將向量嵌入儲存在 Cloud Firestore 文件:

Python
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])
});

使用 Cloud 函式計算向量嵌入

在每次更新文件或檔案更新時,計算及儲存向量嵌入 您可以設定 Cloud 函式

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,
  });
}

建立及管理向量索引

您必須先使用向量嵌入項目執行最鄰近搜尋 您必須建立相應的索引下列範例為 如何建立及管理向量索引

建立向量索引

建立向量索引前,請先升級至最新版的 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 是產品素材資源集合群組的 ID。
  • vector-field 是包含向量嵌入的欄位名稱。
  • database-id 是資料庫的 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 替換為資料庫 ID。

刪除向量索引

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

其中:

  • index-id 是要刪除的索引 ID。 使用 indexes composite list 擷取索引 ID。
  • database-id 是資料庫的 ID。

說明向量索引

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

其中:

  • index-id 是要說明的索引 ID。使用或 indexes composite list:擷取索引 ID。
  • database-id 是資料庫的 ID。

執行最鄰近查詢

此時,您可以執行相似度搜尋,找出最接近 以及向量嵌入功能相似度搜尋需要向量索引。 如果索引不存在,Cloud Firestore 會建議要建立的索引 使用 gcloud CLI

以下範例會找出查詢向量的 10 個最鄰近的項目,

Python
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:測量向量之間的 EUCLIDEAN 距離。 詳情請參閱: Euclidean
  • COSINE:根據向量之間的角度比較向量 而不是以向量規模為依據 我們建議針對單位正規化向量使用 DOT_PRODUCT,而非 COSINE 距離 (在數學上相等), 才需進行詳情請參閱: 餘弦相似度: 內容。
  • DOT_PRODUCT:與 COSINE 類似,但受到 建立向量詳情請參閱: 點號產品

請選擇距離度量

視向量嵌入是否經過正規化而定,您可以 決定要使用哪個距離測量單位。正規化 向量嵌入的規模 (長度) 正好為 1.0。

此外,如果您知道用來訓練模型的距離, 利用這個距離測量 計算向量之間的距離 和一些嵌入

正規化資料

如果您的資料集中的所有向量嵌入都經過正規化處理,則這三個 距離測量結果提供相同的語意搜尋結果。本質上 距離測量結果會傳回不同的值,這些值會以相同方式排序。時間 嵌入會經過正規化,DOT_PRODUCT 通常是最常用來 但在大多數情況下這種差異不大。不過, 應用程式高度敏感,因此 DOT_PRODUCT 可協助您 效能微調功能

非正規化資料

如果資料集的向量嵌入並未正規化 那麼以 DOT_PRODUCT 做為距離,這方面不是數學上的正確 因為內積不會測量距離。視情況而定 包括如何產生嵌入及偏好的搜尋類型 COSINEEUCLIDEAN 距離測量結果會產生 明顯優於其他距離的測量結果 使用 COSINEEUCLIDEAN 進行實驗可能會 ),決定何者最適合您的用途。

不確定資料是經過正規化還是非正規化

如果不確定資料是否已經過正規化,並使用 DOT_PRODUCT,建議您改用 COSINECOSINE 就像 DOT_PRODUCT,內建正規化功能。 使用 COSINE 測量的距離範圍,從 02。結果 如果接近 0,表示向量非常相似。

預先篩選文件

如要在尋找最鄰近的項目前先預先篩選文件,您可以結合 與其他查詢運算子進行相似度搜尋。and和 支援 or 複合篩選器。如要進一步瞭解支援的欄位篩選器,請參閱查詢運算子

Python
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();

擷取計算的向量距離

如要擷取計算出的向量距離,請指派 FindNearest 查詢上的 distance_result_field 輸出屬性名稱,如 如以下範例所示:

Python
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 的值,如以下範例所示:

Python
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'
    });

指定距離門檻

您可以指定相似度門檻,只傳回 門檻。門檻欄位的行為取決於距離測量結果 :

  • EUCLIDEANCOSINE 距離會將閾值限制在 距離小於或等於指定門檻這些距離 會隨著向量變得更為相似。
  • DOT_PRODUCT 距離規定門檻僅限於文件距離 大於或等於指定門檻。點積距離 向量變得更加相似。

以下範例說明如何使用 EUCLIDEAN 距離指標指定距離門檻,最多傳回 10 個最接近的文件,距離約 4.5 個單位距離:

Python
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。
  • Vector Search 不支援即時快照事件監聽器
  • 只有 Python 和 Node.js 用戶端程式庫支援向量搜尋。

後續步驟