从 RAG 检索效率瓶颈,到 HNSW 算法的深度解密
深入探索 Elasticsearch 中 HNSW 算法的核心原理,从 RAG 检索效率瓶颈出发,详解分层导航小世界网络的构建与查询机制。
从 RAG 检索效率瓶颈,到 HNSW 算法的深度解密
引言:好久不见,聊聊一次群聊引发的思考
嗨,大家好!好久没写博客了,最近一直在忙项目,但我的学习热情可没有减退。
前几天,在技术交流群里,大家讨论起了 Elasticsearch 中 HNSW 算法的优化问题。这一聊,瞬间把我拉回了去年做的 RAG(检索增强生成)POC 项目。当时,为了实现多路召回,我选择了 Elasticsearch 8.0+,因为它既支持关键词检索,又能做向量检索,免去了引入过多组件的麻烦。
然而,在测试阶段,我很快遇到了一个效率上的瓶颈。当向量数据量逐步增长时,KNN(K-Nearest Neighbor)检索的性能开始急剧下降。我意识到,要解决这个难题,我必须深入理解 Elasticsearch 背后使用的核心算法——HNSW(Hierarchical Navigable Small World)。
这次群里的讨论让我觉得,这段经历很有分享的价值。所以,我整理了当时的学习笔记和实践心得,希望能帮助到同样在做向量检索优化的朋友们。
HNSW 核心思想:从“暴力检索”到“分层导航”
在深入细节之前,让我们先理解 HNSW 的核心思想。
传统的 KNN 检索,就像是在一个巨大的图书馆里,一本本、一排排地寻找你想要的书,这被称为“暴力检索”。当数据量巨大时,这种方法几乎无法使用。
而 HNSW 算法就像是给这个图书馆创建了一个高效的“索引系统”:
- “小世界”网络: HNSW 建立了一个网络,让任意两点之间都能通过很少的步骤连接起来,就像社交网络中的“六度分隔理论”一样。
- “分层”结构: 这个网络不是平铺的,而是分层的。你可以把它想象成一个城市的地图:最高层是高速公路网,中间是主干道,最底层是具体的街道。
当我们需要检索时,我们从最高层开始,快速缩小搜索范围,然后逐层向下,最终在最底层的“街道”上找到最精确的结果。通过这种方式,HNSW 实现了在保证高准确性的同时,极大地提升了搜索速度。
深入 HNSW 内部:图的构建与查询
如果你和我一样好奇,HNSW 的“分层导航”到底是如何实现的?那这个部分就是为你准备的。我们将揭开 HNSW 图构建和查询的神秘面纱。
新节点如何加入图?
当你向 Elasticsearch 索引一个新文档(即一个新向量)时,HNSW 图的构建过程就开始了。这个过程就像是给社区添一个新邻居:
- 确定“楼层”: 算法会给新节点随机分配一个最高层级。这个分配是基于概率的,保证越低的层级,节点数量越多。低层级是容纳所有数据的“街道”,而高层级是数量稀少的“高速公路”。
- 寻找“最佳邻居”: 从图的最高层开始,算法会进行搜索,找到新节点在当前层级的最佳邻居。这个搜索的广度由
efConstruction参数控制。 - 建立“社交圈”: 找到最佳邻居后,算法会与最近的
M个邻居建立双向连接。这个过程会逐层重复,直到到达新节点被分配的最高层。
通过这个过程,新节点被高效地插入到图中,并建立起“捷径”,确保它能被快速找到。
一次查询是如何进行的?
当你提交一个查询向量时,HNSW 会利用已建好的图,以极快的速度找到最相似的结果。这个过程可以被分解成几个关键步骤:
- 确定“出发点”: 算法会从一个固定的、预先确定的入口点开始搜索。这个入口点是整个图的“起点”。
- 逐层“导航”: 算法会从最高层开始,利用查询向量与节点的距离,进行贪心搜索。它会从当前位置的邻居中,找到最接近查询向量的节点,并一步步朝目标方向移动。
- “深入小巷”: 这个过程会逐层向下进行。在每一层,算法都会找到一个更接近目标的节点,并把它作为下一层的搜索入口。这个搜索的广度由
efSearch参数控制。 - 找到“宝藏”: 最终,当搜索到达最底层的“街道”时,算法会返回与查询向量最相似的 K 个结果。
通过这种“层层递进”的导航方式,HNSW 避免了暴力检索的巨大开销,实现了高效的近似最近邻搜索。
解密 HNSW 的三个关键参数
在 Elasticsearch 中,HNSW 的性能和准确性主要由三个关键参数决定。理解它们,你就掌握了调优的“钥匙”。
M:每个节点的最大连接数- 作用:
M控制了每个节点在构建图时可以连接的邻居数量。 - 权衡:
M值越大,图的连接越密集,搜索路径选择越多,准确性越高;但同时,索引占用的内存和构建时间也越大。
- 作用:
efConstruction:构建时的候选池大小- 作用:
efConstruction控制了在索引构建时,算法寻找最佳邻居的广度。 - 权衡:
efConstruction值越大,构建时的搜索越彻底,生成的图质量越高,搜索准确性越高;但相应地,索引构建时间也会显著增加。
- 作用:
efSearch:搜索时的候选池大小- 作用:
efSearch控制了在搜索查询时,算法在每一层进行贪心搜索的广度。 - 权衡:
efSearch值越大,搜索的广度越大,召回率(准确性)越高;但搜索时间也会相应增加。
- 作用:
我的实践心得与调优之旅
理解了这些参数后,我开始对我的 Elasticsearch 集群进行调优。
最初,为了追求快速索引,我把 M 和 efConstruction 设置得比较小。结果是索引速度很快,但查询的召回率很低,很多相关的结果都未能被检索到。
后来,我根据业务需求重新进行了权衡:我的 RAG 系统对语义检索的准确性要求很高,宁可牺牲一些索引速度,也要保证检索质量。于是,我逐步增大了 M 和 efConstruction 的值。
这个权衡过程,让我联想到了在其他业务场景中的案例:
- 电商商品检索: 在电商平台中,我们需要根据用户的模糊查询(如“红色复古连衣裙”)快速找到最相似的商品。这对搜索的召回率和准确性要求很高。在这种场景下,我们通常会设置相对较大的
M和efConstruction,以保证搜索结果的相关性,即使这意味着更长的索引构建时间。 - 视频推荐系统: 对于像 YouTube 这样的视频平台,每天都有海量的新视频上传,实时索引的效率至关重要。同时,用户对推荐的“相关性”也有很高要求。这种场景下,我们通常会找到一个平衡点,可能会略微牺牲一些召回率,将
efConstruction设置得适中,以确保索引速度能够跟上数据增长。
最终,在经过多次测试后,我找到了一个平衡点。在保证搜索延迟在可接受范围内的同时,我的召回率得到了显著提升,RAG 系统的效果也随之改善。
这个过程让我深刻体会到:没有完美的参数配置,只有最适合你业务需求的权衡。
结语:HNSW 不仅仅是算法,更是工程智慧
通过这次深入学习,我不仅解决了 RAG 项目中的一个关键效率问题,更对高维向量检索技术有了全新的认识。HNSW 算法本身就是一种工程智慧的结晶,它巧妙地在速度、准确性和资源消耗之间找到了平衡。
希望我的这段经历也能对你有所启发。如果你也在使用向量检索,不妨花点时间去了解它背后的算法原理,相信这会让你对自己的系统有更深的掌控。