找出近似最鄰近項目 (ANN) 和查詢向量嵌入項目

本頁說明如何使用 ANN 距離函式,找出近似最近鄰 (ANN) 並查詢向量嵌入。

如果資料集很小,可以使用 K 近鄰 (KNN) 找出精確的 K 近鄰向量。不過,隨著資料集擴大,KNN 搜尋的延遲時間和費用也會增加。您可以使用 ANN 找出近似的 k 個最鄰近項目,大幅降低延遲和成本。

在 ANN 搜尋中,傳回的 k 個向量並非真正的 k 個最近鄰,因為 ANN 搜尋會計算近似距離,且可能不會查看資料集中的所有向量。有時,系統會傳回幾個不在前 k 個最鄰近項目中的向量。這就是所謂的「召回率損失」。 可接受的召回率損失程度取決於用途,但在大多數情況下,為了提升資料庫效能而損失部分召回率,是可接受的取捨。

如要進一步瞭解 Spanner 支援的近似距離函式,請參閱下列資料庫方言的參考頁面:

查詢向量嵌入

Spanner 會使用向量索引,加快近似最鄰近 (ANN) 向量搜尋的速度。您可以使用向量索引查詢向量嵌入。如要查詢向量嵌入,請先建立向量索引。接著,您可以使用任一項近似距離函式來尋找 ANN。

使用概略距離函式時的限制包括:

  • 概略距離函式必須計算嵌入資料欄和常數運算式 (例如參數或常值) 之間的距離。
  • 概略距離函式輸出內容必須在 ORDER BY 子句中做為唯一排序鍵,且 ORDER BY 後方必須指定 LIMIT
  • 查詢必須明確篩除未建立索引的資料列。在大多數情況下,這表示查詢必須包含與向量索引定義相符的 WHERE <column_name> IS NOT NULL 子句,除非資料欄已在表格定義中標示為 NOT NULL

如需詳細的限制清單,請參閱約略距離函式參考頁面

範例

假設有一個 Documents 資料表,其中包含從 DocContents 位元組資料欄預先計算的文字嵌入 DocEmbedding 資料欄,以及從其他來源填入的 NullableDocEmbedding 資料欄 (可能為空值)。

GoogleSQL

CREATE TABLE Documents (
  UserId       INT64 NOT NULL,
  DocId        INT64 NOT NULL,
  Author       STRING(1024),
  DocContents  BYTES(MAX),
  DocEmbedding ARRAY<FLOAT32> NOT NULL,
  NullableDocEmbedding ARRAY<FLOAT32>,
  WordCount    INT64
) PRIMARY KEY (UserId, DocId);

PostgreSQL

CREATE TABLE documents (
  user_id      bigint not null,
  doc_id       bigint not null,
  author       varchar(1024),
  doc_contents bytea,
  doc_embedding float4[] not null,
  nullable_doc_embedding float4[],
  word_count   bigint,
  PRIMARY KEY (user_id, doc_id)
);

如要搜尋最接近 [1.0, 2.0, 3.0] 的 100 個向量,請按照下列步驟操作:

GoogleSQL

SELECT DocId
FROM Documents
WHERE WordCount > 1000
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], DocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

PostgreSQL

SELECT doc_id
FROM documents
WHERE word_count > 1000
ORDER BY spanner.approx_euclidean_distance(
  ARRAY[1.0, 2.0, 3.0]::float4[], doc_embedding,
  options=>jsonb'{"num_leaves_to_search": 10}'
)
LIMIT 100;

如要搜尋最接近 SQL 運算式所產生嵌入項目的 100 個向量,請使用下列模式。在本例中,查詢會找出與 UserId = 1 和 DocId = 1 嵌入最相似的文件:

GoogleSQL

WITH emb AS (
  SELECT DocEmbedding AS value
  FROM Documents
  WHERE UserId = 1 AND DocId = 1
  LIMIT 1
)
SELECT DocId
FROM Documents, emb
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  emb.value, DocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

PostgreSQL

SELECT documents.doc_id
FROM
  documents,
  (
    SELECT doc_embedding AS value
    FROM documents
    WHERE user_id = 1 AND doc_id = 1
    LIMIT 1
  ) vector
WHERE documents.doc_embedding IS NOT NULL
ORDER BY spanner.APPROX_EUCLIDEAN_DISTANCE(documents.doc_embedding,
         vector.value, options=>'{"num_leaves_to_search": 10}'::jsonb)
LIMIT 100

如果嵌入資料欄可為空值:

GoogleSQL

SELECT DocId
FROM Documents
WHERE NullableDocEmbedding IS NOT NULL AND WordCount > 1000
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], NullableDocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

PostgreSQL

SELECT doc_id
FROM documents
WHERE nullable_doc_embedding IS NOT NULL AND word_count > 1000
ORDER BY spanner.approx_euclidean_distance(
  ARRAY[1.0, 2.0, 3.0]::float4[], nullable_doc_embedding,
  options=>jsonb'{"num_leaves_to_search": 10}'
)
LIMIT 100;

後續步驟