想象你要在「知识大礼堂」里找一位只见过一面的朋友。你有两种记忆方式:第一种是脑海里存着一张他的证件照,每个像素都清清楚楚,照片上每一处明暗都算数——这就是稠密向量,每一维都有值,谁也不能缺;第二种是你在一张几百行的清单上勾了几栏:「戴眼镜」「一米八」「东北人」「爱打羽毛球」,其余栏目全空着——这就是稀疏向量,维度虽高,但绝大部分位置是零。找人时,前者靠「照片像不像」来判断,后者靠「勾选项重不重」来核对。 真实世界中,搜索引擎、推荐系统、智能问答每天都在上演这场「找人大作战」。有的场景适合拍照片,有的场景适合填清单,聪明的系统往往两者兼用。本文将带你从零理解这两种向量的本质差异、背后的检索算法(HNSW、IVF、BM25)、如何把它们融合成混合检索,以及落地时的工具链与评估方法。读完你就能明白:为什么说「没有最好的向量,只有最合适的检索」。
对 AI 从业者而言,这是向量检索与语义搜索的基础功。掌握稠密/稀疏两类向量的取舍与混合检索范式,是设计高召回、低延迟搜索系统的核心竞争力,直接决定 RAG、推荐排序等项目的上线效果。
可视化演示
展示0/1背包问题的动态规划求解过程,逐步填充DP表,每个格子记录前i个物品在容量j下的最大价值。
本节配有交互动效(进入视口后自动加载,约 3KB)。
初始化阶段 DP表初始化为0,dp[i][j]表示前i个物品容量j时的最大价值 dp[0][j]=dp[i][0]=0 逐步填充DP表求解最优解
概念图谱
在「知识大礼堂」的找人大作战里,你手里有两样工具:证件照与标签清单。证件照对应稠密向量——它把一个人的面貌压缩成一组连续的实数值,比如 768 维的 Embedding,每一维都有意义,谁也不能是零;标签清单对应稀疏向量——维度可能高达几十万(如词表大小),但一篇文章只勾中几百个词,其余位置全是零,像白纸上零星落着几个墨点。
从数学上看,稠密向量的每个分量都是实值,且绝大多数非零;稀疏向量虽然维度极高,但非零元素占比通常不足 5%。这意味着两者在存储上差异悬殊:稠密向量每个维度都要存一个浮点数,稀疏向量则可以只记录「槽位号 + 值」的键值对,大幅节省内存。语义表达上也各擅胜场:稠密向量由神经网络从语料中学习,擅长捕捉「意思相近」的深层语义;稀疏向量直接来自词法统计,擅长捕捉「字面精确」的显式匹配——就像照片能认出「穿蓝衣服的陌生人」,清单却能牢牢锁定「名字叫张三的人」。
两个向量既然长这样,怎么衡量「像不像」?最常用的尺子是余弦相似度:
理解了两种向量的长相与度量方式,就能明白:照片擅长「神似」,清单擅长「形似」。搜索引擎里,两者往往互为补充,下一节我们将看看当大礼堂里人山人海时,如何高效地找出那个「最像的人」。
- 分子——对应槽位相乘求和把两个向量同一维度的值相乘再累加。若某维度有一方是零,乘积为零,等于自动忽略它。
- 分母——各自长度的乘积分别求出每个向量的「总长度」(各维度平方和开根号),再将两者相乘,起到归一化作用。
- 整体——方向夹角的余弦分子除以分母,得到一个介于 -1 到 1 的数值。值越接近 1,说明两个向量方向越一致,语义或词法上越相似。
检索算法
大礼堂里有十万个人,逐张照片比对(暴力搜索)虽准但太慢——每来一个查询就要算十万次余弦相似度。于是人们发明了「先分组、再精找」的策略。IVF(倒排文件) 的思路是:提前用聚类算法(如 K-means)把全体向量分成若干簇,每个簇有一个「簇代表」(质心)。查询时先算出查询点离哪个质心近,只进那一两个簇里精找,而不是全礼堂乱窜。HNSW(分层可导航小世界图) 则换了个思路:把向量组织成一张「多层好友网络」,上层是稀疏的长距离连接(一跳能蹦很远),下层是密集的短距离连接(精确定位)。查询时从顶层开始,沿着「看起来更近」的邻居逐层下跳,就像在大礼堂里先问门卫、再问楼层管理员、最后问到座位号。
IVF 的核心是给查询点挑选候选簇:
而 HNSW 则在图结构上做文章:每个节点与若干邻居相连,边分长短。插入新向量时,从顶层出发,每层贪心找最近邻并建立连接;查询时同样逐层下钻。为了让图更紧凑,PQ(乘积量化) 会把向量切段压缩,用「码本编号」代替原始数值,牺牲一点精度换巨幅内存节省——相当于把高清照片压缩成一份「马赛克缩略图」,肉眼看着像就行。
回到找人的故事:IVF 是「先按片区划分再进楼找人」,HNSW 是「沿着熟人网络一跳一跳逼近目标」,PQ 是「把照片压缩了再比」。三者常组合使用,比如「IVF+HNSW+PQ」三件套,是工业界 ANN 检索的经典配方。
- 查询向量 \mathbf{x}新来的搜索请求被 Embedding 模型编码成的稠密向量,相当于你要找的那张新照片。
- 质心 \mathbf{m}_c第 c 个簇的代表点,由 K-means 对历史向量聚类得到,相当于某个片区的「平均脸」。
- 最小化距离平方计算 x 与每个质心的距离平方,取最小值对应的下标 c;实际操作中取排名前 n 个簇扩大召回。
稀疏检索
清单派也有自己的作战体系。既然标签清单只有少数槽位被勾选,何必遍历所有人?倒排索引的做法是:为每个标签(词)维护一个「勾选了这个标签的人」的链表。查「戴眼镜」时,直接取出该链表,瞬间得到所有候选人——这就是搜索引擎倒排索引的雏形。但光靠「有没有勾」太粗糙,还得考虑「勾得多认真」和「这个标签稀不稀有」。
TF-IDF 给了第一个答案:TF(词频)衡量词在文档中出现的次数,IDF(逆文档频率)衡量词的稀缺程度——「的」字人人用,价值低;「球磨机」只出现在专业文档里,价值高。BM25 则是 TF-IDF 的进阶版,它引入了词频饱和机制和文档长度归一化:词频到了一定程度后收益递减(一篇文档里出现 10 次和 20 次的差异远小于 0 次和 10 次),同时惩罚过长的文档。BM25 的评分公式如下:
稀疏检索的特点由此显现:可解释性强(每个打分都能追溯到具体关键词)、无需训练、对精确 ID 和专有名词极其友好。缺点是词法天花板——同义改写后没有字面重合就召回不到,正如清单上没勾「戴眼镜」就无法通过它找到人。而稠密检索恰好能补上这个洞:语义相近但字面不同也能命中。两者结合,就是下一节的混合检索。
- IDF(q_i)——罕见词加权词 q_i 在所有文档中越少见,IDF 越大,相当于「越独特的标签越有辨识度」。
- 词频饱和项——带 k1 的分式当词频 f 增大时,整个分式趋近于 k1+1 的上限,避免「出现 100 次就是出现 10 次 10 倍分」的线性爆炸。
- 文档长度归一化|D| / avgdl 是文档相对平均长度的比值。文档越长,分母越大,得分被稀释,防止长文档靠「词多」占便宜。
混合检索
照片找人和清单找人各有死穴:照片认得出「神似」却常把「长得像但名字不同」的人搞混;清单锁定「形似」却对「同一个意思换个说法」束手无策。聪明的做法是双管齐下,让双方各自召回一批候选人,再把结果合并排序——这就是混合检索(Hybrid Search)。
最简单的合并方式叫归一化加权融合:把稠密检索的分数和稀疏检索的分数各自归一化到同一量纲(如 0-1),然后加权求和。权重 w 可调:偏向语义就调大稠密权重,偏向精确匹配就调大稀疏权重:
另一种更稳健的融合叫 RRF(倒数排名融合),它不关心原始分数,只看两路各自给出的排名序号:
在 RAG(检索增强生成)应用中,混合检索几乎是标配:先用稀疏检索兜住「专有名词、代码、ID 类查询」的精确需求,再用稠密检索兜住「自然语言问句」的语义需求,最后 RRF 合并后送入大模型。这就像找人时既问保安要了精确名单,又让熟悉面孔的朋友指认——两条线索交叉验证,最终答案才可靠。
- 排名倒数的形式1/(k+rank) 在 rank 为 1 时最大,rank 越大衰减越快,天然突出两路都靠前的文档。
- 两路求和把文档在稠密路和稀疏路的排名贡献相加,两路都排名高则总分高。
- 常数 k 的平滑作用k 避免了 rank=0 时无穷大,同时让排名第 1 和第 2 的差距不至于过大。
工具链
工欲善其事,必先利其器。搭建稠密+稀疏混合检索的「找人大系统」,需要几类现成工具。
第一类是稠密向量生产工具:HuggingFace Transformers 和 OpenAI Embedding API 负责把文本转成稠密向量。用 sentence-transformers 这个库几行代码就能加载一个预训练模型,把句子编码成 384 维或 768 维的向量——它就是那个「证件照拍摄师」,按下快门,一张照片出炉。
第二类是稠密向量加速工具:Faiss 是其中的王牌,由 Meta 开源。它内置了 IndexFlatIP(暴力内积检索)、IndexIVFFlat(IVF 聚类检索)、IndexHNSWFlat(HNSW 图检索)等多种索引结构,几行代码就能在千万级向量上实现毫秒级检索——相当于给照片建了一座高效档案馆。
第三类是稀疏检索工具:rank_bm25 库提供了纯 Python 的 BM25 实现,scikit-learn 的 TfidfVectorizer 则可以快速生成稀疏 TF-IDF 向量——它们是「清单管理员」,负责维护倒排列表并打分。
第四类是系统级托管工具:Milvus、Weaviate、Elasticsearch 等向量数据库把稠密索引、稀疏索引、混合检索能力整合成一个服务,提供 RESTful/gRPC 接口和持久化存储,扛住线上流量。其中 Elasticsearch 天然支持 BM25,新版又加上了稠密向量 ANN 检索,混合检索开箱即用——相当于把照片档案馆和清单管理员请进同一个办公室里协同办公。
选型建议顺着数据规模走:百万级以下用 Faiss + rank_bm25 足够轻量;千万级且需高可用,直接上 Milvus 或 Elasticsearch。工具链本身不复杂,真正考验人的是把「照片通道」和「清单通道」的分数对齐、延迟压下去、召回率提上来,这才是工程内功。
评估体系
照片和清单两条路都建好了,怎么证明「混合后真的更会找人」?答案是建立一套评估体系,核心指标是召回率(Recall) 与精度(Precision)。
召回率衡量「该找的人找回来多少」,精度衡量「找回来的人里多少是对的」。在检索场景中常用 Recall@K:取前 K 个结果,看其中命中的相关文档占全部相关文档的比例:
除召回与精度外,延迟(P95 查询耗时)和基线对比同样关键。正确的做法是建立三组基线:纯稠密、纯稀疏、混合检索,在同一份评估集上分别测 Recall@10、Precision@10 和延迟。混合检索并非总能完胜——若语料以精确型号为主,稀疏可能反超;若以同义改写为主,稠密占优。只有用数据说话,才能回答「要不要上混合」这个灵魂拷问。
回到礼堂故事:评估体系就是给找人大作战记分牌。照片派说自己找得全,清单派说自己找得准,别吵——同一份「失踪者名单」上跑一遍 Recall 和 Precision,高下立判。工程落地时尤其要小心:离线指标涨了,线上用户不一定感知得到,所以离线评估后还要做小流量在线实验,用点击率、成交率等业务指标做最终裁决。
- 相关文档集合由人工标注或评估集预先定义,相当于「真正该找到的那批人」的名单。
- TopK 结果集合检索系统返回的前 K 条结果,K 通常取 10 或 50。
- 交集与比值命中数除以总数,得到 0-1 之间的召回率;同时可搭配精度 = 命中数 / K 一起看。
AI 应用场景
企业知识库 RAG 问答
阶段:检索增强生成(RAG)中的召回环节
解决的问题:传统向量检索只走稠密 Embedding,遇到「请查询编号为 A12-B 的设备的保修政策」这类精确 ID 查询时,语义模型容易把编号改写混淆,召回不准;只走稀疏 BM25 又接不住「有没有性价比高的国产替代方案」这类同义改写问法。混合检索同时跑两条通道,RRF 融合排名,既锁死编号,又扩展语义。
具体案例:某制造业企业将十万份技术文档接入 RAG 系统。业务人员提问「TP750 型压力表的密封圈材质」,稠密通道召回「压力表 密封」相关文档,稀疏通道精确定位「TP750」关键词文档,RRF 融合后答案引用准确率从 63% 提升至 91%,幻觉大幅减少。
电商搜索排序
阶段:召回 + 粗排
解决的问题:电商搜索既要理解「适合送女朋友的礼物」这类意图型查询,又要精确匹配「iPhone 15 Pro 256G 原色钛金属」这类规格型查询。单用稠密检索时长尾型号召回差,单用稀疏检索时意图理解差。混合检索让两路互补,再通过线上下实验调节权重 w,找到语义与精确的最佳平衡。
具体案例:某电商平台接入混合检索后,长尾商品搜索的 GMV 提升 12%,「搜索无结果率」下降 38%。用户搜「国产跑鞋 透气」时,稠密路带来「李宁 赤兔 6 Pro」,稀疏路补充「安踏 氢跑 3.0」等精确标签匹配商品,两路合并排序后曝光多样性显著提高。
Python 演示
import numpy as npfrom math import sqrt def cosine(a, b): return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))) def bm25_score(query_terms, doc_terms, doc_len, avgdl, N, df, k1=1.5, b=0.75): score = 0.0 for qt in query_terms: tf = doc_terms.count(qt) if tf == 0: continue idf = np.log((N - df[qt] + 0.5) / (df[qt] + 0.5) + 1.0) norm = 1 - b + b * (doc_len / avgdl) score += idf * (tf * (k1 + 1)) / (tf + k1 * norm) return score corpus = [ "TP750 pressure gauge sealing ring is made of PTFE material", "pressure gauge TP750 seal ring replacement guide", "budget friendly domestic alternative to seal rings", "PTFE seal ring heat resistance test report",]dense_vecs = np.random.rand(len(corpus), 8)query_vec = np.random.rand(8) corpus_terms = [d.lower().split() for d in corpus]N = len(corpus)avgdl = np.mean([len(t) for t in corpus_terms])df = {}for doc in corpus_terms: for w in set(doc): df[w] = df.get(w, 0) + 1 q_terms = "TP750 seal ring".lower().split()dense_rank = sorted(range(len(corpus)), key=lambda i: cosine(dense_vecs[i], query_vec), reverse=True)sparse_rank = sorted(range(len(corpus)), key=lambda i: bm25_score(q_terms, corpus_terms[i], len(corpus_terms[i]), avgdl, N, df), reverse=True) k = 60rrf = {i: 0.0 for i in range(len(corpus))}for i, doc_id in enumerate(dense_rank): rrf[doc_id] += 1.0 / (k + i + 1)for i, doc_id in enumerate(sparse_rank): rrf[doc_id] += 1.0 / (k + i + 1)hybrid_rank = sorted(rrf, key=rrf.get, reverse=True) print("dense top: ", dense_rank[:3])print("sparse top:", sparse_rank[:3])print("hybrid top:", hybrid_rank[:3])
def cosine(a, b):实现稠密向量的余弦相似度,方向越一致得分越高,等价于「证件照片像不像」。idf = np.log((N - df[qt] + 0.5) / (df[qt] + 0.5) + 1.0)BM25 中的 IDF 项,词越罕见,分值越高,等价于「标签越独特越值钱」。norm = 1 - b + b * (doc_len / avgdl)文档长度归一化,长文档被稀释,避免「字多即相关」的偏差。dense_rank = sorted(... , reverse=True)按余弦相似度对文档降序排序,得到稠密通道的排名列表。rrf[doc_id] += 1.0 / (k + i + 1)RRF 核心逻辑:对两路排名各算一次倒数贡献并累加,两路都靠前的文档总分最高。hybrid_rank = sorted(rrf, key=rrf.get, reverse=True)按融合后的总分重新排序,得到最终混合检索结果。
常见误区
误区「稀疏向量维度越大,信息越丰富,检索越准。所以词表越大越好。」我常把词表扩到百万级,结果检索效果反而变差了。
正解维度高只代表「槽位多」,不代表「填得好」。稀疏向量的信息量取决于非零槽位的质量。词表过大导致数据稀疏加剧、哈希碰撞增多、存储倒排列表的指针开销膨胀;更关键的是,BM25/IDF 对低频词的估计会变得极不稳定。就像清单有十万个栏目,但每个人都只勾了十几个,栏目之间的区分度反而被稀释。
为何常见:人们误以为「高维=精准」,忽略了向量检索是「信噪比」的游戏——零值不会带来信息,只有非零槽位的判别力才决定效果。
误区「深度学习都这么厉害了,稠密向量作为神经网络产物,全面碾压 BM25 这类老古董,直接全量换掉就完事了。」
正解稠密向量的优势在「语义召回」,但在精确匹配上反而是短板——对「iPhone 15 Pro 256G 原色钛金属」这种带严格型号的查询,Embedding 会把相似但有细微差别的描述混为一谈;而稀疏检索在专有名词、代码片段、合规词过滤等场景几乎不可替代。业界实践表明,稀疏检索在长尾精确查询上经常反超稠密,两者是互补关系而非替代关系。
为何常见:模型能力被过度神化,忽略了「语义相似」不等于「字面精确」,而很多真实查询恰恰需要字面锁定。
误区「两边分数都算出来加在一起不就行了?加个权重就行,不用考虑量纲。」
正解稠密余弦相似度通常落在 [-1,1],BM25 分数可能从 0 到几十,直接相加等于让稀疏分数主导。正确做法是分数归一化加权求和,或干脆用 RRF 只依赖排名。否则就像给照片相似度打 0.9 分、给清单重合打 121 分,加在一起时照片那票几乎被淹没。
为何常见:直觉上「多路投票」容易想到「直接加票」,忽略了各投票者手里的票面价值根本不对等,必须折算成同一币值。
误区「ANN 是近似的,会漏结果;线上数据量不大,不如逐条暴力算,最保险。」
正解ANN(如 HNSW、IVF)在召回率上可通过参数逼近暴力搜索(Recall@10 达 95%+),但延迟能降低一到两个数量级。数据量过万后,暴力搜索的毫秒级延迟在线上是不可接受的。正确姿势是:用 ANN 做召回,用暴力精排(如对 Top100 重算真实余弦分数)做兜底,兼顾速度与准确。
为何常见:把「近似」等同于「不可用」,忽略了工程里「以粗召回 + 精排」的分层漏斗是常态,而非缺陷。
面试题
Q1. 稠密向量最核心的特征是什么?
- A.维度极高但绝大部分为零
- B.每个维度都有连续的实数值← 正确答案
- C.只包含 0 和 1 两个值
- D.必须由人工手动标注生成
答案:B。稠密向量由 Embedding 模型生成,每个维度都是非零的连续实数值,像一张每处都有明暗信息的照片。A 是稀疏向量特征,C 是二值向量特征,D 错在稠密向量由模型自动学习生成而非人工标注。
Q2. 下列哪一项是稀疏向量的典型代表?
- A.BERT 输出的 768 维句向量
- B.TF-IDF 词法特征向量← 正确答案
- C.图像像素矩阵展开向量
- D.词向量 Word2Vec
答案:B。TF-IDF 特征基于词表构建,维度等于词表大小,但一篇文档只命中少数词,非零值极少,是典型的稀疏向量。BERT 句向量、图像像素、Word2Vec 均为稠密向量。
Q3. IVF 索引加速检索的核心思路是什么?
- A.先聚类分区,再只查最近的簇← 正确答案
- B.构建多层跳转图逐层下钻
- C.对向量做乘积量化压缩
- D.用哈希表做精确查找
答案:A。IVF 先用 K-means 把向量集聚成多个簇,查询时计算查询点与各质心的距离,只进入最近的一两个簇做精确搜索,相当于先分片区再进楼找人。HNSW 多层图是 HNSW 的思路,PQ 量化是压缩方法,哈希表不用于 ANN。
Q4. HNSW 为什么能实现快速近邻检索?
- A.因为把向量按树形结构分区存储
- B.因为用倒排链表连接中文关键词
- C.因为构建了长连接与短连接并存的多层图← 正确答案
- D.因为对所有向量做了降维处理
答案:C。HNSW 构建多层图,顶层边稀疏且跳转距离大,底层边密集用于精确定位。查询时从顶层逐层下跳,像先问门卫再问楼层管理员,最终锁定目标。倒排链表属于稀疏检索,树形分支不是 HNSW 的核心机制。
Q5. BM25 中 IDF 项的主要作用是什么?
- A.对长文档进行惩罚
- B.对常见词降权、罕见词加权← 正确答案
- C.将词频映射到 0-1 区间
- D.计算查询与文档的余弦相似度
答案:B。IDF 衡量词的稀缺程度,「的」「了」等常见词 IDF 低,「球磨机」等罕见词 IDF 高,从而使罕见词在打分中占据更大权重。长文档惩罚由 b 参数控制,词频饱和由 k1 控制,两者都不是 IDF 的职责。
Q6. RRF 排名融合的最大优势是什么?
- A.不需要依赖各路的原始分数,只看排名← 正确答案
- B.能自动学习各路检索的最佳权重
- C.直接对原始分数做加权求和
- D.保证融合结果一定优于每一路
答案:A。RRF 对排名取倒数求和,完全绕开稠密余弦与 BM25 分数量纲不一致的麻烦,只要排序正确即可融合。它不学习权重(B 错),不做分数加权(C 错),也无法保证绝对优于单路(D 错),但工程上稳健易用。
Q7. 以下哪些场景更适合稀疏检索?
- A.精确产品型号匹配← 正确答案
- B.合规敏感词过滤← 正确答案
- C.开放式闲聊意图识别
- D.代码片段关键字定位← 正确答案
答案:ABD。稀疏检索擅长字面精确匹配,型号、敏感词、代码关键字都有明确的字面特征,适合用倒排索引精确定位。开放式闲聊意图语义灵活、同义表达多,依赖稠密向量的语义理解,稀疏难以覆盖。
Q8. 混合检索相比单路检索的收益包括哪些?
- A.语义召回与精确匹配互补← 正确答案
- B.提升长尾关键词的命中率← 正确答案
- C.降低向量存储的内存开销
- D.缓解单一模型的语义漂移风险← 正确答案
答案:ABD。混合检索同时跑语义与词法两路,语义路覆盖改写意图,词法路锁定精确表达,长尾词命中更高,也降低了对单一 Embedding 模型的依赖。但混合检索需要同时维护两套索引,存储开销反而增大,故 C 错。
Q9. 下列哪些是 Faiss 支持的索引类型?
- A.IndexIVFFlat← 正确答案
- B.IndexHNSWFlat← 正确答案
- C.IndexPQ← 正确答案
- D.IndexBTree
答案:ABC。Faiss 内置 IVF、HNSW、PQ 量化等多种 ANN 索引结构,分别对应聚类分区、多层图、量化压缩三种思路。B 树属于传统数据库索引,不在 Faiss 的向量索引体系内。
Q10. 稠密检索在「产品型号精确匹配」上弱于稀疏检索的根本原因是?
- A.低维连续语义空间容易混淆微小 ID 差异← 正确答案
- B.稠密模型训练数据不足导致欠拟合
- C.稠密向量维度太高导致索引退化
- D.稠密检索算法本身不支持等值匹配
答案:A。稠密向量把语义压缩进连续空间,「相近语义」的向量天然靠近,导致「TP750P」与「TP750S」这类仅一位之差但有本质区别的型号在空间中被折叠在一起。稀疏检索用离散词条精确匹配,不存在这种混淆。这并非训练数据不足或索引退化,而是稠密表征「重语义、轻字面」的固有特性。