HNSW 靠 “顶层粗跳缩小范围,底层细搜锁定答案” 的方式,用少量跳跃就找到近似最近邻,这正是它比暴力全扫快得多的原因
建图(索引构建)、检索(使用索引,查询)
连接点(edge,边)
1. 提供 “跳转路径”(搜索的通道)
查询时,算法沿着边从一个节点跳到另一个节点,每次都跳到与查询向量更相似那个,类似 “贪心下山”。没有边,节点就是孤岛,无法搜索
2. 连接上下层(纵向边 = 层间通道)
边分两类:
- 层内边:同一层里相邻节点之间的连接(横向),负责这一层的精细搜索
- 层间边:上下层之间同一向量对应节点的连接(纵向),查询时靠它从上层逐层下探到底层
搜索流程就是:从顶层入口开始 → 沿层内边粗跳 → 通过层间边下到下一层 → 再沿该层的边细搜 → 一直到底层
3. 决定 “召回精度 vs 成本” 的旋钮(M 参数)
连接点数量由M(每个节点的最大连接数)控制:
- M 大→ 边多,搜索路径更全,召回更准,但内存占用大、建索引慢
- M 小→ 边少,省内存省时间,但可能跳过头,召回下降
它解决什么问题
在海量向量里找 “最相似” 的。暴力全扫最准但太慢,HNSW 用图结构 + 贪心换取 “几乎一样准,但快几个数量级”。
它的数据结构
不是排序数组,不是哈希桶,而是一张多层图(Graph):
- 节点= 一个向量
- 边= “我离这个向量比较近”
- 多层= 底层放全部向量,上层只有少数代表
图上没有全局顺序,每个节点只 “认识” 自己连着的几个邻居。这就是为什么它找答案靠 “看邻居、比较、跳”
建图阶段(一次性的准备工作)
- 每个向量成为节点,连到最近的 M 个邻居
- 每个节点随机决定层数,多数只在底层
- 形成 “底层密、上层稀” 的倒金字塔结构
检索阶段(每次查询要做的事)
- 顶层粗跳:从稀疏的顶层入口,看邻居里谁更近就跳过去 —— 在极少节点里快速锁定 “大概区域”
- 逐层下探:每层到局部最优点后往下一层,重复 “看邻居→跳更近”
- 底层精搜:在最密的底层,只在这一小片区域贪心走几步,走到 “没有邻居比我更近” 就停
- 返回:当前这个局部最优点就是答案(近似最近邻)
它为什么快:整个过程只看 “沿途的一小撮邻居”,从不全库比较。层越多,顶层越稀疏,粗定位越省力
相关参数
M 和 ef_construction 建索引时定好,基本不动
ef_search 是查询时随时可调的旋钮 —— 快就调小,准就调大