ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Embedding模型选型与向量检索全链路匹配实践

Embedding模型选型与向量检索全链路匹配实践

从标题就能看出,这篇是讲 Embedding 模型选型和向量检索全链路匹配的。我做了这么久 RAG 和语义搜索相关的项目,最大的感受就是:很多团队在模型选型这件事上太草率了——随便拿个开源模型就上线,结果召回率一塌糊涂,重排怎么调都救不回来。所以这篇我打算把“选模型”这件事掰开揉碎讲清楚,从评估指标、场景适配到检索索引的配套选择,一次性说明白。

1. 为什么选错模型的代价如此之高

1.1 向量质量决定了整个系统的天花板

很多初学者会有个误区,觉得 Embedding 模型就是把文本变成一串数字,随便选一个差不多的就行。但实际上 Embedding 模型的质量直接决定了向量检索的上限——检索只是把向量拿去做相似度计算,它本身不产生语义理解能力。如果模型输出的向量压根没有准确表达文本语义,后面的所有流程都是在错误的基础上做优化,无论索引调得多快、重排模型多强,都是在放大噪音。

举一个实际的例子。我做过一个项目,需要从大约 100 万条客服工单里检索相似问题。最初用的是某个通用领域的开源中文向量模型,维度是 768。在自建的标准问答对上测试,精确率看着还行,但噪音词一多,比如“如何退换货”和“退换货的流程是什么”,这两个在语义上几乎等价的句子,余弦相似度竟然只有 0.82,而“如何退换货”和“如何投诉”的相似度却高达 0.78。这个区分度太低了,导致 100 万条数据里做 Top-10 召回时,真正相关的工单经常被挤到后面。

这就是典型的“模型能力不足以支撑业务场景”。向量质量不达标,不管是调阈值还是换索引算法,都只是隔靴搔痒。

1.2 一次选错,后期返工成本极高

有人可能觉得,模型先凑合用,等后面效果不行了再换。但要知道,Embedding 模型如果换了,所有已经向量化的数据都得重新处理入库,索引要重建,缓存要清空,端到端的测试要重新跑。

我记得有一次,一个项目刚开始为了快速验证,临时用了一个轻量级模型(当时的考量是省资源),跑了 500 万条数据。后来效果实在不行,换了一个精度更高的模型。结果整整花了两天时间重新生成向量和重建索引,中间还因为两套向量表征空间不一致,导致 A/B 测试数据完全不可用。所以我的建议是,在项目启动阶段,哪怕多花几天时间评估选型,也比后期推倒重来要划算得多。

2. Embedding 模型选型的核心维度

2.1 看语言和领域,而不是看名气

选模型第一件事不是看排行榜,而是看它是不是为你的场景服务的。

  • 语言覆盖:如果业务是纯中文,建议优先看专门做过中文优化的模型,4 字节中文 token 处理能力有没有、分字还是分词、对口语化表达的支持怎么样;如果是中英混合场景,前面说的常识中,跨语言语义对齐能力就特别重要。我用过一个多语言模型,英文效果很好,但中文口语里“砍一刀”这种词直接翻车,检索完全失效。
  • 领域属性:通用领域模型放在金融、医疗、法律等专有术语密集的场景里,效果会肉眼可见地下降。专业场景要么使用垂直微调的模型,要么在通用模型基础上用领域语料做继续训练。我做过一个法律场景的检索项目,通用模型对“不可抗力条款”和“情势变更”这种概念区分得很模糊,后来换了法律语料微调的模型,召回率提升了差不多 12 个百分点。
  • 输入长度上限:如果你的文档动辄几千字,模型的最大序列长度就很重要。有些模型支持 8k token,有些只有 512。超过上限的文本会被截断,截断意味着语义丢失,检索结果自然不准。

2.2 看输出的向量表达方式

这里头有一个经常被忽略的点:向量维度不是越高越好。

维度越高,理论上表达的信息容量越大,但内存开销也急剧增长。我们算一笔账:如果一条文本是 1024 维的 float 数组,那就是 4KB。1000 万条文本就是 40GB 内存。如果不做量化、不上 GPU,单靠普通云服务器根本扛不住。

所以我们在选维度时,要结合两个约束:

  • 语义区分度是否足够;
  • 数据规模和硬件成本是否可控。

通常 768 维对绝大多数业务够用了,1536 维以上更适合图文跨模态或者超大语料库场景。

还有一个点是向量正规化。很多模型输出的向量并不是归一化的,而余弦相似度在未归一化的向量上会受到模长影响,导致检索偏向长文本。所以实操上,不管选哪个模型,我都会在入库和查询之前都做一次 L2 归一化,把向量模长统一。这是个小细节,但影响很大。

2.3 看模型背后配套的评测数据和实践经验

现在开源社区有很多模型卡片,上面有各种 Benchmark 数据。但我的经验是:公开评测集的分数和实际业务效果之间的相关性是有限的。尤其是中文场景,很多评测集本身就有偏向。

所以我会用自己业务里的真实数据构建一个 mini 评测集。数量不用多,300 到 500 对“查询 + 相关文档”就行。然后用 Recall@K 和 MRR 这两个核心指标跑一遍对比,看哪个模型在你的数据上真的表现好,这样比看任何线上榜单都靠谱。这个习惯我从一开始做检索系统就坚持到现在,帮我避开过好几次“高分低能”的坑。

3. 向量检索方案与模型选择的匹配逻辑

3.1 不同的索引算法,对向量分布有不同的感知

选好了模型,向量数据拿到手,下一步是建立索引。但这里有个匹配问题:不同索引算法对向量空间分布的假设不一样,而 Embedding 模型产出的向量分布天然存在差异。如果二者不匹配,检索效果也会被拖累。

  • 暴力检索(Flat):最直白的方式,全量算相似度,没有任何近似假设。数据量小于几万条时非常实用。缺点是大于百万级别时延迟不可接受。
  • HNSW:基于图结构的近似最近邻算法,召回率通常很好,检索也快。但对内存的消耗也比较大,高维向量会尤其明显。
  • IVF(倒排索引):先聚类再检索,把向量空间划分为多个区域。它假定数据在空间上有一定的聚类特性。如果向量本身的分布特别均匀、没有明显的聚类,IVF 的召回率就会下降。

在实操中,我一般遵循这个策略:

  • 数据量在 10 万以下,直接上 Flat 或 HNSW,省心;
  • 100 万以上,需要先看数据分布再定方案。如果业务有明显的类别分层,IVF 效果好;如果语义跨类比较普遍,HNSW 更稳。

为此我在项目早期会做一个向量分布可视化,把向量降维到二维平面上看。比如用 PCA 或者 t-SNE 看一下,这个步骤能直观感受到模型输出的向量是不是“成团”的。聚类越明显的,IVF 越适配;分层不明显的,老老实实 HNSW。

3.2 量化压缩要谨慎

百万级以上数据,为了省内存,很多团队会做向量量化。常见的有 PQ(乘积量化)和 SQ(标量量化)。

标量量化相对保守,一般是把 float32 变成 int8,仅损失少量精度。在 Recall@10 上通常只掉 1% 到 2%,性价比很高。乘积量化更激进,把向量切分成子空间再做压缩,内存能降到原来的 1/4 甚至更低,但召回掉得比较猛。如果必须用 PQ,我建议目标准确率下降控制在 5% 以内,否则就得考虑换更大的内存机型,而不是牺牲检索质量。

我还遇到过一个有意思的现象:某向量模型输出的分布比较紧凑,做 SQ 压缩后召回率几乎没掉;换了另一个模型,同样是 SQ,Top-10 直接掉到原来的 0.9 倍。原因是不同模型出来的向量范围分布差异很大,这一点也侧面印证了选型和索引是联动的,不是孤立的。

3.3 距离度量的选择

很多向量数据库默认使用余弦距离,但有些索引算法内部实际用欧氏距离。在向量做过 L2 归一化之后,这两个度量是等价的。这个知识点能帮你在配置索引时避免歧义。

我见过一个同事的项目,查询向量没有归一化,索引用的是欧氏距离,结果检索出来一大堆“短文本”,就是因为模长干扰了距离度量。后来把所有向量统一归一化,效果立刻回归正常。这是一个特别不起眼但特别容易踩的坑。

4. 实际场景中的模型评测与选型流程

4.1 从模型到检索的完整评测漏斗

我通常把评测分成两个阶段。

第一阶段是“离线模型评估”。用预先标注好的数据集,把多个候选模型统一跑一遍。这里要保证同一套向量化代码、同一套查询集合、同一个索引算法,只改变模型变量。核心指标:

  • Recall@K:前 K 条结果里包含相关文档的比例;
  • MRR:第一个相关结果排位的倒数均值;
  • 延迟 P95/P99:在线场景更重要。

第二阶段是“在线指标验证”。离线效果好的模型,还要接到真实的检索链路上看端到端效果。比如在 RAG 场景里,除了看检索召回,还要看生成质量。有些模型召回不错,但召回的片段经常是噪音段落,反而干扰生成。这一步不跑,整体效果还是不放心。

4.2 两个实战案例复盘

我复盘两个有明显对比的项目。

第一个项目是电商问答检索。候选模型有 A(通用开源)、B(中文优化开源)、C(闭源大模型提供的向量接口)。线下评测阶段,B 的 Recall@10 比 A 高 8%,几乎接近 C 的水平。而且 B 的本地部署成本远小于 C 的调用成本,最终选了 B。上线后效果也符合预期。这说明,很多场景下,一个针对性优化过的中小模型不一定输给大模型接口。

第二个项目是英文论文语义检索。线下评测里,某个英文专用 Embedding 模型表现突出,但它的输入长度上限太低,论文摘要超过上限就被截断,导致长文档丢失核心信息。后来选了一个支持更长上下文的模型,虽然单项精度略低,但完整覆盖率更好。这提醒我:不要只看 Benchmark,要结合文档长度、切分策略一起看。

4.3 评测工具链建议

我现在的标准跑测流程是:

  1. 用脚本批量调 Embedding 服务,把候选文档向量化并落盘,保存为 npy 或 parquet;
  2. 用向量检索库(本地方便调试)在内存里建索引,跑查询集,输出指标;
  3. 用可视化的方式把检索结果打印出来,人工抽样看 Top-5 里有没有不相关但被召回的样本;
  4. 记录指标到统一的 CSV,多轮迭代。

这套流程不需要太复杂的框架,关键在于数据集要贴近真实业务。我见过不少团队拿公开的问答对做评测,最后上线效果和评测结果差距大,归根到底就是评测集和真实分布不一致。

5. 选错模型后的常见问题与排查思路

5.1 症状一:检索结果“看起来相似但语义无关”

如果 Top-10 召回的结果看起来都有字面上的词重叠,但语义上八竿子打不着,几乎可以断定:模型对语义的理解能力不足。

排查步骤:

  1. 检查是不是用了很小的模型,参数量不足导致语义表征能力弱;
  2. 检查有没有做归一化,是否因为文本长度导致偏向;
  3. 用一对高相似度的同义词句对测试,看模型输出向量的余弦相似度是否理想(一般要大于 0.85)。

解决办法无非两个方向:一是换更强的模型;二是给检索加一层重排,用交叉编码器重新打分。但重排只是兜底,它解决不了向量空间本身不准确的问题,所以原模型该换还是要换。

5.2 症状二:检索结果对关键词过于敏感

这种情况是,明明语义上很相关但字面表达差异较大(比如“工资没发”和“薪酬拖欠”)就召不回来,而字面上有重合的反而排前面。

这通常是模型对同义改写的能力一般,或者训练语料里缺少口语化表达。我常试的一个小技巧是:把业务里的高频口语短语拿出来,做同义改写生成,喂给模型做小规模的对比测试。如果仍然不行,那就要考虑用领域数据微调。不过微调的工程量也要评估好。

5.3 症状三:向量检索服务本身报错或延迟高

延迟高不一定是模型的问题,也要排查索引和基础设施。

大概思路:

  • 先看 query 向量化和检索分别耗时多少。再决定瓶颈在哪。
  • 如果向量化耗时长,检查模型推理有没有用 GPU、有没有做 batch 化;
  • 如果检索耗时长,看索引类型、看数据量、看是否命中缓存;
  • 如果内存爆了,检查向量维度、量化方式、以及底层存储的压缩配置。

这里有一个容易忽略的点:向量的入库速度。如果数据量很大,单线程逐条向量化入库会非常慢,需要做并行化和批处理。有一次我没注意这一点,500 万条数据整整入库了两天,后来加了 16 个并发进程,时间缩到了 4 个小时以内。

6. 一些我踩过坑之后总结的经验

最后分享几个我自己的体会。

先拿少量真实数据做端到端验证,再决定全量方案。我无论做什么项目,都会先抽样 1 万条左右的数据,快速跑通整个链路,包括向量化、索引、查询、重排,确认无误之后再铺开做全量。这个习惯帮我避开了很多仓促上线的灾祸。

重视维度、量化、索引三者的组合效果。模型选型不是只看模型本身,它要和向量维度、量化策略、索引算法放在一起评估。同样的模型配不同的索引策略,效果差别会很大。做选型的时候,不能只盯着 Embedding 模型的指标,一定要做组合测试。

不要盲目迷信“更大的模型”。大模型接口的向量效果好,但成本和延迟也高。如果业务量很大,还要考虑调用成本和向量维度带来的存储成本。一些中小型开源模型在垂直领域的表现可能更实用。我在某个场景实测过,选用 768 维的中型模型,配合合理的重排,整体效果完全够用,成本和延迟却低了很多。

监控线上召回质量,形成反馈闭环。上线之后,建议定期抽检检索结果,人工评估 Top-10 里有多少有效结果。有条件的话,把用户点击了哪些结果的用户行为记录下来,反哺离线评测集。这样模型选型就不再是一次性的事,而是可以持续优化的循环。

最后再说一个小技巧:如果你有多个 Embedding 模型要对比,不妨把它们的向量都存下来,然后直接算模型 A 的检索结果和模型 B 的检索结果之间的交叠率。交叠率低说明两个模型对语义空间的理解分歧大,这时候不用看指标也知道,业务上最好做一个稳定性测试再决定选谁。这个对比方式很直观,也省去很多无谓的争论。

模型选型这件事真的值得你多花时间。向量检索的整个链路里,Embedding 模型就是地基,地基不牢,上层建筑再漂亮也稳不住。希望这篇的经验能帮你少走点弯路。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进