跳到正文
原文
Hugging Face Blog·· 10 小时前精选AI 评分60

Hugging Face 提出模型集成契约 MIC,用缩小模型在 CPU 上做 CI 测试

Transformers Model Integration Contracts

AI 导读

Hugging Face 提出模型集成契约(MIC),用按真实 checkpoint 配置缩小的 fixture 模型在 CPU 上运行快速集成测试。

推荐理由

Hugging Face 用缩小的 fixture 模型在 CPU 上复现了 UMT5 注意力回归,读者可据此了解一套低成本 CI 测试方案的完整做法。

正文 · AI 翻译

2026 年 7 月 30 日,Transformers 为 T5 启用了 PyTorch 的缩放点积注意力(SDPA)。副作用是,UMT5 的解码器可以关注到未来的 token。在训练期间,它可以偷看它本应预测的答案。没有任何崩溃,这个 bug 在 main 分支上停留了将近两个月。

后来我们回放那些 commit 时,一个在 CPU 上运行的小型 UMT5 模型在每一个 commit 上都标记出了这个问题。本文讲的就是那个模型以及围绕它构建的测试。

为什么 bug 会进入 main

Transformers 支持超过 750 种模型类型,其测试套件规模庞大:main 分支上有 88,000+ 个 CPU 测试,外加每日运行中 200,000+ 个 GPU 测试。模型测试只是其中一部分:我们还会测试 pipeline、训练、分布式执行、量化和注意力内核。

Transformers testing: 88,000+ CPU tests on main and 200,000+ GPU tests daily. Hardware includes CPUs, NVIDIA GPUs, AMD GPUs, Intel XPUs and Gaudi 3. PRs run selected tests.

集成测试会加载真实 checkpoint,并在真实硬件上调用模型的 API。在每个 PR 上都运行全部这些测试,意味着要让大量 GPU 整天忙碌,这并不现实。等到定时运行发现回归时,它可能已经进入 main 了。

我们的做法是缩小模型。我们构建一个 fixture:用真实 checkpoint 的配置生成一个小模型,层数更少、更窄,但包含它所拥有的每一种层,并且其 tokenizer 和 processor 文件保持不变。它只发布一次,然后重复使用。我们在廉价的 CPU runner 上对它运行“快速集成测试”。在我们的 CPU 研究中,实验性的 UMT5 fixture 使用了 66 MB 权重;源 checkpoint 为 1,227 MB。

这也覆盖了非常大的模型:有些模型无法装进我们标准的 GPU runner,但它们的 fixture 可以在 CPU 上运行。

模型集成者已经在构建小型测试模型(Transformers 的 tiny-random-* checkpoint)。我们想要一个通用的配方,以及超越加载模型和运行一次前向传播的检查。UMT5 的 bug 本来可以轻松通过一次前向传播。

Fixture 不会取代完整的集成测试,但在 PR 上它们给了我们在 bug 进入 main 之前抓住它的机会。

引入 Model Integration Contracts

transformers-mic 是一个用于打包这些测试的拟议标准。一个 Model Integration Contract(MIC)包含三个部分:

  • 一个 fixture:上面描述的缩小模型,发布一次并固定版本
  • 一个 usage path:一个可运行的 Markdown 示例,展示人们用该模型做的事情,例如生成文本或运行一个训练步骤
  • baselines:运行该示例时记录下来的输出,由人工审核

如果在 fixture 上运行 usage path 仍然产生 baselines,则 contract 成立。

一个 contract 覆盖一种架构和一条 usage path,而不是单个 checkpoint:兼容的 checkpoint 可以共享一个 fixture,同时各自保留自己的预期结果。我们将它们发布在 huggingface-mic 中。

例如,SmolLM contract 从“The capital of France is”这类 prompt 生成文本,并将其 token ID 与记录的答案进行比较。在加载模型和 tokenizer 之后,生成代码简化为单个 prompt 如下:

inputs = tokenizer("The capital of France is", return_tensors="pt")
outputs = model.generate(**inputs, max_new_tokens=4, do_sample=False)
new_tokens = outputs[:, inputs["input_ids"].shape[1]:]
print(tokenizer.batch_decode(new_tokens, skip_special_tokens=True))

它还会比较训练损失、检查梯度是否有限,并验证优化器步骤会改变权重。见完整的 usage.md。

Hub 上的一个 fleet dataset 将每个 MIC 固定到一个冻结的修订版本,以及用于记录其 baselines 的包版本。每次 CI 运行都会记录它使用的 fleet 修订版本,以便我们日后复现。

在 PR 上,我们根据补丁所修改的文件来挑选 MIC。

UMT5 回归

我们在 Transformers 五个月的提交记录上回放了冻结的 MIC。小型 UMT5 fixture 从 2026 年 7 月 30 日引入该回归起就将其标记出来,直到 9 月 28 日修复为止。标签对数概率与审核过的基线相差 1.22,训练损失相差约 0.20。

有一个细节很关键:该 contract 使用的是 SDPA,即默认的注意力路径。只运行 eager 注意力变体会漏掉这个 bug。只有当 fixture 与真实 checkpoint 走同一套代码时,它才有用。

随后我们在 Transformers 的 CI 中于一个 proof 分支上回退了该修复。检查以相同的差异失败,并且所选 T5 contract 的 runner 步骤在 CPU 上耗时 13.4 秒(不含环境准备)。

回放结果和 CI 演示中有测量数据和复现细节。

模型是如何被缩小的

最初的配方很简单:把维度调小、保留更少的层。这对混合架构来说还不够。如果两层都使用滑动注意力,而全注意力层消失了,那么保留两层也没有用。

现在的配方有四步:

  1. 缩小配置。从一个真实 checkpoint 出发,减小隐藏维度、注意力头数和深度。保留不同的层类型,包括滑动注意力与全注意力的混合,或稠密层与 MoE 层的混合。某些架构需要额外规则来保持其组件兼容。我们还会查看模型自带的 Transformers 测试:test_modeling_<model>.py 中的 ModelTester 已经手动挑选了一个小配置,因此对于通用规则无法按名称识别的任何宽度,我们采用它的尺寸。
  2. 保持预处理不变。保留词表、特殊 token 和输入维度,并逐字节复制 tokenizer 和 processor 文件。加载和预处理正是我们想要测试的内容之一。
  3. 构建并验证权重。尝试带种子的随机权重,并改变输入以检查模型是否有响应。如果没有响应,就尝试在 contract 的小批量样本上训练它。重新加载保存的模型,检查输出、损失和梯度是否有限。拒绝失败的构建。
  4. 冻结并记录。保存通过验证的权重,并在一个已知良好的 Transformers 构建上测量可重复的输出。审核每个受支持平台和精度的基线,然后发布一个固定快照。CI 复用它;它不会为每个 PR 重建 fixture。

我们还保留原始的权重文件格式。否则就会跳过用户加载真实 checkpoint 时运行的转换代码。完整配方有更多细节。

Llama 4 Scout 缩小报告展示了这在大型模型上的效果。1086 亿参数变成 4470 万。视觉栈从 34 层减到 2 层,语言栈从 48 层减到 4 层,同时保留所检查的层类型和 MoE 路由风格。

用 agent 构建 MIC

我们的 CLI tmic 会创建 contract 并构建 fixture。对于新模型,由 LLM 起草用法代码和清单,并通过 CPU 和 GPU 运行来测量候选基线。LLM 只负责起草;它不负责批准。更改预期值需要人工审核。

要在你的机器上试用,请用你的源 checkpoint 代替 org/model:

pip install "transformers-mic[runtime]" "hf-doc-builder @ git+https://github.com/huggingface/doc-builder.git"
hf auth login
tmic run org/model --local-only --fixtures-only --no-history --bill-to ''

它会起草合同、构建 fixture 并在你的机器上记录其基线,请你在每一步进行审查,并在上传任何内容之前停止。对于新模型,草稿通过 Inference Providers 由 LLM 生成,费用计入你自己的账户(--bill-to '')。CLI 文档涵盖了各个步骤。

我们使用 agent 跨模型系列构建 MIC。当它们遇到边缘情况时,我们修复配方本身而不是单个 fixture,并得到模型专家的帮助。已发布的模型和剩余空白见覆盖空间。

随机 vs 过拟合

使用随机权重时,我们的 Whisper fixture 对音频变化几乎无响应。它能运行,但这使它成为音频路径的糟糕测试。

因此我们在几个音调上训练它,并搭配诸如“red”和“green”之类的词。我们并不是在教它语音识别,只是让音频变化在其输出中可见。在我们的权重初始化研究中,随机 Whisper fixture 捕获了 24 个注入故障中的 18 个。过拟合 fixture 捕获了全部 24 个。

这给了我们两种构建权重的方式:

  • 从随机权重和固定种子开始。
  • 在几个例子上过拟合,例如“The capital of France is” => “Paris”。

Whisper 的结果并不能确定哪种更好。那些是合成故障,我们还没有发现仅由过拟合版本捕获的真实回归。两个版本都漏掉了真实 Whisper checkpoint 暴露的一个辅助解码 bug。

因此,当随机权重通过检查时我们保留它们,而当需要更好的灵敏度或可读答案时我们使用过拟合。训练只需几秒钟,但这是多一个可能无法收敛的步骤,也是多一件需要维护的事情。对比包含两者的结果。

开放问题

截至 2026 年 11 月 1 日,Transformers CI 中的 MIC 任务会报告失败,但不会阻塞 PR。它是否应该阻塞取决于我们尚未测量的因素:

  • 它在 PR 上,而不仅仅是在重放中,能捕获多少真实 bug?
  • 它多久会让我们去追查误报?
  • 基线审查需要多少工作量?

我们也不知道有多少 bug(比如 Whisper 辅助解码那个)只有在真实权重下才会出现,或者过拟合是否能捕获随机权重漏掉的真实 bug。

覆盖范围仍然不完整,无论是模型系列还是人们使用的 API。当回归漏过时,我们可以添加缺失的使用路径,并在损坏和修复版本上重放它。我们会保留现有测试,直到它们的覆盖范围被纳入考量。

最后,我们希望将原始 Hub 模型页面链接到它们的 MIC,这样使用 checkpoint 的开发者或 agent 可以从一个在该 checkpoint、其环境和预期输出上测试过的示例开始。

想帮忙?从覆盖空间中挑选一个未覆盖的模型,尝试tmic 工作流,并贡献一条使用路径或审查一份生成的合同。

来源:Hugging Face Blog · hf-mirror.com