向量搜索
随着生成式人工智能的兴起,矢量数据库在数据库领域获得了强劲的关注。 这些数据库能够高效存储和查询高维向量,非常适合语义搜索、推荐系统和自然语言理解等任务。
向量搜索是一种通过比较向量表示(也称为嵌入)来检索语义相似数据的技术,而非依赖传统的精确匹配查询。 这种方法使得智能、上下文感知的应用能够超越基于关键词的检索。
在Spring Data的背景下,矢量搜索为构建智能、上下文感知应用开辟了新可能,尤其是在自然语言处理、推荐系统和生成式人工智能等领域。 通过使用熟悉的仓库抽象建模基于向量的查询,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}'
}
虽然你可以使用向量作为查询类型,你不能在你的领域模型中使用,因为休眠需要浮点数组或双数组作为向量类型。 |
向量模型
为了以类型安全且习惯性的方式支持向量搜索,Spring Data 引入了以下核心抽象:
向量
这向量类型表示一种n维数值嵌入,通常由嵌入模型生成。
在 Spring Data 中,它被定义为围绕浮点数数组的轻量级包装器,确保不可变性和一致性。
这种类型可以作为搜索查询的输入,也可以作为领域实体上的属性来存储相关的向量表示。
Vector vector = Vector.of(0.23f, 0.11f, 0.77f);
用向量在你的领域中,模型消除了处理原始数组或数字列表的需求,提供了一种更安全、更具表达力的向量数据处理方式。
这种抽象还便于与各种矢量数据库和库的集成。
它还允许实现厂商特定的优化,如不映射到标准浮点的二进制或量化矢量(浮和双截至IEEE 754)表示。
域对象可以具有向量属性,可用于相似性搜索。
请考虑以下例子:
class Comment {
@Id String id;
String country;
String comment;
@Column(name = "the_embedding")
@JdbcTypeCode(SqlTypes.VECTOR)
@Array(length = 5)
Vector embedding;
// getters, setters, …
}
| 将向量与域对象关联后,向量会被加载并存储为实体生命周期的一部分,这可能会增加检索和持久化作的额外开销。 |
搜索结果
这搜索结果<T>类型封装了向量相似性查询的结果。
它包含匹配的领域对象和一个相关性评分,表示其与查询向量的匹配程度。
这种抽象提供了结构化的结果排序处理方式,使开发者能够轻松处理数据及其上下文相关性。
搜索结果<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));
在这个例子中,按国家和嵌入搜索方法返回搜索结果<评论>该对象包含一个列表搜索结果<评论>实例。
每个结果都包含匹配的评论实体及其相关性评分。
相关性分数是一个数值,表示匹配的向量与查询向量的对应程度。 根据分数代表距离还是相似性,分数越高可能意味着匹配越近,也可能表示距离较远。
用于计算该得分的评分函数可能因底层数据库、索引或输入参数而异。
评分、相似度与评分功能
这得分类型包含一个数值,表示搜索结果的相关性。
它可以根据结果与查询向量的相似度对结果进行排名。
这得分类型通常是浮点数,其解释(越高越好或越低越好)取决于所用的具体相似度函数。
分数是向量搜索的副产品,并非成功搜索作的必要条件。
得分值不属于领域模型,因此最好以带外数据形式表示。
通常,分数由评分函数.
用于计算该分数的实际评分函数可能取决于底层数据库,可通过搜索索引或输入参数获得。
Spring Data 支持为常用函数声明常量,例如:
- 欧几里得距离
-
计算n维空间中的直线距离,涉及平方差分和的平方根。
- 余弦相似性
-
先计算点积,然后再除以两向量长度的乘积,测量两个向量之间的角度。
- 点积
-
计算元素乘法的总和。
相似度函数的选择会影响搜索的性能和语义,通常取决于所使用的底层数据库或索引。 Spring Data 采用了数据库的原生评分功能,并决定评分是否可用于限制结果。
Hibernate 将距离函数调用转换为 PGvector 和 Oracle 的本地数据库函数。
它们的结果通常是距离。
使用相似而不是得分春季数据将距离分数归一化为0到1之间的相似度。分数越高,两个向量的相似度越高。
得分和相似在仓库检索方法interface 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 | 运行搜索并返回与给出相似结果向量应用余弦计分。 |
| 2 | 进行搜索并返回评分为0.9或用余弦距离更小。 |
| 3 | 进行搜索,将分数归一化为相似度值。
返回结果具有相似性0.9或使用余弦计分更大。 |
| 4 | 进行搜索,将分数归一化为相似度值。
返回结果具有相似性0.5和1.0或使用欧几里得计分法更高。 |
JPA要求评分函数在创作时提供得分或相似选择评分函数的实例。 |
向量搜索方法
向量搜索方法在仓库中定义,采用与标准 Spring Data 查询方法相同的约定。
这些方法返回搜索结果<T>并且需要一个向量参数用于定义查询向量。
实际实现取决于底层数据存储的内部结构及其在矢量搜索方面的能力。
| 如果你是 Spring Data 仓库的新手,务必熟悉仓库定义和查询方法的基础知识。 |
通常,你可以选择两种方法声明搜索方法:
-
查询推导
-
声明基于字符串的查询
向量搜索方法必须声明向量参数用于定义查询向量。
派生搜索方法
派生搜索方法使用方法名称来推导查询。 矢量搜索支持以下关键词,在声明搜索方法时运行矢量搜索:
| 逻辑关键词 | 关键词表达 |
|---|---|
|
|
|
|
近和在仓库搜索方法中的关键词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);
}
派生搜索方法可以声明域模型属性和向量参数的谓词。
导出搜索方法通常更易阅读和维护,因为它们依赖方法名称来表达查询意图。
然而,派生搜索方法要求声明得分,范围<评分>或ScoreFunction作为近/在关键词通过分数限制搜索结果。
注释搜索方法
带注释的方法可以完全控制查询语义和参数。 与派生方法不同,它们不依赖方法名惯例。
带注释的搜索方法必须定义整个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);
}
向量搜索方法不要求在投影中包含分数或距离。
使用带注释的搜索方法时返回搜索结果,执行机制假设如果存在第二列投影,该列保持得分值。
通过对实际查询的更多控制,Spring Data 可以减少对查询及其参数的假设。
例如相似归一化使用查询中的原生评分函数将给定相似度归一化为分数谓词值,反之亦然。
如果带注释的查询没有定义例如分数,则返回的分数值搜索结果<T>将为零。
排序
默认情况下,搜索结果按分数排序。
你可以通过使用排序参数:
排序在仓库检索方法interface CommentRepository extends Repository<Comment, String> {
SearchResults<Comment> searchByEmbeddingNearOrderByCountry(Vector vector, Score score);
SearchResults<Comment> searchByEmbeddingWithin(Vector vector, Score score, Sort sort);
}
请注意,自定义排序不允许将分数作为排序标准表示。 你只能引用域名属性。