向量检索与 ANN:从 Embedding 到 HNSW
Published in:2026-01-18 | category: 搜推算法

本文是 搜推算法笔记 第 3 篇。索引见 搜推算法 · 知识索引

上一篇:Query 理解与多路召回 · 下一篇:精排与评价指标

语义召回解决字面匹配的盲区——用户搜「纯奶」,标题写「纯牛奶」,BM25 可能漏召,但向量空间距离很近。BM25 与向量是互补关系:前者保精确(专名、型号),后者补语义(同义、描述性 Query)。本文讲解 Embedding 原理、相似度度量、ANN 算法选型与工程部署。

1. 从字面匹配到向量匹配

1
2
3
Query:  无线耳麦
标题A: 蓝牙耳机 ← 字面几乎无交集,但语义极近
标题B: 无线路由器 ← 有「无线」,但意图不同

BM25 可能更偏向 B(有「无线」),向量更可能偏向 A——这就是语义召回的价值。


2. Embedding 原理直觉

嵌入函数 (f) 把对象映射到 (\mathbb{R}^d):

[
f(\text{蓝牙耳机}) = [0.12, -0.33, \ldots, 0.08] \in \mathbb{R}^{768}
]

类型例子用途
词向量Word2Vec、GloVe词级相似(弱)
句/段落向量Sentence-BERT、BGE、E5检索、语义相似(强)

搜推召回主要用 句向量(Query 一句、标题一句):

1
2
3
4
tokens → Transformer → token 隐向量序列
↓ mean pooling / CLS
↓ L2 归一化
句向量

Mean pooling:(v=\frac{\sum_i m_i h_i}{\sum_i m_i}),(m_i) 为 attention mask。

词向量 vs 句向量 vs 双塔:怎么选?

类型代表粒度检索质量适用
词向量Word2Vec、GloVe弱(需平均成句)词级分析、改写近邻
句向量(单塔)Sentence-BERT、BGE句/段Query-Doc 语义匹配
双塔DSSM、业务双塔Query 塔 + Item 塔强(可 ANN)大规模召回
交叉编码器Cross-EncoderQuery+Doc 拼接最强但慢精排 rerank Top50

召回用双塔/句向量(快),精排 rerank 可用 Cross-Encoder(准但贵)——Cross-Encoder 对 Query 和 Doc 做联合编码,精度高但无法预计算 Doc 向量,只能对少量候选逐对打分。

Sentence-BERT / BGE 训练直觉

通用句向量模型多用对比学习:正样本对 ((q, d^+)) 拉近,负样本 ((q, d^-)) 推远。

InfoNCE 损失(in-batch negatives):

[
\mathcal{L}=-\log\frac{\exp(\mathrm{sim}(q,d^+)/\tau)}{\exp(\mathrm{sim}(q,d^+)/\tau)+\sum_{d^-\in\mathcal{N}}\exp(\mathrm{sim}(q,d^-)/\tau)}
]

  • (\tau):温度系数,控制分布锐度
  • (\mathcal{N}):batch 内其它 Doc 作负样本(或 hard negative mining)
  • 训练目标:Query 与相关 Doc 在向量空间余弦相似度最大

BGE / E5 等模型在通用检索语料(MS MARCO、中文百科等)上预训练,开箱即用;业务双塔则在自家点击日志上继续训,把「相关」对齐到「会点击/会转化」。

Word2Vec 到 Transformer 的演进

阶段模型核心思想局限
词向量Word2Vec (CBOW/Skip-gram)上下文预测,(w) 与上下文共现一词一向量,无法处理多义
上下文向量ELMoBiLSTM 动态表示慢,难大规模 ANN
句向量Sentence-BERTBi-Encoder + 对比学习检索主流
交叉编码Cross-EncoderQuery+Doc 拼接过 Transformer准但不可预计算 Doc

搜推召回选 Bi-Encoder(双塔/句向量) 而非 Cross-Encoder,根本原因是 Doc 向量必须可离线预计算 + ANN 检索


3. 相似度度量

余弦相似度:

[
\cos(a,b)=\frac{a\cdot b}{|a||b|}
]

L2 归一化后,内积 = 余弦——系统里常先归一化,再用内积检索。

手算:

1
2
3
4
q  = [0.6, 0.8]          # 已归一化
d1 = [0.6, 0.8] → 点积 1.0
d2 = [0.8, -0.6] → 点积 0.0
d3 = [-0.6, -0.8]→ 点积 -1.0

归一化后欧氏距离与余弦单调相关:(|a-b|^2=2-2a\cdot b)。

三种相似度怎么选?

度量公式是否需归一化特点
余弦相似度(\frac{a·b}{|a||b|})建议 L2 归一化只看方向,不看模长;文本检索主流
内积(a·b)必须 L2 归一化才等价余弦计算最快,索引友好(HNSW 内积)
欧氏距离(|a-b|_2)归一化后与余弦单调部分索引默认支持

工程惯例:离线 L2 归一化 → 在线用内积检索。选一种度量并贯彻到底,混用会导致索引和暴力搜索结果不一致。


4. 精确检索的复杂度问题

暴力法复杂度 (O(Nd)):

Nd=768 时
1e4往往可暴力
1e6~1e8必须 ANN

ANN(Approximate Nearest Neighbor):不保证每次都找到真正最近邻,但以高概率找到足够近的,换数量级加速。

提高往往牺牲
速度 / QPS一点召回精度
省内存(量化)一点精度

检索 Recall@K(真实近邻被找回的比例)评估索引质量。


5. ANN 算法:IVF / HNSW / PQ

三种 ANN 对比

维度IVFHNSWPQ / IVF-PQ
原理聚类分桶,查近桶多层导航小世界图向量量化压缩
召回率中~高(nprobe 控制)中(有损)
查询延迟
建索引
内存
增量更新较方便较麻烦较方便
典型场景亿级、内存有限千万级、要效果十亿级、内存紧

IVF(Inverted File)

1
2
离线:对全体向量聚类 → nlist 个 centroid → 每个向量归到最近桶
在线:Query 找最近 nprobe 个 centroid → 只在这些桶内暴力算距离 → TopK

nprobe ↑ → 更准、更慢。

HNSW(Hierarchical Navigable Small World)

多层图,上层是「高速公路」,下层细查。工业界非常常用。

优点代价
高召回、延迟优秀内存占用较大

参数:M(边数)、efSearch ↑ → 更准更慢更吃内存。

HNSW 检索流程(直觉):

1
2
3
4
5
6
1. 从顶层入口点开始
2. 在当前层贪心走向 Query 最近邻(沿边跳转)
3. 到达局部最优后,下降到下一层继续
4. 最底层(Layer 0)用 efSearch 宽搜:
维护大小为 ef 的候选堆,不断扩展邻居
5. 从候选堆取 TopK
参数含义调大效果
M每个节点最大出边数图更密、召回更高、内存更大
efConstruction建索引时的搜索宽度索引质量更高、建索引更慢
efSearch查询时的候选池大小召回更高、查询更慢

复杂度: 单次查询约 (O(\log N)) 次距离计算(实践中),远优于暴力 (O(N))。代价是索引内存约为原始向量的 1.5~2 倍(存图结构)。

PQ(Product Quantization)原理

把 (d) 维向量切成 (m) 段,每段 (d/m) 维,各段独立量化:

[
x \in \mathbb{R}^d ;\Rightarrow; [x_1, x_2, \ldots, x_m]
]

每段用 (k) 个聚类中心(码本)表示,原向量变为 (m) 个整数索引。存储从 (d \times 4) 字节(float32)降到 (m \times \log_2 k) 位。

查询时: Query 向量同样分段,用预计算的查表距离(Asymmetric Distance Computation)快速估算与库中向量的距离,无需解压全精度向量。

优点代价
内存降 10~50 倍量化有损,Recall@K 下降
适合十亿级需调 (m)、(k) 平衡精度与压缩

IVF-PQ = 先 IVF 分桶缩小搜索范围,再 PQ 压缩桶内向量——Faiss 经典组合。

场景常见选择
效果与延迟均衡HNSW
数据极大、内存紧IVF-PQ
数据很小暴力 / flat

千万级向量库选 HNSW 是常见默认;pgvector、Milvus、Faiss 均支持。


6. 在线离线全流程

离线:

1
2
Item 文本 → 同款预处理 → Encoder(BGE/双塔 Item 塔)
→ L2 normalize → 写入向量库 / 建 HNSW → 版本号

在线:

1
Query → 同一套预处理 → 同一模型 Encoder → ANN TopK → (item_id, score)

一致性红线

Query 与 Doc 必须:同一模型版本、同一 pooling/归一化、同一文本字段策略。否则空间不对齐,相似度无意义。


7. 双塔召回

1
2
3
Query 塔:q_text / user → q_vec
Item 塔:item 特征 → d_vec
分数 ≈ q_vec · d_vec

离线可把所有 d_vec 建好索引;在线只跑 Query 塔 + ANN。

双塔训练目标:

[
\mathcal{L}=-\log\sigma(\mathbf{q}\cdot\mathbf{d}^+) - \sum_{\mathbf{d}^-}\log\bigl(1-\sigma(\mathbf{q}\cdot\mathbf{d}^-)\bigr)
]

或用 sampled softmax / in-batch negatives。关键约束:

  • Query 塔与 Item 塔最后一层必须同维,点积才有意义
  • 负样本质量决定效果——随机负样本简单,hard negative(BM25 Top 但未点击)更难但更有效
  • 点击日志作正样本时,注意 position bias 和曝光偏差(见 精排篇

为什么双塔能 ANN? Item 向量与 Query 无关,可离线算好;在线只 encode Query 一次,在索引里找最近邻——复杂度 (O(\log N)) 而非 (O(N \times \text{encode}))。

通用 BGE业务双塔
训练通用语料自家点击/转化
优点开箱即用更贴业务目标
缺点未必贴转化要数据与训练成本

典型路径:先用通用预训练向量验证效果,积累业务数据后再训领域双塔。

通用向量 vs 业务双塔 vs Cross-Encoder

维度通用 BGE/E5业务双塔Cross-Encoder
训练数据通用语料业务点击/转化业务点击/转化
离线 Doc 向量可预计算可预计算不可(需联合编码)
在线延迟encode + ANNencode + ANN逐对打分,慢
效果上限最高
适用阶段冷启动 / 验证有数据后定制精排 rerank Top50

向量数据库选型

方案优点适用规模典型场景
Milvus专业向量库、功能全亿级独立语义检索服务
Faiss性能极强、灵活任意离线/在线嵌入 C++ 服务
pgvector复用 PG 运维、可 JOIN千万级中小规模、已有 PG 栈
ES knn运维统一千万级向量量不大、与全文检索同集群
Redis Vector低延迟、内存百万级热数据缓存

选型考量:数据量、运维成本、是否与业务库同库、与全文检索的资源隔离。

全文检索是核心交易路径时,向量检索独立部署更常见——避免 ANN 查询与倒排检索争抢 CPU/内存。

pgvector 参考架构(中小规模)

PostgreSQL + pgvector 适合千万级以下、已有 PG 基础设施的场景:

1
2
3
离线:Doc 文本 → Encoder → L2 normalize → 批量写入 PG(HNSW 索引)
在线:Query → Encoder → ANN 查询 → TopK item_id
增量:MQ 触发新 Doc embed 写入;改标题更新向量;下架标记/delete
维度pgvectorMilvus 独立部署
运维复用 PG多一套系统
索引HNSWHNSW/IVF 可选
数据量千万级亿级更适合
与业务 JOIN同库方便需同步 ID

8. 工程实践要点

向量库放哪?

方案优点风险
全文检索 ES knn运维统一与全文检索抢资源,P99 恶化
独立向量库(Milvus/PG/Faiss)解耦多一套系统

全文检索是核心交易路径时,独立部署更常见

增量更新

  • 新 Item:实时/准实时 embed 写入
  • 改标题:更新向量
  • 下架:删除或标记不可召回

与字面召回互补


下一步

Prev:
华为管理法学习笔记(下):干部管理、文化与激励
Next:
RocketMQ 技术内幕