第 14 章 Attention:当前输出应该看输入哪里
14.1 当前输出需要的,也许是较早的状态
Section titled “14.1 当前输出需要的,也许是较早的状态”第 13 章 的编码器曾经产生有用的颜色状态,却在得到最终摘要之前丢掉了它。解码器只拿到 [0,0],无法区分红色和蓝色输入。交给解码器的唯一源表示里已经没有这种区别,把解码器做得更复杂,也不能凭空恢复它。
假设我们保存 全部三个编码器状态。写颜色之前,解码器可以按一种方式组合它们;写物体之前,再换一种组合。源序列仍由递归编码器读取,目标序列仍由递归解码器写出。我们改变的是每个输出步能访问哪些源信息。
这正是递归翻译模型引入注意力的动机:从保存的源表示中,为当前输出计算一份上下文,不再只依靠一个固定摘要。Bahdanau 等人用可学习的对齐函数实现了这样的模型。本章则先明确一种更简单的打分规则,再亲手算出整个机制。Bahdanau 等,2015,第 3 节
14.2 同一个解码位置,面对三个可读取的位置
Section titled “14.2 同一个解码位置,面对三个可读取的位置”例 14.1 — 从算出的源状态中读取颜色信息。 沿用第 13 章的源序列 [红色, 钥匙, 。]、嵌入和 ReLU 编码器。令 ,按源位置保存的状态为:
这些状态由权重计算得来,不是原始输入嵌入。例如,钥匙的嵌入是 [-a,a],对应状态却是 [0,a]。一个已保存状态概括了涉及此前输入的计算,不一定是某个孤立词的向量。
现在给定一个解码状态,用来探查读取结果:。字母 表示 查询向量(query),当前输出位置用它与可用的源状态比较。在本章的递归结构中,查询取自上一步解码状态。本章使用的两个查询值都是特意给定的探查值,不是学出的解码轨迹,也不是第 13 章概率表产生的状态。这样可以单独看清读取操作,而不虚构尚未给出的解码器权重。
查询和源状态都只有两个坐标。规定分数为 未缩放的点积:对应坐标相乘,再把乘积相加。它不是余弦相似度,不除以向量长度;也不是下一章将引入的缩放点积。这是 Luong 等人讨论的一种打分方式,与 Bahdanau 原论文中可学习的加性打分不同。本章正文和代码始终使用同一条未缩放规则。Luong 等,2015,第 3.1 节,式(8)
表 14.1 — 从比较分数算出源位置权重。
| 源位置 | 状态 | 与 [1,0] 的点积 | 指数值 | 归一化权重 |
|---|---|---|---|---|
| 1:颜色 | [a, 0] | 1 × a + 0 × 0 = a | 2 | 2/4 = 1/2 |
| 2:钥匙 | [0, a] | 1 × 0 + 0 × a = 0 | 1 | 1/4 |
| 3:句号 | [0, 0] | 0 | 1 | 1/4 |
分数行是 [a,0,0]。按第 6 章的 Softmax,把它变成非负且总和为一的权重。由于 、,分母是 。若除以三,算的是指数值的平均,而不是把它们归一化成概率分布。分数 为零,指数值仍是一,因此权重并不为零。
这些概率把一次读取分配给 三个源位置,不是分配给候选输出词。颜色状态的权重为 1/2,不等于输出 red 的概率为 50%。要得到上下文,需要把每个完整源向量乘上它的权重,再逐坐标相加:
第一个坐标从颜色状态得到 ,其余两项贡献为零;第二个坐标从钥匙状态得到 。句号状态贡献的是零向量,但它的正权重仍占据归一化总量的一部分,会影响其他状态分到多少权重。
这个加权和就是当前的 上下文向量(context vector)。它和每个被加权的状态一样,只有两个坐标,不会因为有三个位置就变成三维。在这个小计算里,结果的每个坐标都必须处于源状态对应坐标的最小值与最大值之间:非负且和为一的权重,形成的是加权平均,不能任意创造超出对应坐标范围的新值。
14.3 输出位置变了,读回的上下文也变了
Section titled “14.3 输出位置变了,读回的上下文也变了”例 14.2 — 从同一组状态中读取物体信息。 所有源状态保持不变,改为给定第二个查询 。分数变成 [0,a,0],因为点积现在取第二个坐标,而不是第一个。指数值为 [1,2,1],仍除以总和 4,得到权重 [1/4,1/2,1/4]。因此:
第一次读取给颜色状态最多权重,第二次给钥匙状态最多权重。保存的源状态没有改变,改变的是当前查询,因此返回的组合不同。这就是这里所说的 动态访问(dynamic access),并不暗示模型在有意识地搜索,或能可靠判断什么最重要。
- 一组数字颜色 [a,0] × 1/2
- 一组数字钥匙 [0,a] × 1/4
- 一组数字句号 [0,0] × 1/4
- 一组数字求和 → [a/2, a/4]
分数 [a,0,0] → 指数值 [2,1,1] → 除以 4。
- 一组数字颜色 [a,0] × 1/4
- 一组数字钥匙 [0,a] × 1/2
- 一组数字句号 [0,0] × 1/4
- 一组数字求和 → [a/4, a/2]
分数 [0,a,0] → 指数值 [1,2,1] → 除以 4。
图 14.1 保持源位置顺序,只交换前两个位置的权重。目标位置与源位置之间这种不必一一对应的软关联,常被称为 对齐(alignment)。它不必只选一个位置,不必沿对角线,也不必符合人类逐词标注的翻译对应。这里的权重来自已经声明的分数;这种读取能否帮助学出的解码器,还需要另外检验。
完整算过之后,再写一般形式。设源状态有 个,每个宽度为 ,查询宽度也为 。目标位置记作 ,源位置记作 :
标量 是比较分数,这里的字母本身不是指数函数的底数。下标 遍历分母中包含的所有源位置。标量 是该位置的权重,把缩放后的各行相加,就得到宽度为 的行向量 。例 14.1 中,、:有三个分数、三个权重,却只有两个上下文坐标。代入后会重新得到表 14.1 和 [a/2,a/4]。
这种操作叫作 交叉注意力(cross-attention),因为查询来自目标侧解码器,被读取的状态来自源侧编码器。本例直接拿源状态做比较,也直接把它们当作加权求和的值。独立的可学习投影,以及完整的 Q/K/V 记号,留到第 15 章再建立。
14.4 把上下文交给解码器,它还不是答案
Section titled “14.4 把上下文交给解码器,它还不是答案”上下文为预测提供信息,但自身不是预测结果。沿用第 13 章的递归顺序,令 ,先读出注意力上下文,再更新解码器:
旧解码状态是 ,前一个目标嵌入是 ,新增的是随读取而变的上下文 。、、 的职责与第 13 章相同。例如,这三个输入行向量若各有两个坐标,拼接后就给递归更新提供六个坐标,不是六个输出词概率。最后的 Softmax 仍在输出词表上归一化,与源位置上的注意力 Softmax 分开。
训练时仍由教师强制提供前一个正确目标,生成时仍使用前一个选中目标。能访问源状态,并没有消除两种前缀的差异。本章给定的两个查询,也不能证明训练好的解码器实际会在那些位置说什么。
不过,新增路径确实让小编码器较早保留的区别重新可用。蓝色源序列的状态是 [0,0]、[0,a]、[0,0]。用第一个查询打分,三个分数都是零,权重都是 1/3,上下文为 [0,a/3]。它不同于红色源序列的 [a/2,a/4],虽然两者最终编码状态相同。解码器如今拿到了一项随源输入变化的区别,可以利用它;是否能学会合适的输出规则,单凭这个计算还不能确定。
14.5 删去一个位置之前,先说明固定什么
Section titled “14.5 删去一个位置之前,先说明固定什么”例 14.1 中,颜色状态的权重最大。能否因此认定,它就是后续任何输出的主要原因?作出这个判断,需要先确定观察哪个输出量、做什么干预,不能只看排名。
例 14.3 — 读取之前,删去一个已保存状态。 固定第一个查询,并固定其他源状态的数值。把指定位置从分数列表和加权和中完全移除,再对剩余位置重新归一化。这不是把被删位置的分数改成零,因为零分数仍会贡献指数值 1。
为了衡量影响,先规定一个简单的下游读数:上下文第一个坐标,加上第二个坐标的两倍。把这两个坐标记作 、,定义 。这个读数也是作者设定的诊断量,不是学出的翻译分数。删除前,它等于 。
表 14.2 — 固定其余状态,用第一个查询重算删除结果。 权重仍按原源位置排列,零表示该位置被排除。
| 删去的位置 | 权重:颜色、钥匙、句号 | 新上下文 | 读数 r |
|---|---|---|---|
| 不删除 | [1/2, 1/4, 1/4] | [a/2, a/4] | a |
| 1:颜色 | [0, 1/2, 1/2] | [0, a/2] | a |
| 2:钥匙 | [2/3, 0, 1/3] | [2a/3, 0] | 2a/3 |
| 3:句号 | [2/3, 1/3, 0] | [2a/3, a/3] | 4a/3 |
删去颜色后,剩下两个零分数,所以权重各为 1/2。上下文变了,但选定的读数 没有变:第二个坐标增大后再乘二,恰好补回第一个坐标丢掉的贡献。删去钥匙后,剩下的指数值是 2 和 1,分母变成三,读数降到 。删去值为零的句号状态,也会改变归一化,使读数升到 。
因此,权重最大的状态,不一定就是删去后最影响这个输出量的状态。如果改用另一种读数,例如只看上下文的第一个坐标,结果又会不同。注意力权重说明这次读取怎样组合状态;要说明某个输出的因果关系,还需追踪下游计算,并明确测试中的改动。这是一个有限结论,不是说注意力永远不能提供有用证据。Jain 和 Wallace,2019,第 4 节;Wiegreffe 和 Pinter,2019,第 2 节
删除 原始输入 token 又是另一种干预。它可能改变后面各位置的状态,因此必须重新运行编码器。例如,删去源 token 钥匙,留下 [红色, 。]。第一个状态为 [a,0];读句号时加上 [0,-a],得到 [a,-a],经过 ReLU 变成 [a,0]。两个剩余状态如今都是 [a,0]。仍固定第一个查询,两个分数都为 a,权重各为 1/2,上下文为 [a,0],读数为 a。这与只删去已保存的钥匙状态时得到的 不同。
表 14.3 — 删去原输入后,重新编码再读取。 查询和读数规则保持固定,因此这仍不是完整的解码生成过程。
| 删去的源 token | 重算的状态 | 上下文 | 读数 r |
|---|---|---|---|
| 颜色 | [0,a];[0,0] | [0,a/2] | a |
| 钥匙 | [a,0];[a,0] | [a,0] | a |
| 句号 | [a,0];[0,a] | [2a/3,a/3] | 4a/3 |
有些结果与表 14.2 恰好一致,钥匙那一行却不同。只说“删掉一个词,答案变了”,证据仍不完整:后面的状态重算了吗?查询和之后生成的 token 呢?高权重与表面相关性的对应关系值得调查,但它本身不是干预结果。即使干预记录完整,也只建立了特定条件下的影响,不会自动成为对模型的普遍解释。
至少还得有一个可读取状态。如果把全部位置都排除,就没有正的归一化总量,这条规则下也就无法定义加权读取。配套代码会报告错误,不会编造一组全零“概率”。
14.6 访问范围更大,存储与顺序代价仍在
Section titled “14.6 访问范围更大,存储与顺序代价仍在”保存全部源状态,让每个已保存表示都多了一条直接通向当前目标读取的路径。旧结构中,较早源信息必须经过剩余的每次编码更新,才能到达最终上下文。新读取可以直接使用较早保存的状态,绕过这一段路;训练梯度也可以沿读取路径到达那个状态。这不意味着所有长路径都消失了,更不保证网络一定学会有用的表示与对齐。
即使在这个小例子里,也能数清存储代价。固定上下文保存两个数,三个源状态保存六个数。若明确假设每个数占八字节,就是 逻辑激活存储量 16 字节与 48 字节的区别,不含参数、容器、解码状态或训练中间量。源位置有 个、状态宽度为 时,需要保存 个数。这是按假设计数,不是进程内存实测。
四个输出步若每步都为三个源位置打分,就要算十二次点积。每次二维点积含两次标量乘法和一次加法;整个操作还要取指数、归一化、计算加权和。源序列越长,每次读取的工作也越多。
- 一组数字h₀ → h₁ → h₂ → h₃
- 处理步骤保存三个状态
较早保存的状态可以直接进入读取,不必再经过后面的编码更新。
- 一组数字qₜ = sₜ₋₁
- 处理步骤qₜ·h₁、qₜ·h₂、qₜ·h₃
- 一组数字归一化并求和 → cₜ
三次比较可以并行,但都要先拿到当前查询。
- 处理步骤旧状态 + 前一个 token + cₜ → sₜ
- 一组数字下一个查询使用 sₜ
教师强制提供 token,不提供未来隐藏状态;生成时还要先选出下一步输入 token。
图 14.2 标出了哪些计算仍然要等。查询已知后,它与各源状态的比较,以及加权求和的部分计算可以并行。但本例的递归编码器仍需依次更新三次,解码器仍需依次更新四次。即使用教师强制,下一个解码状态也依赖前一个;生成时还多了对前一个选中 token 的依赖。注意力没有把整条递归序列变成相互独立的并行更新。
访问也仍受应用提供的信息限制。如果更新后的 750 元差旅政策从未进入编码器,注意力不能从系统外把它检索回来。某个源状态的相关分数,既不能核实政策版本,也不能授予订单操作权限。
14.7 第三部分检查点:先认清路径,再提出修复
Section titled “14.7 第三部分检查点:先认清路径,再提出修复”表 14.4 — 四种诊断,不要都归为“记性差”。
| 观察到的证据 | 已建立的问题 | 怎样改变相关路径? | 什么仍未得到证明? |
|---|---|---|---|
| 末尾两个输入 token 相同,要求的颜色不同 | 固定窗口的输入碰撞 | 提供更多上下文,或传递计算状态 | 模型能正确使用新增线索 |
| 局部损失信号跨越许多依赖边后衰减 | 反向敏感程度过弱 | 改善状态路径和训练设置,门控可能有帮助 | 任意距离上的可靠保留 |
| 训练第二步收到 red,生成第二步收到 blue | 正确前缀与生成前缀不一致 | 用生成前缀评价后续行为 | 每个早期错误都会继续扩散 |
| 不同源序列得到同一个最终向量 | 这个编码器的固定摘要发生碰撞 | 让目标步访问较早的状态 | 正确的对齐与输出选择能被学会 |
检查点要落到具体操作上:在图 11.1 中指出输入、状态和共享参数;从表 12.1 算回总梯度 −0.75;在表 13.5 定位第一次输入变化;算出表 14.1 的权重和上下文。然后用表 14.2—14.3 解释,为什么一张注意力图不自动构成因果解释。四章静态可见的参考解答给出了各步理由。
共享实践程序为 code/part-iii/run.py,固定数值和预期结果位于 data/part-iii/。notebooks/part-iii/02-seq2seq-attention.ipynb 会执行轨迹、比较两种删除操作,并提供可选的 NumPy 复算。标准库路径只要求完成 Python 入门。核心结果都已列在正文中,理解机制不需要账号、GPU、付费 API 或运行 Notebook。
14.8 本章小结
Section titled “14.8 本章小结”交叉注意力从当前解码查询出发,算出源位置分布,再返回加权后的源状态向量。我们的两个查询从不变的状态集合中读回不同上下文,使最终摘要丢掉的区别重新可用。上下文之后仍需要解码器的输出规则,而单个注意力权重不能独自解释那条规则的结果。
源序列和目标序列的递归依赖仍然存在。第 15 章将追问:同一序列内部 的位置能否直接交互,不再只能通过相邻递归状态传递信息?这会改变另一条信息路径,形成自注意力。可学习投影、缩放以及后面的 Transformer 结构,都属于接下来的论证。
14.9 习题
Section titled “14.9 习题”- 换一个查询。 对原来的三个源状态使用查询
[1,1],计算全部分数、权重、上下文坐标及诊断读数。尽量把答案写成 a 的分数倍。 - 诊断分母。 一位读者因为例 14.1 有三个源位置,算出权重
[2/3,1/3,1/3]。错在哪里?若改用查询[0,0],权重又是多少? - 比较两种删除。 对“删去钥匙”分别求上下文:先只删已保存状态,再删输入 token 并重新编码。指出哪个剩余状态改变了。
- 检验解释。 颜色状态的权重为 1/2,报告因此认定它对最终回复的因果影响必然最大。从本章给出数值反例,并说明这个反例实际检验的输出量与干预是什么。
- 数代价与依赖。 假设有 100 个二维源状态和四个目标步,每个数占八字节,需要多少逻辑源状态存储量?要做多少次点积比较?训练目标已知时,哪些递归依赖仍在?
14.10 参考解答
Section titled “14.10 参考解答”- 分数为
[a,a,0],指数值为[2,2,1],总和为五。权重是[2/5,2/5,1/5],上下文为[2a/5,2a/5],读数为 。三个权重之和为一,两个上下文坐标之和则不必为一。 - 分母必须是指数值之和,所以应为四,不能直接数位置。错误权重之和为 4/3,不是概率分布。零查询则得到三个零分数、三个指数值 1;在这个不同条件下,分母确实为三,权重均为 1/3。
- 只删已保存的钥匙状态,留下
[a,0]和[0,0],权重为 2/3、1/3,上下文为[2a/3,0]。删原输入中的钥匙,则没有钥匙嵌入来抵消颜色坐标,后面的句号状态也变成[a,0]。两个剩余状态都是[a,0],等权平均后仍为[a,0]。 - 固定其余状态,按声明的标量规则读出时,删去权重最高的颜色状态,读数仍为 a;删去钥匙却降到 2a/3。这足以反驳针对该输出量、该干预的自动排序说法,但并没有测量译词准确率或整条生成回复。
- 状态中有 个数,按假设占 字节。四次读取需要 次点积。编码器仍按顺序更新源状态,解码器仍按顺序更新目标状态;已知目标 token,不会让递归隐藏状态相互独立。这些计数不含其他张量和实现开销。
14.11 参考文献
Section titled “14.11 参考文献”- Bahdanau、Cho 和 Bengio,Neural Machine Translation by Jointly Learning to Align and Translate(2015)。第 3 节说明不同目标位置的上下文,以及读取源表示的递归解码器。论文的编码器与加性打分不同于本章小例子。
- Luong、Pham 和 Manning,Effective Approaches to Attention-based Neural Machine Translation(2015)。第 3.1 节给出未缩放点积这一打分选项。本章采用该分数,不声称复刻论文的完整解码顺序。
- Jain 和 Wallace,Attention Is Not Explanation(2019)。第 4 节在特定任务上比较注意力与其他重要性度量,并测试改变注意力分布的结果。
- Wiegreffe 和 Pinter,Attention Is Not Not Explanation(2019)。第 2 节检查这类解释性主张和干预依赖的假设,适合与前一篇论文及本章明确定义的删除操作对照阅读。