本文是 搜推算法笔记 第 3 篇。索引见 搜推算法 · 知识索引。
上一篇:Query 理解与多路召回 · 下一篇:精排与评价指标
语义召回解决字面匹配的盲区——用户搜「纯奶」,标题写「纯牛奶」,BM25 可能漏召,但向量空间距离很近。BM25 与向量是互补关系:前者保精确(专名、型号),后者补语义(同义、描述性 Query)。本文讲解 Embedding 原理、相似度度量、ANN 算法选型与工程部署。
1. 从字面匹配到向量匹配
1 | Query: 无线耳麦 |
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 | tokens → Transformer → token 隐向量序列 |
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-Encoder | Query+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) 与上下文共现 | 一词一向量,无法处理多义 |
| 上下文向量 | ELMo | BiLSTM 动态表示 | 慢,难大规模 ANN |
| 句向量 | Sentence-BERT | Bi-Encoder + 对比学习 | 检索主流 |
| 交叉编码 | Cross-Encoder | Query+Doc 拼接过 Transformer | 准但不可预计算 Doc |
搜推召回选 Bi-Encoder(双塔/句向量) 而非 Cross-Encoder,根本原因是 Doc 向量必须可离线预计算 + ANN 检索。
3. 相似度度量
余弦相似度:
[
\cos(a,b)=\frac{a\cdot b}{|a||b|}
]
L2 归一化后,内积 = 余弦——系统里常先归一化,再用内积检索。
手算:
1 | q = [0.6, 0.8] # 已归一化 |
归一化后欧氏距离与余弦单调相关:(|a-b|^2=2-2a\cdot b)。
三种相似度怎么选?
| 度量 | 公式 | 是否需归一化 | 特点 |
|---|---|---|---|
| 余弦相似度 | (\frac{a·b}{|a||b|}) | 建议 L2 归一化 | 只看方向,不看模长;文本检索主流 |
| 内积 | (a·b) | 必须 L2 归一化才等价余弦 | 计算最快,索引友好(HNSW 内积) |
| 欧氏距离 | (|a-b|_2) | 归一化后与余弦单调 | 部分索引默认支持 |
工程惯例:离线 L2 归一化 → 在线用内积检索。选一种度量并贯彻到底,混用会导致索引和暴力搜索结果不一致。
4. 精确检索的复杂度问题
暴力法复杂度 (O(Nd)):
| N | d=768 时 |
|---|---|
| 1e4 | 往往可暴力 |
| 1e6~1e8 | 必须 ANN |
ANN(Approximate Nearest Neighbor):不保证每次都找到真正最近邻,但以高概率找到足够近的,换数量级加速。
| 提高 | 往往牺牲 |
|---|---|
| 速度 / QPS | 一点召回精度 |
| 省内存(量化) | 一点精度 |
用 检索 Recall@K(真实近邻被找回的比例)评估索引质量。
5. ANN 算法:IVF / HNSW / PQ
三种 ANN 对比
| 维度 | IVF | HNSW | PQ / IVF-PQ |
|---|---|---|---|
| 原理 | 聚类分桶,查近桶 | 多层导航小世界图 | 向量量化压缩 |
| 召回率 | 中~高(nprobe 控制) | 高 | 中(有损) |
| 查询延迟 | 中 | 低 | 低 |
| 建索引 | 快 | 慢 | 中 |
| 内存 | 中 | 大 | 小 |
| 增量更新 | 较方便 | 较麻烦 | 较方便 |
| 典型场景 | 亿级、内存有限 | 千万级、要效果 | 十亿级、内存紧 |
IVF(Inverted File)
1 | 离线:对全体向量聚类 → nlist 个 centroid → 每个向量归到最近桶 |
nprobe ↑ → 更准、更慢。
HNSW(Hierarchical Navigable Small World)
多层图,上层是「高速公路」,下层细查。工业界非常常用。
| 优点 | 代价 |
|---|---|
| 高召回、延迟优秀 | 内存占用较大 |
参数:M(边数)、efSearch ↑ → 更准更慢更吃内存。
HNSW 检索流程(直觉):
1 | 1. 从顶层入口点开始 |
| 参数 | 含义 | 调大效果 |
|---|---|---|
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 | Item 文本 → 同款预处理 → Encoder(BGE/双塔 Item 塔) |
在线:
1 | Query → 同一套预处理 → 同一模型 Encoder → ANN TopK → (item_id, score) |
一致性红线
Query 与 Doc 必须:同一模型版本、同一 pooling/归一化、同一文本字段策略。否则空间不对齐,相似度无意义。
7. 双塔召回
1 | Query 塔:q_text / user → q_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 + ANN | encode + ANN | 逐对打分,慢 |
| 效果上限 | 中 | 高 | 最高 |
| 适用阶段 | 冷启动 / 验证 | 有数据后定制 | 精排 rerank Top50 |
向量数据库选型
| 方案 | 优点 | 适用规模 | 典型场景 |
|---|---|---|---|
| Milvus | 专业向量库、功能全 | 亿级 | 独立语义检索服务 |
| Faiss | 性能极强、灵活 | 任意 | 离线/在线嵌入 C++ 服务 |
| pgvector | 复用 PG 运维、可 JOIN | 千万级 | 中小规模、已有 PG 栈 |
| ES knn | 运维统一 | 千万级 | 向量量不大、与全文检索同集群 |
| Redis Vector | 低延迟、内存 | 百万级 | 热数据缓存 |
选型考量:数据量、运维成本、是否与业务库同库、与全文检索的资源隔离。
全文检索是核心交易路径时,向量检索独立部署更常见——避免 ANN 查询与倒排检索争抢 CPU/内存。
pgvector 参考架构(中小规模)
PostgreSQL + pgvector 适合千万级以下、已有 PG 基础设施的场景:
1 | 离线:Doc 文本 → Encoder → L2 normalize → 批量写入 PG(HNSW 索引) |
| 维度 | pgvector | Milvus 独立部署 |
|---|---|---|
| 运维 | 复用 PG | 多一套系统 |
| 索引 | HNSW | HNSW/IVF 可选 |
| 数据量 | 千万级 | 亿级更适合 |
| 与业务 JOIN | 同库方便 | 需同步 ID |
8. 工程实践要点
向量库放哪?
| 方案 | 优点 | 风险 |
|---|---|---|
| 全文检索 ES knn | 运维统一 | 与全文检索抢资源,P99 恶化 |
| 独立向量库(Milvus/PG/Faiss) | 解耦 | 多一套系统 |
全文检索是核心交易路径时,独立部署更常见。
增量更新
- 新 Item:实时/准实时 embed 写入
- 改标题:更新向量
- 下架:删除或标记不可召回
与字面召回互补
- 向量负责语义
- BM25 负责专名/精确词
- 融合用 RRF/加权(见 Query 理解与多路召回)
下一步
- 候选进池后怎么排序 → 精排与评价指标
- Python 工程落地 → Python 搜推技术栈


