共计 1632 个字符,预计需要花费 5 分钟才能阅读完成。
传统检索的困境
当我们需要在海量数据中快速找到相似的文本、图片或视频时,传统精确检索方法(如线性扫描)会遇到严重瓶颈。想象一下,如果你的商品库有 1000 万条数据,每次搜索都要遍历所有条目计算相似度,响应时间可能长达数秒——这显然无法满足实时交互的需求。
ANN 算法家族
近似最近邻(ANN)算法通过牺牲少量精度换取巨大性能提升,主流方案包括:
- LSH(局部敏感哈希):通过哈希函数将相似向量映射到相同桶中,查询时只需比较同桶数据。时间复杂度 O(1),但召回率随维度增长快速下降
- HNSW(分层可导航小世界):模仿社交网络的层次结构,构建多层级图索引。搜索复杂度 O(log n),适合高召回率场景
- IVF(倒排文件):先对向量聚类,搜索时只扫描最近几个簇。通过调节 nlist 参数平衡速度与精度

Faiss 实战演练
环境准备
import faiss
import numpy as np
from sklearn.preprocessing import normalize
# 生成模拟数据
d = 768 # BERT 向量维度
nb = 100000 # 数据库大小
np.random.seed(1234)
db_vectors = np.random.random((nb, d)).astype('float32')
normalize(db_vectors, copy=False) # 关键步骤!未归一化会导致距离计算失真
索引构建
nlist = 100 # 聚类中心数
quantizer = faiss.IndexFlatIP(d) # 内积作为距离度量
index = faiss.IndexIVFFlat(quantizer, d, nlist)
assert not index.is_trained
index.train(db_vectors) # 聚类过程
index.add(db_vectors)
assert index.is_trained
查询优化
index.nprobe = 10 # 搜索的簇数量,越大越慢但越准
k = 5 # 返回 topK 结果
query = np.random.random((1, d)).astype('float32')
normalize(query, copy=False)
D, I = index.search(query, k) # D 为距离,I 为索引
print(f"最近邻索引:{I}, 相似度得分:{1-D}")
性能调优手册
参数实验数据
| nprobe | 延迟(ms) | 召回率 @10 |
|---|---|---|
| 1 | 2.1 | 65% |
| 5 | 4.7 | 89% |
| 20 | 18.3 | 98% |
内存压缩技巧
对于超大规模数据,可采用乘积量化(PQ):
m = 8 # 子向量数
bits = 8 # 每个子向量编码位数
index = faiss.IndexIVFPQ(quantizer, d, nlist, m, bits)
压缩后内存降至原大小的 10%-25%,召回率损失约 5 -15%
避坑实践
- 冷启动问题:新数据到来时重建索引成本高。解决方案:
- 定期增量训练(faiss 的 clone_before_add 参数)
-
使用 OnDiskPca 降低维度后再索引
-
分布式部署:
- 主从架构:主节点维护索引,worker 节点处理查询
- 一致性保证:通过 Redis 发布订阅通知索引更新
进阶方向
- 语义增强:先用 BERT 生成 query 和 doc 的向量,比传统 TF-IDF 效果提升 30%+(参考 Google 的 ANCE 论文)
- 推荐系统应用:
# 用户历史行为向量平均作为 user embedding user_vec = np.mean([item_vecs[i] for i in clicked_items], axis=0) faiss.normalize_L2(user_vec)
写在最后
在实际电商推荐系统中,我们通过 ANN 将召回阶段耗时从 800ms 降至 12ms。关键经验是:先用小规模数据测试算法特性,再逐步调参。Faiss 文档中的 faiss.StandardGpuResources() 还能启用 GPU 加速,适合超大规模场景。遇到问题时,不妨看看 Facebook 的 faiss-wiki 和 issue 区,很多坑已经被前人踩过了。
正文完
