本頁面說明如何使用 Cloud Firestore 執行 K-nearest 使用以下技術進行鄰 (KNN) 向量搜尋:
- 儲存向量值
- 建立及管理 KNN 向量索引
- 使用支援的向量執行 K-nearest-Neighbor (KNN) 查詢 距離測量
儲存向量嵌入
您可以建立向量值,如文字嵌入: Cloud Firestore 資料並儲存在 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]) });
使用 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
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
做為距離,這方面不是數學上的正確
因為內積不會測量距離。視情況而定
包括如何產生嵌入及偏好的搜尋類型
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();
擷取計算的向量距離
如要擷取計算出的向量距離,請指派
FindNearest
查詢上的 distance_result_field
輸出屬性名稱,如
如以下範例所示:
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')); });
如要使用欄位遮罩傳回部分文件欄位和 distanceResultField
,您也必須在欄位遮罩中加入 distanceResultField
的值,如以下範例所示:
Python
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' });
指定距離門檻
您可以指定相似度門檻,只傳回 門檻。門檻欄位的行為取決於距離測量結果 :
EUCLIDEAN
和COSINE
距離會將閾值限制在 距離小於或等於指定門檻這些距離 會隨著向量變得更為相似。DOT_PRODUCT
距離規定門檻僅限於文件距離 大於或等於指定門檻。點積距離 向量變得更加相似。
以下範例說明如何使用 EUCLIDEAN
距離指標指定距離門檻,最多傳回 10 個最接近的文件,距離約 4.5 個單位距離:
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); });
限制
使用向量嵌入時,請注意下列限制:
- 支援的嵌入維度上限為 2048。如要儲存較大的索引,請使用 降低維度。
- 最鄰近查詢傳回的文件數量上限為 1000。
- Vector Search 不支援即時快照事件監聽器。
- 只有 Python 和 Node.js 用戶端程式庫支援向量搜尋。