在 Spanner 中執行向量相似度搜尋,找出 K 近鄰

本頁說明如何在 Spanner 中執行向量相似度搜尋,方法是使用餘弦距離、歐氏距離和點積向量函式,找出 K 個最鄰近的項目。這項資訊適用於 GoogleSQL 方言資料庫和 PostgreSQL 方言資料庫。閱讀本頁面之前,請務必瞭解下列概念:

  • 歐氏距離:測量兩個向量之間的最短距離。
  • 餘弦距離: 測量兩個向量間的角度餘弦值。
  • 內積:計算角度的餘弦值,並乘以對應向量大小的乘積。如果您知道資料集中的所有向量嵌入內容都已正規化,則可以使用 DOT_PRODUCT() 做為距離函式。
  • K 最鄰近 (KNN): 一種監督式機器學習演算法,用於解決分類或迴歸問題。

您可以使用向量距離函式執行 K 近鄰 (KNN) 向量搜尋,用於相似度搜尋或檢索增強生成等用途。Spanner 支援 COSINE_DISTANCE()EUCLIDEAN_DISTANCE()DOT_PRODUCT() 函式,這些函式可對向量嵌入內容執行作業,讓您找出輸入嵌入內容的 KNN。

舉例來說,將作業 Spanner 資料產生並儲存為向量嵌入後,您就可以在查詢中提供這些向量嵌入做為輸入參數,在 N 維空間中尋找最接近的向量,搜尋語意相似或相關的項目。

這三個距離函式都會採用 vector1vector2 引數,這兩個引數屬於 array<> 類型,且必須包含相同維度,長度也必須相同。如要進一步瞭解這些函式,請參閱:

範例

下列範例會說明 KNN 搜尋、對分割資料進行 KNN 搜尋,以及搭配 KNN 使用次要索引。

所有範例都使用 EUCLIDEAN_DISTANCE()。你也可以使用 COSINE_DISTANCE()。此外,如果資料集中的所有向量嵌入都經過正規化,您可以使用 DOT_PRODUCT() 做為距離函式。

假設 Documents 資料表含有從 DocContents 位元組資料欄預先計算的文字嵌入 DocEmbedding 資料欄。

GoogleSQL

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

PostgreSQL

CREATE TABLE Documents (
UserId       bigint NOT NULL,
DocId        bigint NOT NULL,
Author       varchar(1024),
DocContents  bytea,
DocEmbedding float4[],
PRIMARY KEY  (UserId, DocId)
);

假設「棒球,但不是職業棒球」的輸入內容嵌入是陣列 [0.3, 0.3, 0.7, 0.7],您可以使用下列查詢,找出最接近的前五個相符文件:

GoogleSQL

SELECT DocId, DocEmbedding FROM Documents
ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
ARRAY<FLOAT32>[0.3, 0.3, 0.7, 0.8])
LIMIT 5;

PostgreSQL

SELECT DocId, DocEmbedding FROM Documents
ORDER BY spanner.euclidean_distance(DocEmbedding,
'{0.3, 0.3, 0.7, 0.8}'::float4[])
LIMIT 5;

這個範例的預期結果如下:

Documents
+---------------------------+-----------------+
| DocId                     | DocEmbedding    |
+---------------------------+-----------------+
| 24                        | [8, ...]        |
+---------------------------+-----------------+
| 25                        | [6, ...]        |
+---------------------------+-----------------+
| 26                        | [3.2, ...]      |
+---------------------------+-----------------+
| 27                        | [38, ...]       |
+---------------------------+-----------------+
| 14229                     | [1.6, ...]      |
+---------------------------+-----------------+

例 2:對分區資料執行 KNN 搜尋

您可以修改上一個範例中的查詢,在 WHERE 子句中新增條件,將向量搜尋限制為資料子集。這項功能常見的應用是搜尋分區資料,例如屬於特定 UserId 的資料列。

GoogleSQL

SELECT UserId, DocId, DocEmbedding FROM Documents
WHERE UserId=18
ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
ARRAY<FLOAT32>[0.3, 0.3, 0.7, 0.8])
LIMIT 5;

PostgreSQL

SELECT UserId, DocId, DocEmbedding FROM Documents
WHERE UserId=18
ORDER BY spanner.euclidean_distance(DocEmbedding,
'{0.3, 0.3, 0.7, 0.8}'::float4[])
LIMIT 5;

這個範例的預期結果如下:

Documents
+-----------+-----------------+-----------------+
| UserId    | DocId           | DocEmbedding    |
+-----------+-----------------+-----------------+
| 18        | 234             | [12, ...]       |
+-----------+-----------------+-----------------+
| 18        | 12              | [1.6, ...]      |
+-----------+-----------------+-----------------+
| 18        | 321             | [22, ...]       |
+-----------+-----------------+-----------------+
| 18        | 432             | [3, ...]        |
+-----------+-----------------+-----------------+

範例 3:透過次要索引範圍進行 KNN 搜尋

如果您使用的 WHERE 子句篩選器不屬於資料表的主鍵,則可建立次要索引,透過僅限索引掃描加快作業速度。

GoogleSQL

CREATE INDEX DocsByAuthor
ON Documents(Author)
STORING (DocEmbedding);

SELECT Author, DocId, DocEmbedding FROM Documents
WHERE Author="Mark Twain"
ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
   <embeddings for "book about the time traveling American">)
LIMIT 5;

PostgreSQL

CREATE INDEX DocsByAuthor
ON Documents(Author)
INCLUDE (DocEmbedding);

SELECT Author, DocId, DocEmbedding FROM Documents
WHERE Author="Mark Twain"
ORDER BY spanner.euclidean_distance(DocEmbedding,
   <embeddings for "that book about the time traveling American">)
LIMIT 5;

這個範例的預期結果如下:

Documents
+------------+-----------------+-----------------+
| Author     | DocId           | DocEmbedding    |
+------------+-----------------+-----------------+
| Mark Twain | 234             | [12, ...]       |
+------------+-----------------+-----------------+
| Mark Twain | 12              | [1.6, ...]      |
+------------+-----------------+-----------------+
| Mark Twain | 321             | [22, ...]       |
+------------+-----------------+-----------------+
| Mark Twain | 432             | [3, ...]        |
+------------+-----------------+-----------------+
| Mark Twain | 375             | [9, ...]        |
+------------+-----------------+-----------------+

後續步驟