跳到主要内容

语义过时的 Embedding:当向量不再理解当下

· 阅读需 10 分钟
Tian Pan
Software Engineer

你曾在十八个月前嵌入了知识库。模型没变。分块(chunks)没变。索引很健康,延迟也正常,召回率仪表盘是一条 0.86 的水平线。然而,客服团队正悄无声息地在工单回复中粘贴错误的文章链接,销售机器人在潜在客户询问新产品时不断翻出已弃用的 SKU,而一名内部用户刚告诉你助手“感觉变笨了”,却说不出具体原因。

一切都没坏。是你的嵌入(embeddings)老了。在你的领域中,“post”一词以前指的是博客文章;现在,语料库中有一半的地方用它指代 Slack 帖子、论坛帖子和职位发布(job posting),而你那十八个月前的向量仍将其视为同一个概念。编码这些向量的模型从未见过这些新含义,从未见过新的产品名称,从未见过品牌重塑,也从未见过引入了三个新术语的监管规定——而你的客户现在正不假思索地使用这些术语。检索系统回答了它知道如何回答的问题,但这已不再是你的用户正在提出的问题。

这种失效模式并不是文献中警告你的那种。众所周知的故事是,你升级了嵌入模型,坐标系发生了偏移,导致 v1 向量和 v2 向量变得不可比,你不得不重新索引。那个问题很明显——迁移任务就在 Jira 工单上,有人负责,操作手册上写着“别名切换”。而这里描述的失败恰恰相反:没有人升级任何东西,没有人迁移任何东西,系统却悄无声息地滑向错误,因为你的技术栈之外的世界一直在修改其词汇的含义。

嵌入编码了世界不断修改的意义快照

嵌入是对哪些词语彼此接近的一种冻结的观点。模型的预训练语料库对每个标记(token)都有一个上下文分布,生成的向量编码了这种分布。当你嵌入文档的那一刻,你就让你的检索系统承诺了那个观点。当用法发生转变时,向量并不会更新。向量不知道在你的行业中,“agent”现在默认指的是“LLM agent”,而不是“browser user-agent”或“real-estate agent”。它仍然将分块定位在 2024 年的分布所认为的位置。

Hamilton、Leskovec 和 Jurafsky 的历时性嵌入(diachronic-embedding)研究展示了语言随时间变化的两种模式,这在生产环境的检索中值得铭记。罕见词汇的含义变化速度快于常用词汇,而多义词(本身就带有多种含义的词)的变化速度比单义词快,即使在控制了频率之后也是如此。在 B2B 产品中,变化最快的正是那些你最关心的词:术语、缩写、产品名称、监管速记。这些词在公开网络中频率较低(因此嵌入模型对它们的先验较弱),且在你的领域中高度多义(因此它们正是世界会重塑其含义的词)。

你嵌入的语料库是固定的。流入系统的查询分布却不是。每周,你的用户都会用当前世界的词汇编写查询。每周,“用户现在的命名方式”与“十八个月前向量的命名方式”之间的鸿沟都会变宽一点。这不是模型 bug。这不是索引 bug。这是一个日历问题。

召回率仪表盘是按昨日的相似性概念评分的

这是掩盖 bug 的部分。你根据黄金标准集(golden set)来衡量召回率。黄金标准集是在嵌入语料库时或之后不久构建的,由人类使用当时的词汇编写查询。这些查询与向量匹配得很好——当然如此,它们是用同一种方言编写的。召回率保持平稳。

与此同时,真实用户使用“今天”的词汇编写查询,得到的检索结果虽然在技术上与他们的查询嵌入具有高相似性,但在语义上却偏离了他们的意图,于是他们要么改写查询,要么放弃。无论是改写还是放弃,都不会出现在你的检索指标中。仪表盘报告的是一群已不再存在的查询样本的健康状况。这与根据去年的欺诈模式进行评分的欺诈检测器是同一种类型的 bug——分数是真实的,但分数已毫无意义。

有几种模式可以发现这一点:

  • 运行“当前词汇金丝雀”(current-vocabulary canary)测试:每季度由熟悉客户当前交流方式的人员从头编写一小组查询,并针对相同的检索流水线进行评分。将其命中率与原始黄金标准集的命中率进行比较。其中的差距就是你的漂移量。
  • 观察每个查询的“无点击”和“改写”率:并按查询的新颖程度进行细分。包含嵌入时语料库中未出现标记的查询,可能是索引无法很好服务的查询;如果这些查询在流量中的占比攀升,说明索引正在你脚下老化。
  • 抽取“零重合”样本:即排在首位的检索分块与查询之间没有显著词汇重合的查询。其中一些将是合法的语义转述胜利;但许多将是系统抓取了一个模糊接近的向量,因为索引中没有任何内容真正匹配新术语。
加载中…
References:Let's stay in touch and Follow me for more thoughts and updates