赞
踩
kNN搜索是通过相似度判断来根据查询向量查找K个邻近的向量
使用kNN的场景有:
1、基于自然语言处理(NLP)算法的相关性排序
2、产品推荐和推荐引擎
3、图片或视频的相似搜索
1、为了运行kNN搜索,我们需要把我们的数据转换成有意义的向量值。然后把向量值添加到文档的dense_vector类型的字段里面。查询向量也需要有相同的维度
2、基于相似度来设计我们的向量,使与查询向量越接近的文档的向量其匹配越好
ES支持两种kNN搜索的方法:
1、近似kNN:使用kNN搜索API
2、精确,暴力kNN:利用向量函数使用script_score查询
多数情况下,我们只需要使用近似kNN。近似kNN提供低延迟是以较慢的索引速度和不完美的准确性为代价。
精确的暴力kNN保证精确结果但是不能很好地适应大型数据集。通过这种方式,script_score查询必须扫描每个匹配的文档节点来计算向量函数,这将导师搜索非常慢。然而我们可以通过限制传给向量函数的文档数量来降低延迟。如果我们能筛选出一个很小的文档子集,那么我们就可以通过这个方法获取较好的性能。
这个功能是技术预研功能,未来可能后续会改变或者移除,使用的时候请注意。
为了使用近似kNN搜索,需要使用kNN搜索api来搜索已经被索引的dense_vector字段
1、明确映射一个或多个dense_vector字段,近似kNN搜索要求下列mapping选项需要设置:
- PUT my-approx-knn-index
- {
- "mappings": {
- "properties": {
- "my-image-vector": {
- "type": "dense_vector",
- "dims": 5,
- "index": true,
- "similarity": "l2_norm"
- },
- "my-tag": {
- "type": "keyword"
- }
- }
- }
- }

2、添加文档数据,进行索引
- POST my-approx-knn-index/_bulk?refresh=true
- { "index": { "_id": "1" } }
- { "my-image-vector": [230.0, 300.33, -34.8988, 15.555, -200.0], "my-tag": "cow.jpg" }
- { "index": { "_id": "2" } }
- { "my-image-vector": [-0.5, 100.0, -13.0, 14.8, -156.0], "my-tag": "moose.jpg" }
- { "index": { "_id": "3" } }
- { "my-image-vector": [0.5, 111.3, -13.0, 14.8, -156.0], "my-tag": "rabbit.jpg" }
3、通过kNN搜索api执行搜索
- GET my-approx-knn-index/_knn_search
- {
- "knn": {
- "field": "my-image-vector",
- "query_vector": [-0.5, 90.0, -10, 14.8, -156.0],
- "k": 10,
- "num_candidates": 100
- },
- "fields": [
- "my-image-vector",
- "my-tag"
- ]
- }
备注:近似kNN搜索是在8.0版本新增的,在此之前,dense_vector字段不支持在mapping里面设置index=true。如果我们在8.0版本之前创建的索引包含dense_vector字段,为了支持近似kNN搜索,需要重建索引并且设置新字段的mapping中index=true。
为了收集结果,kNN搜索api在每个分片上查找一定数量(num_candidates
)的近似最近邻候选对象。搜索计算候选向量与查询向量的相似度,从每个分片选择k个最相似的结果。搜索最终拿到每个分片返回的结果然后找到最终的k个邻近结果。
我们可以通过调大num_candidates的值来更精确的获取结果,代价是搜索的速度会变慢。如果使用一个比较大的num_candidates值会从每个分片获取更多的候选者。这就要花费更多的时间,也就有更大的可能找到真正的k个邻近结果。
类似的,我们可以降低num_candidates的值来获取更快的搜索,同时要接受潜在不太精确的结果。
ES的每个分片都是由segment组成,索引数据存储在segment里面。为了实现近似搜索,ES在segment里面把密集向量的值当做HNSW图来存储。索引近似kNN搜索的向量之所以花费大量的时间是因为构建这个图的成本很高。我们需要增加客户端的索引请求超时时间并使用bulk请求。
强制索引segment合并为一个可以改善kNN索引的延迟,当索引只有一个segment的时候,搜索只需要检测一个包括所有的HNSW图。当存在多个segment时,kNN搜索每一个segment来检测每一个小的HNSW图。我们应该在索引没有不再写入数据的时候强制合并segment。
为了使用精确kNN搜索,我们需要使用带向量函数的script_score查询
1、明确的定义一个或多个dense_vector字段,如果我们不打算使用近似kNN搜索,可以忽略index属性的设置或者设置为false。这将会明显改善索引速度。
- PUT my-exact-knn-index
- {
- "mappings": {
- "properties": {
- "my-product-vector": {
- "type": "dense_vector",
- "dims": 5,
- "index": false
- },
- "my-price": {
- "type": "long"
- }
- }
- }
- }
2、索引我们的数据
- POST my-exact-knn-index/_bulk?refresh=true
- { "index": { "_id": "1" } }
- { "my-product-vector": [230.0, 300.33, -34.8988, 15.555, -200.0], "my-price": 1599 }
- { "index": { "_id": "2" } }
- { "my-product-vector": [-0.5, 100.0, -13.0, 14.8, -156.0], "my-price": 799 }
- { "index": { "_id": "3" } }
- { "my-product-vector": [0.5, 111.3, -13.0, 14.8, -156.0], "my-price": 1099 }
- ...
3、使用搜索api来运行带有向量函数的的script_score查询
- GET my-exact-knn-index/_search
- {
- "query": {
- "script_score": {
- "query" : {
- "bool" : {
- "filter" : {
- "range" : {
- "my-price" : {
- "gte": 1000
- }
- }
- }
- }
- },
- "script": {
- "source": "cosineSimilarity(params.queryVector, 'my-product-vector') + 1.0",
- "params": {
- "queryVector": [-0.5, 90.0, -10, 14.8, -156.0]
- }
- }
- }
- }
- }

备注:为了限制传递给向量函数的匹配到的文档数量,最好在script_score.query参数增加一个filter查询。如果需要,我们可以在参数里面使用match_all query来匹配所有的文档。然而,匹配所有文档会显著增加搜索延迟
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。