Loading
BERT 会给你给它的每个句子添加两个词元:开头的 [CLS] 和结尾的 [SEP]。它们是记账用的——一个标记开始,一个标记结束——都不是你写下的词。
在许多注意力头里,它们也正是大部分注意力的去处。
这给任何想画出这个句子的界面出了难题。你没法把弧线画到 [SEP] 上:访客没有输入过它,也不知道它是什么。于是你删掉那些列,再把剩下的重新归一化,让这一行仍然加起来等于 1。
这个项目当初就是这么做的。算术是对的。呈现是个谎言。
关键结论: 如果你移除分布的一部分再重新归一化,你显示的每个数字都变成了一个更小整体的份额——而它看上去与原整体的份额一模一样。
the → scissors 0.431 看起来是一段很强的关系。而在它所来自的那个头里,真实权重是 0.021。显示出的数值,是那一行在删除后幸存的 4.8% 当中的份额。
词行会在可见词上重新归一化,所以显示出的权重是移除 [CLS] 与 [SEP] 之后剩余部分的份额。切换开关,看看移除之前的同一行。
在两个视图之间切换。柱子的形状是一样的——当然一样,重新归一化只是一次均匀缩放——但一行描述的是模型做了什么,另一行描述的是剪辑之后剩下什么。
在九个句子、61 行聚焦行上测得:
| 一行被移除份额的中位数 | 24% |
| 损失超过一半的行 | 61 行中的 19 行 |
| 最坏情况 | 95% |
一行中约四分之一,在显示前就消失了。而屏幕上没有任何地方说明这一点。
聚合代码从写下的那天起就一直在计算这个被移除的量。有一个叫 removedMass 的变量,还有一个断言它正确的单元测试。
然后 worker 把它丢掉了,从未发给界面。
诚实的机制被建好、被测试,然后被断开。这比根本没有它更糟——因为包括我在内的每个相关者,都能指着一个测试证明"这个数字我们是知道的"。
知道一个数字与展示一个数字,是两件不同的成就,而只有第二件能抵达读者。
不是重写。而是提升协议版本,让被移除的份额随每个矩阵一起传输,然后要求界面必须把它说出来。
三处改动,按难受程度排列:
把数字说出来。 每一行聚焦行现在都会报告有多大份额去了 [CLS]/[SEP],超过 50% 时用文字说明,而不是留下一个容易被略过的数值。
汇聚行被标记并调暗。 在大多数头里,标点会吸走巨量权重——某个头里 heads → . 测得 0.983——因此标点被排除在"决定这一行叫什么"的峰值选择之外。它依然被显示、被排序、数值依然精确。它只是不再有权定义这一行讲的是什么。
面板解释它自己的标题。 对 Water boils at 100°c.,标题写着 100°c 0.163,而下面列表的第一项是 . 0.438。两者都为真。此前没有任何东西解释这种不一致;现在面板会说:这一片以最强的内容词命名,而这里是超过它的那个标点权重。
手机端也全部补齐。此前每一条限定说明都是 hidden md:block 或藏在默认收起的面板里——这完全是反的,因为手机访客恰恰是那个无法打开矩阵自行核对的人。
我试过。更糟。
画向 [SEP] 的弧线,指向的是读者从未输入、也无法解释的词元。它主宰整幅图,对读者的句子毫无启发,还让工具看起来是坏的。重新归一化的视图确实更有用:它回答的是"在我写下的词当中,注意力去了哪里"——这正是访客在问的问题。
缺陷从来不是重新归一化,而是悄悄地重新归一化。展示一个子集的份额完全没问题,前提是告诉读者那是哪个子集,以及原始整体缺失了多少。
这个区别——错误的数字与会误导人的正确数字之间的区别——正是整个项目所围绕的东西。一天之内修复的五个缺陷,全都是这个形状:算术正确,呈现误导。
有一项已发表的技术声称能彻底消解这个问题:测量真正流动的东西,而不是被"看向"的东西。第 9 篇会去测试它。
下一篇:把十六个头合起来的那个数学上正确的方法,以及它为什么几乎什么都没说。