← 返回文章选择

Attention 阅读便笺:为什么要挡住未来位置

半页手写便笺:RNN 为什么难并行,解码器为什么要挡住未来位置。

《Attention Is All You Need》在我的手写本上只占了半页,留下三个关键词:序列计算、FFN、masked self-attention。这篇就按便笺的篇幅留着,不扩成 Transformer 教程。

RNN 的等待沿着序列累加

RNN 的序列计算不容易并行:第 3 个位置要等第 2 个算完,第 2 个又得等第 1 个。等待沿着序列往后累加,序列越长,要等的步数越多。

论文提出以注意力为核心的 Transformer,训练时同一层里的位置可以并行处理,不用排这个队。但层与层之间仍要依次计算,不是整段一次算完;生成时也还是一个位置一个位置往后走。

论文 §4 用一张表(Table 1)把“谁要排队”写成了数,n 是序列长度,d 是表示维度:

层类型 每层计算量 顺序操作 任意两位置间最长路径
自注意力 O(n²·d) O(1) O(1)
循环 O(n·d²) O(n) O(n)

这张表也写出了代价:不用排队,换来的是随 n 平方增长的计算量。论文自己也说,自注意力只在 n 小于 d 时才比循环层快。并行换来的东西也有数字:base 模型在一台 8 张 P100 的机器上训练 10 万步,约 12 小时;big 模型 30 万步,3.5 天(§5.2)。

FFN 是前馈网络。这次只记下了名字,具体维度和计算细节留到以后读。先留一个值得读的理由:2021 年的 Attention is Not All You Need 证明,去掉跳跃连接和 MLP,纯注意力堆叠的输出会随深度双指数地退化成秩 1 矩阵。FFN 不是装饰。

生成时不能提前看到答案

我对 masked self-attention 的简记是:当前位置可以看自己和左边,不能看未来位置。这里说的是论文解码器里用来避免访问后续位置的因果遮罩,不能推广到所有 self-attention。论文 §3.2.3 列了注意力的三种用法:编码器自注意力、解码器自注意力、编码器到解码器的注意力,遮罩只在第二种里。做法也简单,softmax 之前把不该连的位置设成 −∞。

下面只画遮罩的可见范围:实线是位置 3 可以访问的位置,虚线是被遮罩的未来位置。

位置3可访问位置1、2、3,位置4被因果遮罩阻止访问

查看 Mermaid 源码
flowchart TD
query["位置 3 的查询"] -->|可访问| p1["位置 1"]
query -->|可访问| p2["位置 2"]
query -->|可访问| p3["位置 3"]
query -.->|遮罩阻止访问| p4["位置 4"]

“能看到当前位置”不等于提前读到了待预测的答案,因为完整训练过程还有目标序列移位:把目标序列整体右移一位,再送进解码器。配合因果遮罩,每个输出位置只看得到它前面已经给出的目标 token,自己该预测的那个还没送进来。论文 §3.1 把这两件事写在同一句里:

This masking, combined with fact that the output embeddings are offset by one position, ensures that the predictions for position i can depend only on the known outputs at positions less than i.

遮罩管“看不见”,移位管“答案还没送进来”。想补一组输入和目标位置的对应例子,可以对照原论文 PDF 的模型结构与解码器说明。

今天这件事在代码里是一个参数。PyTorch 的 scaled_dot_product_attention 设 is_causal=True,用的就是下三角遮罩。自己传布尔遮罩时要小心方向:这个函数里 True 表示参与注意力,而 MultiheadAttention 里 True 表示不允许关注,两边差一个取反。

留下几句话 读者来信

读到这里,有什么想法?欢迎留下你的看法。