跳到主要内容

AI数据版本控制:团队发现得太晚的数据集-模型耦合问题

· 阅读需 10 分钟
Tian Pan
Software Engineer

你的模型精度在某个夜晚突然下降了8%。模型代码没有任何改动,没有发生任何部署,评估套件是绿色的。于是你花了一周时间调整超参数、修改提示词、对比检查点损失——最终有人注意到,三天前特征流水线里落地了一次Schema迁移。一个字段从NULL改成了空字符串。就这样,就是这个变化导致了回退。

这是生产ML系统中最常见的故障模式,与模型质量几乎毫无关系。问题的根源在于大多数团队被坑过之后才会补上的一个结构性缺口:数据版本和模型版本紧密耦合,但它们由不同的工具追踪、归属于不同的团队

问题不在于团队不知道数据重要。每个ML团队都知道数据重要。问题在于团队组织工具的方式,在"数据"和"模型"之间制造了一条隐形边界,使得一侧的变更可以悄无声息地传播到另一侧。

双团队问题

大多数组织都有某种形式的这种分工:数据工程团队负责流水线、Schema和特征基础设施,ML团队负责训练代码、模型制品和实验。这种分工在组织上是合理的,它对应不同的技能集和不同的生命周期。

但这也制造了一个盲区。数据团队将Schema变更视为日常维护——更新列类型、回填字段、废弃特征。ML团队将模型回退视为模型问题——训练循环、评估集或服务基础设施一定有什么地方变了。两个团队的默认排查思路都不是"上游发生了什么变化?"

这就是为什么67%的规模化AI组织报告,至少有一次关键问题与统计不对齐超过一个月未被发现有关。信号之所以延迟,正是因为最可能察觉到症状(模型质量下降)的团队,对根因(数据变更)没有可见性。

排查模式是可以预测的:ML团队发现回退 → ML团队排查模型变更 → ML团队一无所获 → ML团队升级 → 数据团队最终浮出相关Schema变更 → 所有人同意下次在Slack加一条通知。如此循环。

生产中"数据版本"的真实含义

"数据版本控制"听起来很直接——像给代码提交打标签一样给数据集打标签。但实际上,模型的"数据"并不是单一的制品,而是一个分层的栈:

原始源数据:客户记录、事件日志、API响应。这里的变更通常由你无法控制的上游系统驱动——供应商修改了Schema,产品发生了迁移,GDPR删除请求从训练历史中移除了记录。

计算特征:聚合、嵌入、派生信号。这些是你的特征流水线,它们受业务逻辑变更影响("我们改变了30天滚动均值的计算方式")。特征逻辑的变更很少触发ML回归测试,因为它们看起来像数据,而不像代码。

训练快照:用于训练某个模型版本的特定数据切片。大多数团队能告诉你训练数据的日期范围,但很少有人能重建某次训练运行中输入的精确特征值。

服务时特征:推理时喂给模型的内容。这些应该与训练时的分布一致,但往往并不一致——尤其是在训练运行之间更新了特征流水线的情况下。

这些层中的任何一层都可以独立变更。如果没有每一层与依赖它的模型版本之间的显式耦合,你就拥有了一个可以在没有清晰因果链的情况下出故障的系统。

血缘图:将故障追溯到源头

在生产中真正有效的解决方案不是更好的监控(尽管监控有帮助)。而是将数据版本和模型版本作为同一依赖图中的节点来处理,用边使上下游影响变得显式可见。

血缘图模式的工作方式如下。每个重要的制品——数据集快照、特征计算、模型检查点——都是一个节点。每个依赖关系——"这个特征是在这个git SHA的流水线代码下,从这个数据集版本计算而来"——都是一条有向边。当一个节点变更时,你可以沿图向下游游走,找出哪些下游制品现在可能已失效。

实践中这意味着:

当数据团队将一列从NULL迁移到空字符串时,这个变更应该自动标记所有在迁移前特征Schema上训练过的模型版本。ML团队不需要知道迁移发生了——血缘图会在生产回退告知他们之前,就提示他们的模型可能已经过时。

当训练任务运行时,它不仅应该记录"我使用了哪个数据日期范围",还应记录"我消费了这张特征表的哪个快照ID"。Apache Iceberg原生支持这一点——你可以将特征生成固定到特定快照ID,使训练任务可重现且可审计。Iceberg v2的行级删除支持使得高基数特征表(需要高效upsert)的实践变得可行。

当模型团队想理解为什么3.2版本比3.1版本表现更好时,他们可以对比特征血缘,而不仅仅是训练代码。通常答案是"3.2是在特征流水线改进后的数据上训练的"——没有血缘,这个因果解释是不可见的。

生产中经得住考验的三种模式

1. 使用不可变制品引用的快照固定

训练任务应该通过不可变ID引用数据制品,而不是通过逻辑名称("今天的特征")或日期范围。Delta Lake的时间旅行语法和Iceberg的快照标记都支持这一点。原则是:可重现性要求三个月后重新运行同一个训练任务能产生相同的模型,这要求数据输入完全可恢复。如果你的训练命令包含WHERE event_date > '2025-01-01'但没有固定表快照,你没有一个可重现的训练运行——你有一个随底层表变化会悄然产生不同结果的训练配方。

2. 版本感知的特征存储

一个不追踪哪些模型版本消费了哪些特征版本的特征存储,主要用处是减少训练-服务偏差——这是真实的好处,但并不完整。更有价值的能力是反向查找:给定一个服务异常,当时激活的是哪个特征版本?给定一个特征流水线变更,哪些在线模型现在需要重新评估?

生产级特征存储支持同时部署多个特征版本,允许你独立于回滚模型而回滚特征计算。这对于LLM驱动的系统尤为重要,因为生成特征的嵌入函数可能独立于下游模型发生变化。

3. 部署前的变更影响传播

数据流水线变更在落地生产前应该经过影响分析。这是数据版本的类型检查器。在Schema变更部署之前,系统应该识别:哪些特征流水线读取了这一列,哪些模型版本在这些特征上训练过,哪些在线服务端点依赖于这些模型。

DataHub和OpenMetadata等工具提供了列级血缘。组织层面的挑战是确保这个分析真正成为部署的门控条件——而不仅仅是被忽视的信息。在这方面成功的团队,将"无下游ML影响"作为数据流水线CI中的必要检查,而不是可有可无的东西。

让工具真正发挥作用的组织变革

数据-模型血缘的工具已经成熟。DVC、MLflow、Iceberg、Delta Lake、特征存储——这些问题都有成熟的解决方案。更难的问题是组织层面的:让数据团队和ML团队进入同一个血缘系统,使图真正连通。

常见的失败模式:数据团队在他们的数据目录中追踪血缘。ML团队在MLflow中追踪实验。两者之间的边界是一个文件路径或日期范围,由一个团队非正式地传给另一个团队。图恰好在大多数回退起源的地方断开了。

有效的做法是将特征物化步骤——数据团队拥有的流水线生产ML团队消费的特征——视为带有明确版本合约的联合所有权边界。数据团队承诺:"当我们更改这个特征的计算时,我们会对其版本化,而不是原地更新。"ML团队承诺:"当我们训练模型时,我们会记录消费了哪个特征版本。"

这与其说是工具问题,不如说是合约问题。工具在合约存在之后负责执行合约。跳过合约协商的团队最终会在边界两侧都有完整的血缘,而中间有一个缺口。

衡量你的版本控制是否真正有效

你的数据-模型耦合已断裂的最清晰信号:在生产回退排查中,你无法在一小时内回答以下三个问题。

  1. 这个模型究竟是在什么数据上训练的?
  2. 在这次回退出现前的两周里,数据流水线发生了什么变化?
  3. 哪些在线模型依赖于发生变化的那个特征?

如果回答这三个问题中的任何一个都需要发Slack消息或翻阅日历事件去回忆"还记得我们那时改过那个东西吗?",你的版本控制基础设施就有缺口。目标不是完美的工具——而是让这三个问题仅凭工具就能回答,不需要用人的记忆作为检索机制。

做到这一点的团队通常还会将第四个问题作为主动检查来监控:"每个在线模型训练之后,哪些数据制品发生了变化?"每周运行这个检查并将结果路由给模型负责人,可以在漂移引发回退之前就发现它,而不是等到事后。

32%的问题

大约32%的生产ML流水线在部署后的前六个月内经历了显著的分布偏移。这不是模型质量问题——大多数流水线在上线前都经过了验证。这是数据漂移问题,它在没有人将数据版本历史与模型监控告警连接起来的情况下悄然积累。

那些避免落入这32%的团队,未必使用了更好的模型或写了更好的训练代码。他们弥合了数据版本控制系统和模型版本控制系统之间的缺口,使得一侧的变更能自动在另一侧浮现为信号。

这个结构性连接——横跨数据-模型边界两侧的血缘图——就是将"模型回退了"从一场调试远征转变为可追溯、可预防事件的关键。这不是光鲜的基础设施,它不会出现在任何基准测试里。但这就是"浪费一周重新调优提示词"和"花一小时追溯字段类型变更的下游影响"之间的差距。

在需要它之前就建好这张图。等你真正需要它的时候,你已经没有时间去建了。

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