向量搜尋
隨著生成式 AI 的興起,向量資料庫在資料庫世界中獲得了強大的關注。這些資料庫能夠高效地儲存和查詢高維向量,使其非常適合語義搜尋、推薦系統和自然語言理解等任務。
向量搜尋是一種透過比較向量表示(也稱為嵌入)而非依賴傳統精確匹配查詢來檢索語義相似資料的技術。這種方法支援智慧的、上下文感知的應用程式,超越了基於關鍵詞的檢索。
在 Spring Data 的上下文中,向量搜尋為構建智慧的、上下文感知的應用程式開闢了新的可能性,尤其是在自然語言處理、推薦系統和生成式 AI 等領域。透過使用熟悉的倉庫抽象對基於向量的查詢進行建模,Spring Data 允許開發人員以 Spring Data 程式設計模型的簡潔性和一致性無縫整合基於相似度的向量資料庫。
要使用 Hibernate 向量搜尋,您需要在專案中新增以下依賴項。
以下示例展示瞭如何在 Maven 和 Gradle 中設定依賴項
-
Maven
-
Gradle
<dependencies>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-vector</artifactId>
<version>${hibernate.version}</version>
</dependency>
</dependencies>
dependencies {
implementation 'org.hibernate.orm:hibernate-vector:${hibernateVersion}'
}
儘管您可以使用 Vector 作為查詢的型別,但不能在域模型中使用它,因為 Hibernate 要求浮點數或雙精度陣列作為向量型別。 |
向量模型
為了以型別安全和慣用的方式支援向量搜尋,Spring Data 引入了以下核心抽象
Vector
Vector 型別表示 n 維數值嵌入,通常由嵌入模型生成。在 Spring Data 中,它被定義為浮點數陣列的輕量級包裝器,確保不變性和一致性。此型別可用作搜尋查詢的輸入,或作為域實體上的屬性來儲存關聯的向量表示。
Vector vector = Vector.of(0.23f, 0.11f, 0.77f);
在域模型中使用 Vector 消除了使用原始陣列或數字列表的需要,提供了一種更型別安全和富有表現力的方式來處理向量資料。這種抽象還允許輕鬆整合各種向量資料庫和庫。它還允許實現供應商特定的最佳化,例如不對映到標準浮點數(根據 IEEE 754 的 float 和 double)表示的二進位制或量化向量。域物件可以具有向量屬性,可用於相似性搜尋。請看以下示例
class Comment {
@Id String id;
String country;
String comment;
@Column(name = "the_embedding")
@JdbcTypeCode(SqlTypes.VECTOR)
@Array(length = 5)
Vector embedding;
// getters, setters, …
}
| 將向量與域物件關聯會導致向量作為實體生命週期的一部分被載入和儲存,這可能會增加檢索和持久化操作的額外開銷。 |
搜尋結果
SearchResult<T> 型別封裝了向量相似性查詢的結果。它包括匹配的域物件和表示其與查詢向量匹配程度的相關性分數。這種抽象提供了一種結構化的方式來處理結果排名,並使開發人員能夠輕鬆處理資料及其上下文相關性。
SearchResult<T>interface CommentRepository extends Repository<Comment, String> {
SearchResults<Comment> searchByCountryAndEmbeddingNear(String country, Vector vector, Score distance,
Limit limit);
@Query("""
SELECT c, cosine_distance(c.embedding, :embedding) as distance FROM Comment c
WHERE c.country = ?1
AND cosine_distance(c.embedding, :embedding) <= :distance
ORDER BY distance asc""")
SearchResults<Comment> searchAnnotatedByCountryAndEmbeddingWithin(String country, Vector embedding,
Score distance);
}
SearchResults<Comment> results = repository.searchByCountryAndEmbeddingNear("en", Vector.of(…), Score.of(0.9), Limit.of(10));
在此示例中,searchByCountryAndEmbeddingNear 方法返回一個 SearchResults<Comment> 物件,其中包含一個 SearchResult<Comment> 例項列表。每個結果都包括匹配的 Comment 實體及其相關性得分。
相關性分數是一個數值,表示匹配向量與查詢向量的匹配程度。根據分數是表示距離還是相似度,更高的分數可能意味著更接近的匹配或更遠的匹配。
用於計算此分數的評分函式可以根據底層資料庫、索引或輸入引數而變化。
分數、相似度和評分函式
Score 型別儲存一個數值,表示搜尋結果的相關性。它可用於根據搜尋結果與查詢向量的相似性對結果進行排名。Score 型別通常是浮點數,其解釋(越高越好或越低越好)取決於所使用的特定相似性函式。分數是向量搜尋的副產品,並非成功搜尋操作所必需。分數值不屬於域模型,因此最好表示為帶外資料。
通常,分數由 ScoringFunction 計算。用於計算此分數的實際評分函式可能取決於底層資料庫,並且可以從搜尋索引或輸入引數中獲取。
Spring Data 支援為常用函式宣告常量,例如
- 歐幾里得距離
-
計算 n 維空間中的直線距離,涉及平方差之和的平方根。
- 餘弦相似度
-
透過首先計算點積,然後將其結果除以它們的長度之積進行歸一化來測量兩個向量之間的角度。
- 點積
-
計算元素乘法之和。
相似性函式的選擇會影響搜尋的效能和語義,並且通常由所使用的底層資料庫或索引決定。Spring Data 適應資料庫的本機評分函式功能以及分數是否可用於限制結果。
Hibernate 將距離函式呼叫轉換為 PGvector 和 Oracle 的原生資料庫函式。其結果通常是距離。當使用 Similarity 而不是 Score 時,Spring Data 將距離分數歸一化為 0 到 1 之間的相似度分數。分數越高,兩個向量越相似。
Score 和 Similarityinterface CommentRepository extends Repository<Comment, String> {
SearchResults<Comment> searchByEmbeddingNear(Vector vector, ScoringFunction function);
SearchResults<Comment> searchByEmbeddingNear(Vector vector, Score score);
SearchResults<Comment> searchByEmbeddingNear(Vector vector, Similarity similarity);
SearchResults<Comment> searchByEmbeddingNear(Vector vector, Range<Similarity> range);
}
repository.searchByEmbeddingNear(Vector.of(…), ScoringFunction.cosine()); (1)
repository.searchByEmbeddingNear(Vector.of(…), Score.of(0.9, ScoringFunction.cosine())); (2)
repository.searchByEmbeddingNear(Vector.of(…), Similarity.of(0.9, ScoringFunction.cosine())); (3)
repository.searchByEmbeddingNear(Vector.of(…), Similarity.between(0.5, 1, ScoringFunction.euclidean()));(4)
| 1 | 執行搜尋並使用餘弦評分返回與給定 Vector 相似的結果。 |
| 2 | 執行搜尋並使用餘弦距離返回得分為 0.9 或更小的結果。 |
| 3 | 執行搜尋並將分數歸一化為相似度值。使用餘弦評分返回相似度為 0.9 或更大的結果。 |
| 4 | 執行搜尋並將分數歸一化為相似度值。使用歐幾里得評分返回相似度介於 0.5 和 1.0 或更大的結果。 |
JPA 要求在建立 Score 或 Similarity 例項時提供 ScoringFunction 以選擇評分函式。 |
向量搜尋方法
向量搜尋方法使用與標準 Spring Data 查詢方法相同的約定在儲存庫中定義。這些方法返回 SearchResults<T> 並需要一個 Vector 引數來定義查詢向量。實際實現取決於底層資料儲存的實際內部結構及其向量搜尋功能。
| 如果您是 Spring Data 儲存庫的新手,請務必熟悉儲存庫定義和查詢方法的基礎知識。 |
通常,您可以選擇使用兩種方法宣告搜尋方法
-
查詢派生
-
宣告基於字串的查詢
向量搜尋方法必須宣告一個 Vector 引數來定義查詢向量。
派生搜尋方法
派生搜尋方法使用方法名來派生查詢。向量搜尋支援以下關鍵詞在宣告搜尋方法時執行向量搜尋
| 邏輯關鍵字 | 關鍵字表達式 |
|---|---|
|
|
|
|
Near 和 Within 關鍵詞interface CommentRepository extends Repository<Comment, String> {
SearchResults<Comment> searchByEmbeddingNear(Vector vector, Score score);
SearchResults<Comment> searchByEmbeddingWithin(Vector vector, Range<Similarity> range);
SearchResults<Comment> searchByCountryAndEmbeddingWithin(String country, Vector vector, Range<Similarity> range);
}
派生搜尋方法可以在域模型屬性和向量引數上宣告謂詞。
派生搜尋方法通常更容易閱讀和維護,因為它們依賴方法名來表達查詢意圖。但是,派生搜尋方法需要宣告一個 Score、Range<Score> 或 ScoreFunction 作為 Near/Within 關鍵詞的第二個引數,以透過分數限制搜尋結果。
註解搜尋方法
註解方法提供對查詢語義和引數的完全控制。與派生方法不同,它們不依賴於方法名約定。
帶註解的搜尋方法必須定義完整的 JPQL 查詢才能執行向量搜尋。
@Query 搜尋方法interface CommentRepository extends Repository<Comment, String> {
@Query("""
SELECT c, cosine_distance(c.embedding, :embedding) as distance FROM Comment c
WHERE c.country = ?1
AND cosine_distance(c.embedding, :embedding) <= :distance
ORDER BY distance asc""")
SearchResults<Comment> searchAnnotatedByCountryAndEmbeddingWithin(String country, Vector embedding,
Score distance);
@Query("""
SELECT c FROM Comment c
WHERE c.country = ?1
AND cosine_distance(c.embedding, :embedding) <= :distance
ORDER BY cosine_distance(c.embedding, :embedding) asc""")
List<Comment> findAnnotatedByCountryAndEmbeddingWithin(String country, Vector embedding, Score distance);
}
向量搜尋方法不需要在其投影中包含分數或距離。當使用返回 SearchResults 的帶註解搜尋方法時,執行機制假定如果存在第二個投影列,則該列包含分數。
透過對實際查詢的更多控制,Spring Data 可以對查詢及其引數做出更少的假設。例如,Similarity 歸一化使用查詢中的原生評分函式將給定相似度歸一化為評分謂詞值,反之亦然。如果註解查詢未定義,例如,分數,則返回的 SearchResult<T> 中的分數將為零。
排序
預設情況下,搜尋結果按其分數排序。您可以使用 Sort 引數覆蓋排序。
Sortinterface CommentRepository extends Repository<Comment, String> {
SearchResults<Comment> searchByEmbeddingNearOrderByCountry(Vector vector, Score score);
SearchResults<Comment> searchByEmbeddingWithin(Vector vector, Score score, Sort sort);
}
請注意,自定義排序不允許將分數表示為排序條件。您只能引用域屬性。