基本向量搜索
近似近邻(ANN)搜索以记录向量嵌入排序顺序的索引文件为基础,根据接收到的搜索请求中携带的查询向量查找向量嵌入子集,将查询向量与子群中的向量进行比较,并返回最相似的结果。通过 ANN 搜索,Milvus 提供了高效的搜索体验。
概述
ANN 和 k-Nearest Neighbors (kNN) 搜索是向量相似性搜索的常用方法。在 kNN 搜索中,必须将向量空间中的所有向量与搜索请求中携带的查询向量进行比较,然后找出最相似的向量,这既耗时又耗费资源。
与 kNN 搜索不同,ANN 搜索算法要求提供一个索引文件,记录向量 Embeddings 的排序顺序。当收到搜索请求时,可以使用索引文件作为参考,快速找到可能包含与查询向量最相似的向量嵌入的子组。然后,你可以使用指定的度量类型来测量查询向量与子组中的向量之间的相似度,根据与查询向量的相似度对组成员进行排序,并找出前 K 个组成员。
ANN 搜索依赖于预建索引,搜索吞吐量、内存使用量和搜索正确性可能会因选择的索引类型而不同。您需要在搜索性能和正确性之间取得平衡。
为了减少学习曲线,Milvus 提供了AUTOINDEX。通过AUTOINDEX,Milvus 可以在建立索引的同时分析 Collections 中的数据分布,并根据分析结果设置最优化的索引参数,从而在搜索性能和正确性之间取得平衡。
单向量搜索
在 ANN 搜索中,单向量搜索指的是只涉及一个查询向量的搜索。根据预建索引和搜索请求中携带的度量类型,Milvus 将找到与查询向量最相似的前 K 个向量。
本节将介绍如何进行单向量搜索。代码片段假定您已经以快速设置的方式创建了一个 Collections。搜索请求携带单个查询向量,并要求 Milvus 使用内积(IP)计算查询向量与集合中向量的相似度,并返回三个最相似的向量。
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp;
import java.util.*;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.token("root:Milvus")
.build());
FloatVec queryVector = new FloatVec(new float[]{0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f});
SearchReq searchReq = SearchReq.builder()
.collectionName("quick_setup")
.data(Collections.singletonList(queryVector))
.topK(3)
.build();
SearchResp searchResp = client.search(searchReq);
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
System.out.println("TopK results:");
for (SearchResp.SearchResult result : results) {
System.out.println(result);
}
}
// Output
// TopK results:
// SearchResp.SearchResult(entity={}, score=0.95944905, id=5)
// SearchResp.SearchResult(entity={}, score=0.8689616, id=1)
// SearchResp.SearchResult(entity={}, score=0.866088, id=7)
Milvus 根据搜索结果与查询向量的相似度得分从高到低排列搜索结果。相似度得分也称为与查询向量的距离,其值范围随使用的度量类型而变化。
下表列出了适用的度量类型和相应的距离范围。
度量类型 | 特征 | 距离范围 |
---|---|---|
| 值越小表示相似度越高。 | [0, ∞) |
| 数值越大,表示相似度越高。 | [-1, 1] |
| 数值越大,表示相似度越高。 | [-1, 1] |
| 值越小,表示相似度越高。 | [0, 1] |
| 值越小,表示相似度越高。 | [0,dim(向量)] 批量向量搜索 |
批量向量搜索
同样,您也可以在一个搜索请求中包含多个查询向量。Milvus 将并行对查询向量进行 ANN 搜索,并返回两组结果。
import io.milvus.v2.service.vector.request.SearchReq
import io.milvus.v2.service.vector.request.data.BaseVector;
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp
List<BaseVector> queryVectors = Arrays.asList(
new FloatVec(new float[]{0.041732933f, 0.013779674f, -0.027564144f, -0.013061441f, 0.009748648f}),
new FloatVec(new float[]{0.0039737443f, 0.003020432f, -0.0006188639f, 0.03913546f, -0.00089768134f})
);
SearchReq searchReq = SearchReq.builder()
.collectionName("quick_setup")
.data(queryVectors)
.topK(3)
.build();
SearchResp searchResp = client.search(searchReq);
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
System.out.println("TopK results:");
for (SearchResp.SearchResult result : results) {
System.out.println(result);
}
}
// Output
// TopK results:
// SearchResp.SearchResult(entity={}, score=0.49548206, id=1)
// SearchResp.SearchResult(entity={}, score=0.320147, id=3)
// SearchResp.SearchResult(entity={}, score=0.107413776, id=6)
// TopK results:
// SearchResp.SearchResult(entity={}, score=0.5678123, id=6)
// SearchResp.SearchResult(entity={}, score=0.32368967, id=2)
// SearchResp.SearchResult(entity={}, score=0.24108477, id=3)
在分区中进行 ANN 搜索
假设您在一个 Collections 中创建了多个分区,您可以将搜索范围缩小到特定数量的分区。在这种情况下,您可以在搜索请求中包含目标分区名称,将搜索范围限制在指定的分区内。减少搜索所涉及的分区数量可以提高搜索性能。
下面的代码片段假定在你的 Collections 中有一个名为PartitionA的分区。
import io.milvus.v2.service.vector.request.SearchReq
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp
FloatVec queryVector = new FloatVec(new float[]{0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f});
SearchReq searchReq = SearchReq.builder()
.collectionName("quick_setup")
.partitionNames(Collections.singletonList("partitionA"))
.data(Collections.singletonList(queryVector))
.topK(3)
.build();
SearchResp searchResp = client.search(searchReq);
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
System.out.println("TopK results:");
for (SearchResp.SearchResult result : results) {
System.out.println(result);
}
}
// Output
// TopK results:
// SearchResp.SearchResult(entity={}, score=0.6395302, id=13)
// SearchResp.SearchResult(entity={}, score=0.5408028, id=12)
// SearchResp.SearchResult(entity={}, score=0.49696884, id=17)
使用输出字段
在搜索结果中,Milvus 默认会包含包含 TOP-K 向量嵌入的实体的主字段值和相似度距离/分数。您可以在搜索请求中包含目标字段(包括向量和标量字段)的名称作为输出字段,使搜索结果携带这些实体中其他字段的值。
import io.milvus.v2.service.vector.request.SearchReq
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp
FloatVec queryVector = new FloatVec(new float[]{0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f});
SearchReq searchReq = SearchReq.builder()
.collectionName("quick_setup")
.data(Collections.singletonList(queryVector))
.topK(3)
.outputFields(Collections.singletonList("color"))
.build();
SearchResp searchResp = client.search(searchReq);
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
System.out.println("TopK results:");
for (SearchResp.SearchResult result : results) {
System.out.println(result);
}
}
// Output
// TopK results:
// SearchResp.SearchResult(entity={color=black_9955}, score=0.95944905, id=5)
// SearchResp.SearchResult(entity={color=red_7319}, score=0.8689616, id=1)
// SearchResp.SearchResult(entity={color=white_5015}, score=0.866088, id=7)
使用限制和偏移
您可能会注意到,搜索请求中包含的参数limit
决定了搜索结果中包含的实体数量。该参数指定了单次搜索中返回实体的最大数量,通常称为top-K。
如果希望执行分页查询,可以使用循环来发送多个搜索请求,每个查询请求中都包含Limit和Offset参数。具体来说,可以将 "限制 "参数设置为希望包含在当前查询结果中的实体数量,将 "偏移"参数设置为已经返回的实体总数。
下表概述了在一次返回 100 个 "实体 "时,如何为分页查询设置 "限制"和"偏移"参数。
查询 | 每次查询要返回的实体 | 已返回实体总数 |
---|---|---|
第 1 次查询 | 100 | 0 |
第二次查询 | 100 | 100 |
第三次查询 | 100 | 200 |
第 n 次查询 | 100 | 100 x (n-1) |
请注意,在一次 ANN 搜索中,limit
和offset
的总和应小于 16 384。
import io.milvus.v2.service.vector.request.SearchReq
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp
FloatVec queryVector = new FloatVec(new float[]{0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f});
SearchReq searchReq = SearchReq.builder()
.collectionName("quick_setup")
.data(Collections.singletonList(queryVector))
.topK(3)
.offset(10)
.build();
SearchResp searchResp = client.search(searchReq);
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
System.out.println("TopK results:");
for (SearchResp.SearchResult result : results) {
System.out.println(result);
}
}
// Output
// TopK results:
// SearchResp.SearchResult(entity={}, score=0.24120237, id=16)
// SearchResp.SearchResult(entity={}, score=0.22559784, id=9)
// SearchResp.SearchResult(entity={}, score=-0.09906838, id=2)
增强 ANN 搜索
AUTOINDEX 可大大拉平 ANN 搜索的学习曲线。然而,随着 Top-K 的增加,搜索结果不一定总是正确的。通过缩小搜索范围、提高搜索结果相关性和搜索结果多样化,Milvus 实现了以下搜索增强功能。
-
过滤搜索
您可以在搜索请求中包含过滤条件,这样 Milvus 就会在进行 ANN 搜索前进行元数据过滤,将搜索范围从整个 Collections 缩小到只搜索符合指定过滤条件的实体。
-
范围搜索
您可以将返回实体的距离或得分限制在特定范围内,从而提高搜索结果的相关性。在 Milvus 中,范围搜索包括以与查询向量最相似的嵌入向量为中心画两个同心圆。搜索请求指定了两个圆的半径,Milvus 会返回所有属于外圆但不属于内圆的向量嵌入。
有关范围搜索的更多信息,请参阅范围搜索。
-
分组搜索
如果返回的实体在特定字段中持有相同的值,搜索结果可能无法代表向量空间中所有向量嵌入的分布情况。要使搜索结果多样化,可以考虑使用分组搜索。
有关分组搜索的更多信息,请参阅分组搜索、
-
混合搜索
一个 Collections 最多可以包含四个向量场,以保存使用不同嵌入模型生成的向量嵌入。通过这种方式,可以使用混合搜索对这些向量场的搜索结果进行 Rerankers,从而提高召回率。
有关混合搜索的更多信息,请参阅混合搜索。
-
搜索迭代器
单个 ANN 搜索最多可返回 16,384 个实体。如果需要在单次搜索中返回更多实体,请考虑使用搜索迭代器。
有关搜索迭代器的详细信息,请参阅搜索迭代器。
-
全文搜索
全文搜索是一种在文本数据集中检索包含特定术语或短语的文档,然后根据相关性对结果进行排序的功能。该功能克服了语义搜索的局限性(语义搜索可能会忽略精确的术语),确保您获得最准确且与上下文最相关的结果。此外,它还能接受原始文本输入,自动将文本数据转换为稀疏嵌入,无需手动生成向量嵌入,从而简化了向量搜索。
有关全文搜索的详细信息,请参阅全文搜索。
-
关键词匹配
Milvus 的关键词匹配功能可根据特定术语精确检索文档。该功能主要用于满足特定条件的过滤搜索,并可结合标量过滤来完善查询结果,允许在符合标量标准的向量内进行相似性搜索。
有关关键字匹配的详细信息,请参阅关键字匹配。
-
使用 Partition Key
在元数据过滤中涉及多个标量字段并使用相当复杂的过滤条件可能会影响搜索效率。一旦将一个标量字段设置为分区关键字,并在搜索请求中使用涉及分区关键字的过滤条件,就可以帮助将搜索范围限制在与指定分区关键字值相对应的分区内。
有关分区键的详细信息,请参阅使用分区键。
-
使用 mmap
有关 mmap 设置的详情,请参阅使用 mmap。
-
聚类压缩
有关聚类压缩的详情,请参阅聚类压缩。