向量检索原理入门:从 ANN 到 HNSW

向量检索原理入门:从 ANN 到 HNSW

做 RAG、推荐系统时,百万级向量检索慢得像蜗牛?cosine 和 L2 该用哪个?面试官一问「召回率怎么算」就卡壳?这篇文章用最直观的方式,把向量检索的核心原理讲透,并标注每个考点的面试追问方向。


目录

  1. 为什么不能用暴力搜索?
  2. ANN:用近似换速度
  3. HNSW:分层导航的小世界图
  4. cosine vs L2:方向还是距离?
  5. 参数调优:召回率与延迟的权衡
  6. 面试考点速查表

1. 为什么不能用暴力搜索?

先看最朴素的做法:精确最近邻检索。给定一个查询向量,遍历库里所有向量,算一遍距离,取最近的 k 个。

python 复制代码
import numpy as np

def brute_force(query, vectors, k=10):
    """暴力扫描:O(n) 线性遍历"""
    dists = np.linalg.norm(vectors - query, axis=1)
    idx = np.argsort(dists)[:k]
    return idx, dists[idx]

问题在哪?

  • 复杂度 O(n):100 万条 × 768 维,每次查询要算 100 万次距离,延迟轻松到几十毫秒甚至上百毫秒。
  • 维度灾难(curse of dimensionality):维度越高,向量在高维空间越稀疏,所有点之间的距离趋同,精确检索的「精确」意义也在减弱。

🎯 面试考点 :面试官问「为什么不用暴力搜索」,别只答「太慢」,要说出 O(n) 线性扫描 + 维度灾难 两层原因。


2. ANN:用近似换速度

ANN(Approximate Nearest Neighbor,近似最近邻) 的核心思想:以可接受的精度损失,换取数量级的速度提升

它不保证返回一定 是真正的最近邻,只保证返回足够接近的邻居。

核心指标:召回率 Recall@k

ini 复制代码
Recall@k = ANN 返回的 k 个结果中,有多少比例是真正的 top-k 最近邻

例:精确检索返回 [A,B,C],ANN 返回 [A,C,D],则 recall@3 = 2/3

🎯 面试考点 :ANN 的「近似」不是玄学,是有量化指标的。Recall@k 与延迟的权衡 是工程落地的核心,也是贯穿全文的主线。


3. HNSW:分层导航的小世界图

HNSW(Hierarchical Navigable Small World)是目前最主流的 ANN 算法。拆开看:

3.1 NSW:单层小世界图

NSW 本身是单层 的。它的「小世界」结构借鉴了现实社交网络的六度分隔思想:

  • 每个节点只连接少数几个邻居
  • 但通过少数几步就能到达任意节点

搜索时用贪心算法导航:从入口节点出发,每次走到「比当前更接近目标」的邻居,直到无法再改进。

3.2 HNSW:加一层「跳表」

HNSW 在 NSW 之上加了分层,借鉴**跳表(Skip List)**思想:

  • 高层 :节点少、连接稀疏 → 负责快速跨越长距离
  • 底层 :节点全、连接稠密 → 负责精细定位

搜索时从最高层入口开始,逐层贪心逼近、向下钻取,最后在底层做精细检索。

🎯 面试考点NSW 是单层的,HNSW 才是分层------这是最容易混淆的硬伤。面试官一听就知道你有没有吃透。


4. cosine vs L2:方向还是距离?

4.1 数学定义

公式(竖线在表格里会被误判为分隔符,所以单独放):

text 复制代码
cosine:cos(θ) = (A·B) / (|A| × |B|)
L2:√(Σ(ai-bi)²)
cosine 相似度 L2 距离
衡量 方向一致性 绝对位置差异
范围 -1, 1,越大越相似 [0, +∞),越小越近
对模长 不敏感 敏感

4.2 归一化后:两者等价(核心考点)

若 A、B 都是单位向量(|A|=|B|=1):

css 复制代码
|A-B|² = |A|² + |B|² - 2A·B = 2 - 2cos(A,B)

所以:L2 距离越小 ⟺ cosine 相似度越大,两者排序完全一致。

但注意 :等价的前提是都归一化。不归一化时,一个向量模长很大但方向接近,cosine 高但 L2 远,排序可能完全不同。

🎯 面试考点 :这是本题最核心的考点。很多系统(如 Faiss)先归一化再用 L2,因为 L2 计算更快、效果等价。

4.3 场景怎么选?

判断标准不是「文本用 cosine、聚类用 L2」这种死记,而是问:我关心的是方向还是绝对位置?

  • 文本 embedding:关心语义方向,且模长受文本长度影响 → 默认 cosine(或归一化后 L2)
  • 聚类(K-means):基于质心欧氏距离 → 默认 L2;特征归一化后用 cosine 也等价
  • 异常检测:偏离中心的距离更直观 → L2

5. 参数调优:召回率与延迟的权衡

HNSW 参数分构建期查询期两组:

5.1 构建期参数(决定「图长什么样」)

参数 含义 影响
M 每个节点最大邻居数 越大图越稠密、召回率越高,但内存和构建时间越大(典型 16~64)
M_max 底层最大连接数(≈2M) 控制底层稠密程度,防止退化
ef_construction 插入时搜索的候选集大小 越大插入找邻居越准、图质量越高,但构建越慢(典型 100~200)

5.2 查询期参数(决定「怎么搜」)

参数 含义 影响
ef_search 查询时维护的候选集大小 越大召回率越高、延迟越高------召回率 vs 延迟的核心旋钮

5.3 实战:FAISS 代码

python 复制代码
import faiss

d = 768  # 向量维度
M = 16
ef_construction = 100
ef_search = 50

# 构建索引
index = faiss.IndexHNSWFlat(d, M)
index.hnsw.efConstruction = ef_construction
index.add(vectors)  # 构建期

# 查询
index.hnsw.efSearch = ef_search
D, I = index.search(query, k=10)  # 查询期

⚠️ 踩坑提醒构建期参数(M、ef_construction)线上已固定,改它们要重建索引 。线上延迟超标,只能调 ef_search ;而且 ef_search 要先离线评测 recall-延迟曲线再定值,不是拍脑袋。


6. 面试考点速查表

考点 一句话答案 加分点
为什么用 ANN? 用可接受的召回率损失换数量级速度提升 说出 O(n) 扫描 + 维度灾难
Recall@k 是什么? ANN 返回结果中真正 top-k 的比例 强调它是工程落地核心指标
HNSW 原理? 分层(跳表)+ 小世界图(贪心导航) 强调 NSW 是单层、HNSW 才分层
cosine vs L2? cosine 看方向、L2 看距离 归一化后等价:|A-B|²=2-2cos
怎么调参? 构建期管图质量、查询期管权衡 强调 ef_search 靠实测、构建期不能混调

总结:核心框架一句话背下来

scss 复制代码
向量检索 = 距离度量 + 索引算法 + 参数调优

距离度量:cosine 看方向 / L2 看距离 → 归一化后等价
索引算法:HNSW = 分层(跳表) + 小世界(贪心导航)
参数调优:构建期(M, ef_construction)管图质量
          查询期(ef_search)管召回率 vs 延迟
核心指标:Recall@k 与延迟的权衡

💡 面试加分建议

  1. 主动串知识点:比如方案设计时提到「归一化后 L2 更快」,体现体系化理解。
  2. 提备选方案:IVFPQ 更省内存但召回率略低、暴力扫描延迟不可接受------说明取舍比只说「选 HNSW」更有说服力。
  3. 强调工程思维:先离线评测、再上线,这是面试官最看重的。

🛠️ 实战背书

这篇不是纯理论------我的 RAG 项目(FastAPI + ChromaDB)底层用的正是 hnswlib 的 HNSW,文中每个参数(M、ef_construction、ef_search)都在真实问答管线上验证过。面试时讲"我用过 + 知道为什么这么调",比只会背定义值钱得多。


如果这篇文章对你有帮助,欢迎点赞收藏~ 后续可以继续更新:IVFPQ 量化原理、向量数据库(Milvus/FAISS/Qdrant)选型实战、RAG 检索优化实战。 🚀

相关推荐
山岚的运维笔记1 小时前
mysql 专业笔记 -- 第 1 章:MySQL 入门
运维·数据库·笔记·后端·学习·mysql·dba
fliter1 小时前
我们如何通过优化 1.1.1.1 的 DNS 缓存节省 100 TB 内存
后端
一开1 小时前
一个自己开发的 Agent Harness-子 Agent篇
后端
KoPa1 小时前
HeySmart:事件总线——异步解耦的艺术
前端·后端
CoderYanger1 小时前
A.每日一题:3345. 最小可整除数位乘积 I
java·数据结构·程序人生·算法·leetcode·面试·学习方法
吃饱了得干活1 小时前
MySQL 内核剖析:ACID 实现、引擎对决、B+树、索引与主从复制闭环
后端·mysql
一百昏1 小时前
cen19c01(Oracle 19c RAC 单节点)网络与 CRS 故障修复报告
后端
小刘是地理大王1 小时前
OpenFeign 兜底回调(fallback / fallbackFactory)实战指南
java·后端
用户7813667114451 小时前
C++中锁的深入分析
后端
程序员麻辣烫2 小时前
OpenClaw Hook系统:Agent框架的非侵入式扩展机制
后端·aigc