2467 words
12 minutes
向量数据库检索算法详解
向量数据库检索算法详解
向量检索是现代 AI 应用的核心技术之一,广泛应用于语义搜索、推荐系统、图像检索等场景。本文将深入解析主流向量数据库采用的检索算法,并提供实用的选型和配置指南。
一、常用检索算法分类
1.1 HNSW (Hierarchical Navigable Small World)
HNSW 是一种基于多层图结构的近似最近邻搜索算法,被视为当前最流行的向量索引算法。
核心特点:
- 多层图结构,自顶向下构建稀疏连接
- 查询速度:O(log n),适合实时检索场景
- 召回率:95-99%,精度表现优异
- 内存占用较高,以空间换时间
工作原理: HNSW 构建一个多层图,上层节点连接较远,下层节点连接较近。查询时从最上层开始,通过贪心搜索快速定位,逐步向下层细化,最终找到最近邻。
1.2 IVF (Inverted File Index)
IVF 采用倒排索引思想,先将向量空间聚类划分,查询时只需搜索部分聚类中心。
核心特点:
- 先聚类后搜索,搜索范围可控
- 支持增量索引,无需全量重建
- 召回率取决于探查簇数量
- 可与 PQ 结合进一步压缩
1.3 PQ (Product Quantization)
Product Quantization 将高维向量分割成多个子空间,分别进行量化编码。
核心特点:
- 高压缩比:10-50x 内存压缩
- 支持亿级向量规模
- 查询速度受分段数影响
- 常与 IVF 结合使用(IVF-PQ)
1.4 LSH (Locality-Sensitive Hashing)
LSH 通过哈希函数将相似向量映射到相同桶中。
核心特点:
- 适合稀疏向量检索
- 天然支持去重任务
- 需要较多哈希表达到高精度
- 查询召回率相对较低
1.5 HNSW + IVF 混合
结合两者优点,在 HNSW 图结构上叠加 IVF 索引层,进一步提升大规模数据下的检索效率。
二、核心参数详解
2.1 HNSW 参数
| 参数 | 含义 | 推荐值 | 说明 |
|---|---|---|---|
| M | 每节点最大邻居数 | 16-64 | 注意:指每个节点最多连接的节点数,而非每层节点数 |
| efConstruction | 构建时搜索广度 | 100-400 | 值越大构建越慢,但索引质量越高 |
| efSearch | 查询时搜索广度 | 50-500 | 值越大召回率越高,但查询延迟上升 |
参数调优建议:
- 数据量小、精度优先:M=32-64,efConstruction=200-400
- 数据量大、追求速度:M=16-32,efConstruction=100-200
- 查询时 efSearch 可动态调整,无需重建索引
2.2 IVF 参数
| 参数 | 含义 | 推荐值 | 说明 |
|---|---|---|---|
| nlist | 聚类中心数 | ~4×√n | n 为向量总数,中心数约为 4 倍平方根 |
| nprobe | 搜索探查簇数 | 10-100 | 越多召回率越高,但延迟增加 |
2.3 PQ 参数
| 参数 | 含义 | 推荐值 | 说明 |
|---|---|---|---|
| m | 分段数 | 32-128 | 分段越多精度越高,但编码长度增加 |
| nbits | 每段编码位数 | 8 | 通常为 8,即 256 个码字 |
2.4 LSH 参数
| 参数 | 含义 | 推荐值 | 说明 |
|---|---|---|---|
| nbits | 哈希位数 | 维度 2-4 倍 | 维度为向量维度 |
| ntables | 哈希表数量 | 50-200 | 越多召回率越高,内存占用越大 |
三、算法选型决策树
根据不同业务场景选择合适的算法:
数据量 < 100万,精度优先 └── 推荐 HNSW
数据量 100万-1000万,均衡考虑 └── 推荐 HNSW 或 IVF+PQ
数据量 > 1000万,内存受限 └── 推荐 IVF + PQ
稀疏向量 / 去重任务 └── 推荐 LSH
需要增量更新 + 大规模 └── 推荐 IVF(可叠加 PQ)四、主流产品算法对比
| 产品 | 主要算法 | 特点 |
|---|---|---|
| Pinecone | HNSW + 优化 | 托管服务,开箱即用 |
| Milvus | HNSW、IVF、GPU加速 | 开源灵活,支持混合索引 |
| Weaviate | HNSW | 默认 HNSW,支持矢量化和混合搜索 |
| Faiss | IVF、PQ、HNSW、LSH 全支持 | Facebook 出品,算法最全面 |
| Qdrant | HNSW + 滤波 | 支持带过滤条件的向量检索 |
| Chroma | HNSW(基于 Faiss) | 轻量级,嵌入管理专精 |
五、代码示例:Faiss 实战
Faiss 是 Facebook 出品的向量检索库,提供了所有主流算法的实现。
5.1 HNSW 索引
import faiss
d = 128 # 向量维度M = 32 # 每节点最大邻居数
# 创建 HNSW 索引index = faiss.IndexHNSWFlat(d, M)
# 设置参数index.hnsw.efConstruction = 200 # 构建时搜索广度index.hnsw.efSearch = 100 # 查询时搜索广度
# 添加向量index.add(vectors)
# 查询D, I = index.search(query_vector, k=10)# D: 距离数组, I: 最近邻索引数组5.2 IVF 索引
d = 128nlist = 100 # 聚类中心数
# 使用内积作为相似度度量quantizer = faiss.IndexFlatIP(d)
# 创建 IVF 索引index = faiss.IndexIVFFlat(quantizer, d, nlist)
# 必须先训练再添加向量index.train(vectors)index.add(vectors)
# 设置探查数量index.nprobe = 20
# 查询D, I = index.search(query_vector, k=10)5.3 IVF-PQ 混合索引
d = 128nlist = 100 # 聚类数m = 64 # PQ 分段数nbits = 8 # 每段编码位数
quantizer = faiss.IndexFlatIP(d)
# 创建 IVF-PQ 索引index = faiss.IndexIVFPQ(quantizer, d, nlist, m, nbits)
index.train(vectors)index.add(vectors)
D, I = index.search(query_vector, k=10)六、配置示例:主流数据库
6.1 Milvus 配置
index: type: HNSW params: M: 32 efConstruction: 200search: params: ef: 1006.2 Qdrant 配置
{ "hnsw_config": { "m": 32, "ef_construct": 200 }, "query_params": { "hnsw_ef": 128 }}6.3 Weaviate 配置
{ "vectorIndexType": "hnsw", "vectorIndexConfig": { "ef": 128, "efConstruction": 200, "maxConnections": 32 }}七、RAG 中的稠密检索 vs 稀疏检索
┌─────────────────────────────────────────────────────────────────┐ │ RAG 检索架构 │ │ │ │ Query ──► ┌─────────────┐ │ │ │ Retrieval │ │ │ └──────┬──────┘ │ │ │ │ │ ┌────────┴────────┐ │ │ ▼ ▼ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ Sparse │ │ Dense │ │ │ │ Retrieval │ │ Retrieval │ │ │ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ ▼ ▼ │ │ BM25 / TF-IDF Embedding Model │ │ (词项匹配) (语义匹配) │ └─────────────────────────────────────────────────────────────────┘核心区别
┌────────────┬──────────────────────────────┬───────────────────────────────┐ │ 维度 │ 稀疏检索 (Sparse) │ 稠密检索 (Dense) │ ├────────────┼──────────────────────────────┼───────────────────────────────┤ │ 表示方式 │ 高维稀疏向量(词项出现与否) │ 低维稠密向量(语义embedding) │ ├────────────┼──────────────────────────────┼───────────────────────────────┤ │ 匹配方式 │ 词项精确匹配 / 统计权重 │ 余弦相似度 / 向量距离 │ ├────────────┼──────────────────────────────┼───────────────────────────────┤ │ 代表算法 │ BM25、TF-IDF │ Sentence-BERT、ColBERT │ ├────────────┼──────────────────────────────┼───────────────────────────────┤ │ 同义词处理 │ ❌ 无法处理 │ ✅ 语义理解 │ ├────────────┼──────────────────────────────┼───────────────────────────────┤ │ 多语言支持 │ 依赖分词器 │ ✅ 跨语言 │ ├────────────┼──────────────────────────────┼───────────────────────────────┤ │ 计算成本 │ 低 │ 中高 │ ├────────────┼──────────────────────────────┼───────────────────────────────┤ │ 可解释性 │ 高(词项可追溯) │ 低(向量不可解释) │ ├────────────┼──────────────────────────────┼───────────────────────────────┤ │ 冷启动 │ ✅ 无需训练 │ 需要预训练模型 │ └────────────┴──────────────────────────────┴───────────────────────────────┘稀疏检索详解
# BM25 算法核心思想 def bm25_score(query: str, document: str, avgdl: float, k1=1.5, b=0.75) -> float: """ BM25 = Σ IDF(qi) × (tf(qi,d) × (k1+1)) / (tf(qi,d) + k1 × (1-b + b × |d|/avgdl))
词项出现次数越多分数越高,但有衰减(饱和) 文档越长惩罚越大 """ score = 0.0 for qi in query.split(): tf = document.count(qi) # 词项频率 idf = log((N - n(qi) + 0.5) / (n(qi) + 0.5)) # 逆文档频率 numerator = tf * (k1 + 1) denominator = tf + k1 * (1 - b + b * len(document) / avgdl) score += idf * numerator / denominator return score稀疏检索示例:
Query: "Python 异步编程" Document: "Python asyncio 是异步编程框架"BM25 匹配:
- “Python” ✓ 匹配
- “异步” ✓ 匹配
- “编程” ✓ 匹配 Score: 高(三个词都命中)
但无法理解 “asyncio” = “异步编程” 的语义关系
稠密检索详解
稠密检索核心思想
def dense_retrieve(query_embedding, document_embeddings, top_k=5): """ 1. Query → Embedding Model → 向量 q 2. Document → Embedding Model → 向量 d1, d2, ... 3. 计算余弦相似度: cos(q, di) 4. 返回 top-k 最相似的文档 """ similarities = [] for doc_id, doc_emb in document_embeddings.items(): sim = cosine_similarity(query_embedding, doc_emb) similarities.append((doc_id, sim))
# 返回最相似的 top-k return sorted(similarities, key=lambda x: x[1], reverse=True)[:top_k]稠密检索示例:
Query: "Python 异步编程" → Embedding: [0.12, 0.45, 0.78, ...] (768维向量)
Document 1: "Python asyncio 是异步编程框架" → Embedding: [0.11, 0.47, 0.79, ...] → similarity: 0.94 ✓
Document 2: "Java 多线程并发处理" → Embedding: [0.65, 0.12, 0.08, ...] → similarity: 0.23即使词项完全不匹配,也能通过语义理解返回正确结果
混合检索(Hybrid Retrieval)
实际应用中通常结合两者优势:
class HybridRetriever: def __init__(self, sparse_weight=0.3, dense_weight=0.7): self.sparse_weight = sparse_weight self.dense_weight = dense_weight self.sparse_retriever = BM25Retriever() self.dense_retriever = DenseRetriever()
def retrieve(self, query: str, top_k=10) -> list[Document]: # 稀疏检索 sparse_results = self.sparse_retriever.retrieve(query, top_k * 2)
# 稠密检索 dense_results = self.dense_retriever.retrieve(query, top_k * 2)
# RRF (Reciprocal Rank Fusion) 融合 fused_scores = {} for rank, doc in enumerate(sparse_results): fused_scores[doc.id] = fused_scores.get(doc.id, 0) + self.sparse_weight / (60 + rank)
for rank, doc in enumerate(dense_results): fused_scores[doc.id] = fused_scores.get(doc.id, 0) + self.dense_weight / (60 + rank)
# 返回融合后 top-k sorted_docs = sorted(fused_scores.items(), key=lambda x: x[1], reverse=True) return [doc for doc_id, _ in sorted_docs[:top_k]]在 AI-Interview 项目中的检索方式
RAG 检索流程:
用户问题 → ┌─────────────────────────────────────────┐ │ 1. Sparse: BM25 初步召回(关键词匹配) │ │ → 快速召回 100 个候选 │ │ │ │ 2. Dense: Embedding 精排(语义相似度) │ │ → 从 100 个中选出 top-5 │ │ │ │ 3. Rerank: 交叉编码器重排 │ │ → 最终 top-3 输出 │ └─────────────────────────────────────────┘ ↓ 相关文档 context ↓ LLM 生成回答何时使用哪种检索
┌──────────────┬──────────┬───────────────────────────────────┐ │ 场景 │ 推荐方式 │ 原因 │ ├──────────────┼──────────┼───────────────────────────────────┤ │ 关键词明确 │ 稀疏检索 │ "Python async await" 精确词项匹配 │ ├──────────────┼──────────┼───────────────────────────────────┤ │ 同义词查询 │ 稠密检索 │ "心脏" = "心肌" 语义理解 │ ├──────────────┼──────────┼───────────────────────────────────┤ │ 多语言场景 │ 稠密检索 │ 跨语言 embedding │ ├──────────────┼──────────┼───────────────────────────────────┤ │ 企业内部术语 │ 混合检索 │ 兼顾专有名词 + 语义 │ ├──────────────┼──────────┼───────────────────────────────────┤ │ 实时性要求高 │ 稀疏检索 │ 计算更快 │ └──────────────┴──────────┴───────────────────────────────────┘八、总结
向量检索算法的选择需要综合考虑以下因素:
- 数据规模:决定采用单一算法还是混合方案
- 精度要求:HNSW 精度最高,LSH 适合快速过滤
- 内存限制:PQ 可大幅压缩内存占用
- 查询延迟:实时系统需要 O(log n) 级别的响应
- 更新频率:IVF 支持增量更新,HNSW 需要重建
对于大多数场景,HNSW 是首选方案;若数据量超过千万级或内存受限,可考虑 IVF+PQ 组合。具体参数设置建议从推荐值开始,通过实际测试调整到最优配置。
向量数据库检索算法详解
https://sgjki547.top/posts/向量数据库检索算法详解/