跳到主要内容

Embedding 迁移是新时代的 Schema 迁移

· 阅读需 13 分钟
Tian Pan
Software Engineer

大多数团队在生产环境中第一次更换嵌入模型(embedding model)时,都会将其视为批处理作业。重新运行嵌入器,构建新索引,切换别名,然后部署。延迟保持正常。错误率为零。每个查询都有结果。然而,检索质量会在数周内悄悄下降,而没人察觉。因为症状是“用户抱怨答案感觉不对”,而不是监控面板上的红报警报。

这不仅仅是部署问题,而是一个团队决定盲目进行的架构迁移(schema migration)。旧的嵌入空间和新的嵌入空间是不同的参考系;以前表示“这两个段落关于同一个话题”的余弦几何(cosine geometry)在数值置信度上不再具有相同的含义。以前聚集在一起的文档和查询会以非均匀的方式漂移。在旧分布上训练的重排序器(re-rankers)会开始处理那些不再符合其学习规律的样本。对逐点相关性(pointwise relevance)评分正常的评估套件会漏掉这一切,因为没有任何单个文档移动得太远,但整个图谱发生了旋转。

如果将这种更换视为数据库迁移,几乎所有出错的情况都是可以预防的。如果将其视为批处理作业,那么回归(regressions)就会按照无人负责的进度表悄然降临。

为什么“无缝替换”几乎总是一个谎言

厂商在发布公告时,通常会将新的嵌入模型描述为前一版本的无缝替换(drop-in replacement)。有时维度是匹配的,有时 API 签名是相同的。这暗示了旧向量和新向量生活在同一个世界,你可以用一个模型的查询来匹配另一个模型的文档。

这种观点在某种程度上是错误的,它会破坏生产系统。

维度完全相同的两个嵌入空间是不可互换的。查询向量和文档向量之间的余弦相似度(cosine similarity)只有在两个向量都来自同一个模型时才有意义——无论如何,数学计算都能运行到底,但参考系已经变了。细心监测这一点的从业者会发现,当模型混合时,相关对的余弦得分会从 0.85 以上崩溃到 0.6 左右;原本检索劳动法文档的查询,开始出现恰好共享表面词汇的房地产文档。系统一直在响应,但系统一直在出错。

“无缝替换”应该被解读为“数据库列类型相同”。它并不能说明存储在该列中的值的语义。从一个模型迁移到另一个模型是数据本身的迁移,而不仅仅是列标题的迁移。

无法捕捉异常的评估套件

大多数运行 RAG 的团队都有一套评估套件。它通常根据一组带标签的查询-文档对来计算 top-k 准确率或 NDCG。在迁移前运行一次,迁移后运行一次,比较差值。如果数字持平,就发布。

这里有一个陷阱:逐点相关性评估(pointwise relevance evals)旨在捕捉单个文档在空间中移动过远的情况。而嵌入迁移不会这样做。它们会旋转整个图谱,这种旋转通常很微妙,且在不同内容类型之间是不均匀的。每个文档都移动了一点。对于某个查询,最相关的单个文档在更换后通常仍然在 top-k 中。但 top-3 中可能会出现以前没有的新文档,而重排序器现在处理的候选集与它训练时使用的略有不同。逐点得分几乎没有变化,但下游的答案质量却大幅波动。

能捕捉到这一点的是一种评估两个索引之间结构关系的指标,而不是其中任何一个的绝对相关性。最简单的版本是 top-k 重合度(top-k overlap):对于一个具有代表性的查询集,旧索引检索出的 top-k 文档中,有多少比例仍在新索引的 top-k 中?我们称之为邻域稳定性(neighborhood stability)。95% 的 top-10 结果得到保留的迁移,与只有 60% 保留的迁移是完全不同的操作。前者是常规更换;后者是架构重构,任何针对旧邻域进行调整的重排序器、提示词模板或下游组件,在流量切换之前都需要一套重新调整的计划。

能在嵌入迁移中幸存下来的评估准则应该是这样的:用逐点相关性衡量绝对质量,用邻域稳定性衡量结构漂移,并在留出的任务集上评估端到端的答案质量,以发现用户可见的回归。在这三个维度中,有两个几乎肯定需要在迁移开始前添加;如果你只有第一个,那么你就是在最重要的维度上盲目飞行。

先双写,再双读,然后切换

数据库迁移有一套公认的序列:添加新列、对两者进行双写、回填历史数据行、带校验的双读、切流、删除旧数据。嵌入迁移也应该遵循同样的流程。

一个可行的模式及其顺序如下:

  • 建立一个由新模型填充的并行索引。 即使维度可能与旧索引匹配,也要将其视为独立的索引,拥有自己的别名、监控以及每个向量上的显式版本标签。为了节省存储空间而将新旧向量放在同一个索引中,这在操作上相当于在一个几何库中混合两种坐标系——最终总会有查询查错对象。
  • 从并行索引存在的那一刻起,将所有新文档和更新文档双写到两个索引中。 双写窗口越长,当你准备切流时,回填(backfill)的积压就越少。
  • 按优先级回填历史文档, 优先处理最热的内容。查询最频繁的 10% 的文档通常占据了大部分检索流量;优先处理这些文档意味着在回填完全结束之前,新索引就已经能对阴影流量(shadow traffic)发挥作用了。
  • 在实时流量仍访问旧索引的同时,针对并行索引进行阴影查询。 比较 top-k 重合度、候选集重排序得分以及采样流上的端到端答案质量。这就是邻域稳定性指标发挥作用的地方——它们会告诉你新索引在结构上是否足够相似,可以原封不动地推出,还是下游组件需要先重新调整。
  • 按查询意图(intent)而非用户 ID 进行分流发布。 某些意图的迁移很干净(针对权威文档的事实查找)。其他意图则会出现严重回归(检索依赖于紧密邻域结构的多跳问题)。朴素的按用户百分比发布会把这些情况混在一起,掩盖了哪些内容类别需要改进;按意图分流发布则能暴露这些问题。
  • 在预发环境中成功执行过至少一次回滚之前,保持两个索引同时在线。 双索引在操作上的核心意义不是切流,而是回滚。如果你无法证明通过切换开关能在不到一分钟内将流量切回旧索引,那么你制定的不是迁移计划,而是一次单向部署。

两个索引共存的窗口期是昂贵的。这种成本是确保迁移安全的代价。那些因为存储翻倍而试图跳过这一步的团队,往往会在两周后回归问题浮出水面时,不得不重新运行整个迁移过程。

多数团队在迁移中期才发现的重排序器依赖

交叉编码重排序器是在由特定向量模型生成的候选集上进行训练的。训练数据隐式地编码了该模型产生的误报分布 —— 即那些通过检索阶段并需要被过滤掉的“近似错误”。更换向量模型后,这种分布也会随之改变。一些旧的误报不再出现,而新的误报会产生。此时,重排序器正在解决一个它从未被训练过的问题。

幸运的情况下,这表现为重排序器精度的轻微下降。而在不幸的情况下,新的候选集差异巨大,导致重排序器的相对排序在整个内容类别上变得不可信 —— 通常这些类别正是向量迁移变动最大、也正是重排序器原本工作量最重的部分。

专业的做法是将重排序器的重新训练视为向量更换的一个计划内依赖,而不是等火烧眉毛了才去处理。这意味着:在双写窗口期间,从新索引中收集带标签的候选集,针对这些候选集重新训练或微调重排序器,并且只有在重排序器通过新模型候选集的验证后,才切换流量。如果重排序器是第三方的且无法重新训练,迁移计划则需要不同的缓解方案 —— 通常是扩大候选集的宽度以补偿排序噪声,或者接受针对特定意图的可调节质量退化。

令团队意外的成本构成

重新对语料库进行向量化并非免费,但令团队感到意外的开销很少是推理成本。按目前的公开定价,使用小型向量模型对 10 亿个 Token 进行重新向量化的费用仅为数十美元;使用大型模型则在几百美元左右。这确实是一笔钱,但很少成为瓶颈。

真正棘手的限制因素各不相同:

  • 索引存储在迁移窗口期会翻倍。 10 亿个 1024 维的向量在计算索引开销前大约有 4 TB。两个索引就是 8 TB。大多数托管向量数据库将存储作为主要的计费项,而双写窗口可能是团队预算负责人第一次看到生产索引实际成本的时候。
  • 对于自托管的向量化模型,GPU 吞吐量是瓶颈。 在 70 亿参数的模型上,单个中端 GPU 每秒产生的向量仅有几千个 Token;在单台机器上处理 10 亿文档的语料库需要以天计,而非小时。重新向量化的集群需要在切换前几周就准备就绪,而不是在切换当天的早晨。
  • 重排序器的重新训练有其自身的标注和计算成本,这并不在预算中的向量化项下。那些将迁移范围定义为“重新向量化语料库”,而没有将其定义为“重新向量化语料库、重新训练重排序器并重新验证评估套件”的团队,往往在进行到一半时回来申请追加预算。
  • 迁移窗口也有质量成本。 在双读期间,查询承担了更多的工作;在灰度发布期间,系统运行在较低置信度的状态下。这种成本是真实存在的;在窗口期内,它表现为用户可见的检索质量下降。将其定价为零,正是导致团队迫于压力跳过双写环节的原因。

计划的核心数字不是新向量的成本。它是两个生产级索引同时运行的整个窗口期的总成本。

谁该为用户可见的质量倒退负责

大多数向量迁移都跨越了组织架构的断层。平台团队负责索引、向量化流水线、双写工具和切换流程。产品团队负责评估套件、撰写“本周回答感觉变差了”的用户调研,以及出问题时的值班。迁移由平台团队执行,却由产品团队评判,而且两个团队都没有掌控将他们联系在一起的指标。

这种失败模式是可以预见的:平台团队发布了一个运行指标良好的干净迁移,产品团队开始看到用户反馈的缓慢恶化,几周后终于有人把这两件事联系起来,此时回滚要么起作用(因为旧索引仍然在线,这样还好),要么不起作用(因为旧索引为了释放存储空间已被停用,这种情况下团队只能忍受数周的质量倒退)。

解决办法是组织上的,而非技术上的:为“迁移窗口期的检索质量”设立单一负责人,该负责人有权根据用户可见的信号(而非仅仅是运行指标)中止或回滚迁移。你负责的评估套件需要包括邻域稳定性(neighborhood-stability)和端到端回答质量,而不仅仅是单点相关性。你运行的值班轮换需要在窗口期内包含平台团队,这样在注意到质量倒退的同时,有权切换开关的人也会被传唤。

架构层面的认知

向量模型并不是一个由单一团队拥有的特征提取器。它是索引器、检索器和重排序器之间的一份契约 —— 并且越来越多地成为根据检索上下文进行生成的生成器。契约的迁移需要版本控制、双写窗口、验证门禁和回滚路径,就像数据库迁移一样。这份契约是隐式的(编码在浮点几何空间中,而非列类型中)这一事实并不会降低其契约属性;它只会让契约失效时更难被发现。

内化了这一点的团队不再将向量更换视为平台任务,而是将其作为跨团队迁移来运作,配备指定的负责人、预设的检查点和明确的验收标准。迁移在窗口期内耗时更长、成本更高。但它也避免了那种无人能解释却让所有人买单的隐性质量倒退。

下次有人提议“直接重新向量化并切换”时,正确的反应不是问“时间表是什么”,而是问:“请给我看双写计划、邻域稳定性目标、重排序器重新训练计划和回滚手册。”如果这四样东西还不存在,那么迁移还没准备好 —— 把它当作批处理任务来对待,正是专业流程所要防止的失败模式。

References:Let's stay in touch and Follow me for more thoughts and updates