Loading
排序函数错了,你能看出来:列表没有排好序。
注意力矩阵错了,它看起来还是一个注意力矩阵。每个值都在 0 到 1 之间,每一行加起来等于 1,数字在词之间的变化也很合理。一个被微妙地弄坏的 Transformer,其输出在人眼看来与正确的毫无差别。
于是常见的写测试的本能——跑一下、看输出、断言你看到的——在这里不可用。更糟的是,它是有害的,因为输出看起来没问题。
关键结论: 你不能拿直觉去测试一个数值系统。你要拿一个与第一个毫无共享的第二实现去测试它——包括不共享写它的人的假设。
我写了 WordPiece 分词器,然后为它写了测试。两者都出自我对"分词是怎么回事"的同一套心智模型。
那套模型在某个具体的地方是错的,于是我产出了一个错误的实现,和一个与之完美吻合的错误测试。套件全绿、自信部署,而 £5 在数周里悄悄以 £ + 5 的形式抵达模型。
那一天,我再多写多少测试都抓不住它。它们全都会从同一个误解出发。
打破僵局的,是构建了第二个与第一个毫无共享代码的 bert-mini:用 Python 而非 TypeScript 写成,依据公开描述而非既有实现,在原始 fp32 HuggingFace 权重上以 fp64 NumPy 运行。不同的语言、不同的算术、不同的日子、不同的假设。
当两个独立实现出现分歧时,两种令人安心的信念里只有一种能活下来,而你必须去弄清是哪一种。
它们在 £5 上分歧了。神谕是对的。
而且有一次,神谕错了。 它的第一版缺少了 HuggingFace 给每个 CJK 字符两侧加空格、使其各自成词的那一步。它在 中文 上与运行时分歧——那一次,对的是运行时。一份提交进仓库的固定样本终结了争论。
这件事改变了神谕的使用方式。未经验证的神谕只是第二种意见,而一个自信却错误的神谕比没有更糟:它会把你派去修改本来就正确的代码。所以现在,神谕必须先以 1.324e-6 的精度复现一份提交在仓库中的 PyTorch 参考,才被允许去裁判任何东西——而且这项校验在每次比较开始时运行,而不是存在于某个人的记忆里。
有了可信的神谕,问题就变成了:拿什么来比较?第一次审计用的是九个精挑细选的句子,而这不够——手工挑选的句子,共享着那只手的偏见。
如今语料库有 64 个句子,都是为了"弄坏东西"而选的:符号、缩写、所有格、CJK、emoji、带重音的拉丁字母、德语和法语、文件路径、电子邮件地址、单个词、重复词、制表符、一个会碎成十三片的词、处在词元上限的句子,以及那对录制参考句。管线的每一个环节都会被比较,而不只是最终数字:
oracle vs PyTorch reference: 1.324e-06
sentences 64 compared 64 over token limit 0
worst abs error attention 2.251e-04 (bar 1e-03)
worst abs error rollout word mx 5.394e-06 (bar 5e-05)
worst abs error rollout sink 1.442e-05 (bar 1e-04)
worst abs error per-head word mx 1.700e-04 (bar 6e-04)
measured pairs quoted in prompts.ts: 29 hold
PASS最有力的单个测试,也是最不聪明的那个:取出全部 30,522 条词表条目,逐一单独送回分词器,要求它原样返回。这抓住的是 bug 的类别而非某个个例——也正因如此,我才敢说那九条之后没有藏着第十条。
那份比较脚本的第一版,会打印 rollout 与按头矩阵的误差,却对它们不做任何断言。只有注意力有阈值。于是我把转储扰动了 1e-4,看看会发生什么。
它打印了 PASS。
整整两个环节被报告出来,却从未被检查。那些数字每次运行都滚过屏幕,看起来令人安心,而且在任何不触及注意力的回归中都会继续令人安心。
现在每个环节都有门限,而每个门限都通过故意弄坏某样东西来验证过:
每个按钮都会破坏校验器读取的转储中的一个数值。无法失败的检查不算检查,所以在信任这些数字之前,每一个环节都用这种方式验证过。
六次变异,六次失败。还有第七种:一个已经偏离 PyTorch 参考的神谕,会在任何比较发生之前中止整个运行。
两套实现意味着双倍的工作量,只有当你的直觉确实无法裁判输出时才值得。对排序函数来说这很荒谬;对量化 Transformer 来说,这是"相信你的数字"与"知道你的数字"之间的差别。
在这里真正抓到 bug 的测试,并不是那些断言预期值的测试。此前有一次,有人凭记忆敲下了三个"预期"的词元 id,其中两个是错的——如果有人信任它,这个测试反而会把错误固化下来。由此得出的规则很直白:永远不要手写预期值。 要么从神谕推导,要么从提交在仓库中的参考推导,要么就别断言。
你的直觉是假设生成器。它不是测量仪器;在任何与数值有关的事情上,它都不该是 bug 与生产环境之间最后的那道防线。
下一篇:针对"汇聚"问题的一个已发表修复方案——测量之后,被放弃。
更多阅读