那些在悄无声息中重新排列你整个语料库的嵌入模型升级
一个新的嵌入模型登上了排行榜。它比你 18 个月前发布的模型得分更高,API 只需更改一行代码,甚至维度也一致。有人提了一个工单:“升级嵌入模型”。这看起来就像更换一个日志库一样简单。
事实并非如此。嵌入模型并不是你检索系统的一个普通组件——它就是你检索系统所处的坐标系。更换它并不会改进你的索引,而是会让索引失效。而最残酷的地方在于,系统并不会崩溃。没有异常,没有失败的健康检查。你的搜索只是开始返回微妙不同的结果,而在 RAG 管道中,“微妙不同”意味着不同的文档被喂给了模型,也就意味着不同的答案最终传达给用户。
这种失效模式不会在代码审查中显现。差异只有三行代码,但受波及的范围却是你索引过的每一个文档。
向量仅在其自身的模型内部具有意义
每个嵌入模型在训练期间都会学习其自身的内部含义表示。输出是一个浮点数组,但该数组并不是对文本的通用描述——它是该特定模型所创造的空间中的一组坐标。
某个模型中的第 47 维度可能追踪的是接近“纹理”的东西。而另一个模型中的第 47 维度可能追踪的是情感,或者根本无法命名。这两个模型从未被训练成在轴的含义上达成一致。它们无法做到,因为它们从未见过彼此。
这产生了一个严重的后果:来自模型 v1 的向量和来自模型 v2 的向量是不可比的,即使它们描述的是完全相同的句子,即使两个数组都有 1536 个维度。它们之间的余弦相似度只是一个数字,而这个数字毫无意义。让相似度对应于语义接近度的几何特征,仅存在于单个模型的空间内。
因此,当你“升级”模型时,你并没有为语料库获得更好的向量。你获得的是生活在与你已经存储的数百万个向量不同的宇宙中的向量。旧索引并没有降级。它只是用一种新查询向量无法理解的语言编写的。
唯一正确的做法是全量重新嵌入(re-embed):让每一个文档都通过新模型运行,并从头开始重建索引。没有增量升级。不存在“用 v2 嵌入新文档并让旧文档逐渐淘汰”这种做法。这最后一种想法正是许多团队悄无声息地摧毁其检索质量的原因。
迁移了一半的索引比任何一个单一模型都要糟糕
想象一下那个诱人的捷径。新模型上线了。新文档使用 v2 进行嵌入。旧文档保留其 v1 向量。同一个索引,混合内容,无需停机。这听起来很务实。
你所构建的是一个跨两个不兼容坐标系计算相似度的索引。使用 v2 嵌入的查询会给每个文档评分——但针对 v1 文档的评分是噪声,而针对 v2 文档的评分才是信号。排名会将它们交织在一起。有些结果是真正的邻居;有些则是两个无关几何空间重叠产生的意外。
这种行为产生的后果确实很难调试。同一个查询对于最近索引的文档返回了极佳的结果,而对于较旧的文档返回的结果却很差。精确率和召回率都会下降,但并不是均匀下降——它们是作为文档最后一次嵌入时间的函数而下降的,而团队中没有人会对这个变量进行绘图。你的可观测性仪表盘显示“检索质量下降了 12%”,而这个汇总数字掩盖了一个事实:一部分文档从良好变成了不可用,而另一部分则保持正常。
一个团队会花一周时间调整分块大小和重排序器权重,然后才有人想到去问哪个模型嵌入了哪一行。混合索引并不会自我声明。它只会让你的系统表现得像是有双重人格,而且不给你任何可以拆分指标的字段。
如果你从中学到一条规则,那就是:单个索引或单个集合必须包含来自完全相同的一个模型版本的向量。混合并不是一种有副作用的权衡。它是一个正确性缺陷。
更高的基准测试分数并不是对你语料库的承诺
升级工单获得批准的原因通常是排行榜上的一个数字。新模型在 MTEB 上的表现超过了旧模型,所以它的检索效果一定更好。这种推论比看起来要脆弱得多。
通用嵌入基准测试衡量的是在各种公共数据集混合下的平均性能。你的语料库并不是那种混合体。它有自己的词汇表、自己的文档风格、以及自己的查询措辞分布。针对特定领域检索的研究发现,一个模型在通用基准测试上的得分可能与它在特定领域的得分不相关——一旦你直接在金融、法律、医学以及充满内部术语的语料库上进行测量,排行榜就会被重新洗牌。
因此,一个基准测试得分更高的模型在你特定的数据上检索效果更差,这完全是可能的——而且经常被观察到。新模型可能以某种方式进行了微调,有助于处理通用网络文本,但却损害了你支持团队编写的那些密集的、充满缩写词的文档。在你使用自己的查询在自己的语料库上进行测量之前,你不会知道结果。
这就是为什么升级决策在需要 API 密钥之前,更需要一个黄金数据集。收集几百个最常用且最关键的查询,每个查询都配对应该被检索到的文档。针对这两个模型运行该集合。进行对比。如果新模型在你自己的数据上没有胜出,那么排行榜就没有投票权。基准测试告诉你该模型擅长作为一个通用模型,它并没有告诉你它擅长作为你的模型。
