上一篇《从 RNN / LSTM / GRU 到早期 Attention》结尾我留了一句话:早期的 attention 并不是为了取代 RNN 而出现的,它只是夹在 encoder–decoder 中间解决 fixed-context bottleneck;但“根据当前需求去读取一组 representation”这种机制,并不一定要依附在 RNN 上。这篇接着讲那个“另一个故事”。
真正的引子,是我复习时反复绕回来的一个问题:
在 RNN 里,位置 A 的信息想影响很远的位置 B,为什么一定要沿着 recurrent state 一步一步传过去?
这个问题乍一听像抬杠——recurrence 不就是沿时间传播的结构吗,不然叫什么 RNN。但我带着它把后面的演进重新走了一遍,发现从 attention 到 self-attention,再到《Attention Is All You Need》那一步跳跃,几乎就是对这个问题的一连串回答。
information moves through state#
先把 RNN 的核心假设摆出来(展开版在rnn-state-transition 那篇):
$$h_t = f(x_t, h_{t-1})$$一个 fixed-dimensional 的 state 承载历史,每一步把当前输入和上一步的 state 揉在一起,得到新的 state。信息不会跳跃,只能沿着这条链往前走:
1x_i
2 ↓
3h_i → h_{i+1} → h_{i+2} → ... → h_j如果 \(x_i\) 的信息想影响很远的 \(x_j\),它必须途经 \(i\) 和 \(j\) 之间的每一个 timestep,一个都不能少。我当时把这个性质总结成一句:RNN: information moves through state——信息是沿着 state“搬”过去的。
起初我没觉得这有什么问题。序列有时间顺序,信息沿时间传播,顺理成章。“为什么两个位置之间的信息交互,一定要经过它们中间的所有 timestep”——这个疑问我记下了,但没有立刻追。
LSTM / GRU 修的是路,不是“要不要这条路”#
第一站是 LSTM / GRU。上一篇理过 gate 怎么把 state 更新变成可学习的动态控制。放到这条主线上看,定位就很清楚了:它们改善的是信息沿 recurrent path 传播的方式——memory path 提供一条更耐久的路线,gate 决定什么写入、什么保留、什么遗忘。
但它们没有动 recurrent topology 本身:
1A → state → state → state → ... → B这条 information path 原样还在,还是得一步一步走。哪怕记忆能力再强,A 到 B 之间隔着的 state transition 一次都不会少。
所以我后来是这么区分这两代工作的:LSTM / GRU 在解决“怎样让信息更安全地走完这条路”,而后来的 attention 这条线,起于另一个问题——“为什么一定要走这条路”。一个在优化路的质量,一个在质疑路的必要性,不在同一层。
从提前总结,到按需读取#
fixed-vector Seq2Seq 到早期 attention 的过程,上一篇已经详细写过,这里只沿“信息路径”的视角快进一遍,因为它是后面一切的前提。
fixed-vector Seq2Seq 让 encoder 读完整个 source,只交出一个 \(c = h_T\)。RNN 本来就在每一步把历史压进 fixed-dimensional state;Seq2Seq 把这个压缩推到极限——整句话最终也要塞进一个 vector。原本隐含的 compression limitation 被放大成明晃晃的 bottleneck。
Bahdanau attention 的解法是不再提前总结:保留 \(h_1, \dots, h_T\) 作为 variable-size memory,让 decoder 在每一步按当前需要动态读出 fixed-size 的 \(c_t\):
$$\text{score} \rightarrow \text{softmax} \rightarrow \text{weighted sum} \rightarrow c_t$$fixed-size constraint 从“整个 memory”挪到了“每次 read 的输出”。用上一篇憋出来的那句话总结:从提前总结,变成按需读取。
顺着这个视角,attention 可以先理解成一种 content-dependent dynamic memory access:读什么由内容决定,而不是由位置或时间决定。
“人眼看图”的那个故事#
这里岔出去澄清一件事,因为我曾被它带偏过。
很多教程介绍 attention 时喜欢用这个类比:人看一张图,不会平均地看所有区域,而是把视线集中在重要的部分。这个类比很直观,我自己讲给别人听时也常用。但把它当成 attention 的起源,就不准确了。
回去翻 Bahdanau et al. 2014 的上下文,他们面对的问题非常具体:神经机器翻译里 fixed-vector Seq2Seq 有 bottleneck,句子一长翻译质量明显下降,所以让 decoder 动态读取 encoder states,顺带学出 soft alignment。整个动机都在信息通路和 memory access 上,和“模拟人眼”没有关系。
视觉 attention 作为一个研究方向确实出现得更早,也确实受人类视觉系统启发;后来 attention 被大量用于 CV 之后,“人眼看图”的类比变得特别自然,当教学工具完全合格。但理解 architecture 演进时,我现在用的 mental model 是 dynamic read / content-dependent information routing,不是“模拟眼睛”。
为什么只有 decoder 能读 encoder#
回到主线。早期 attention 的结构是:
1decoder representation(读取需求)
2 ↓
3 attention
4 ↓
5encoder representations(memory)读取的一方是 decoder,被读的一方是 encoder。看习惯这个图之后,我冒出一个问题:如果“按需读取其他 representation”是个好 idea,为什么它只能发生在 decoder 和 encoder 之间?sequence 内部的位置,就不能互相读吗?
看一个句子:
1The animal didn't cross the street because it was tired.构造 it 的 representation 时,单独的 it 几乎没有语义——它指向什么,完全由上下文决定。理想的 representation 应该是“这里的 it 指向 animal,并且和 tired 有强关系”这样的东西,也就是说,不该是 it,而该是 it-in-this-context。
再看一对:
1The bank approved my loan.
2We sat on the bank of the river.同一个 bank,在两句里的 representation 应该完全不同,而这个不同只能来自句内其他位置。
RNN encoder 里,it 的 representation 当然也能吸收到前面词的信息——但那些信息是沿着 state 链一路压缩、改写过来的,代价是什么,下一节再算。单说结构:既然 decoder 可以按内容动态读取 encoder 的 representations,那让 sequence 内部的某个位置按需读取同一个 sequence 里其他位置的 representations,几乎是顺势的一步。
这就是 self-attention。它并不是一套突然冒出来的新机制,真正变化的是谁在查询谁:
1early attention: A reads B (decoder 读 encoder,两方来自不同 sequence)
2self-attention: A reads A (读取者与 memory 来自同一个 sequence)顺带补一句历史:让 sequence 读自己的想法,也不是《Attention Is All You Need》才第一次出现——Cheng et al. 2016 的 LSTMN、Parikh et al. 2016 的 decomposable attention 都在不同任务里用过 intra-sequence attention。AIAYN 做的是另一件事,后面讲。
RNN 也能把信息传到后面,self-attention 到底多做了什么#
这是整次复习里卡我最久的问题。
RNN 明明也能让后面的 token 拿到前面的信息——\(h_{100}\) 一路经过 \(h_1\) 到 \(h_{99}\),凭什么说它看不到前文?那 self-attention 难道只是换一种方法做同一件事?
想了很久,最后答案落在一个东西上:computational path length。
设 \(x_1\) 和 \(x_{100}\) 需要发生交互。RNN 里:
1x1 → h1 → h2 → h3 → ... → h99 → h100path length 随 sequence distance 线性增长,隔 100 个位置就要经过约 100 次 state transition。而 self-attention 同一层内,\(x_1\) 可以直接读到 \(x_{100}\):一步直达,path length 是 \(O(1)\),与距离无关。
这个差别至少在三个层面上产生后果。
距离不再自动等于路长#
先说清楚:RNN 不是不能表达远距离关系,这样说就冤枉它了。问题在于,距离越远,信息必须经过越多次 state transition,而每一次 transition 都可能修改、混合、压缩或丢失它。走到 \(h_{100}\) 时,\(x_1\) 的信息已经被反复改写了 99 次,剩下什么是路径决定的,不完全由任务决定。
self-attention 不要求“sequence distance ↑ → information path ↑”。long-range dependency 不再天然意味着 long computational path——这是我认为两者最本质的分界线,比“能不能看到上下文”准确得多。
梯度也要走同一条路#
Jacobian 连乘那篇写过,BPTT 时梯度是一串局部 Jacobian 的连乘:
$$\frac{\partial h_T}{\partial h_k} = \frac{\partial h_T}{\partial h_{T-1}} \frac{\partial h_{T-1}}{\partial h_{T-2}} \cdots \frac{\partial h_{k+1}}{\partial h_k}$$forward 的 information path 有多长,backward 的 credit assignment path 就有多长。连乘 \(T-k\) 个 Jacobian,梯度很难稳定,这是消失 / 爆炸的几何来源,LSTM 的 memory path 正是给这条路打的补丁。
而如果 forward 方向上两个位置之间就是一条直接的边,backward 也只需要走这一条边。所以 computational path length 同时牵动三件事:information flow、gradient propagation、long-range dependency 能不能学出来。只说“attention 缓解梯度消失”太便宜了——要点是路径在 architecture 层面变短了,连乘项数从随距离增长变成 \(O(1)\),问题的性质变了。
relationship 不必再间接形成#
RNN 构造当前位置的 representation 时,远处的信息通常已经历过很多次 sequential transformation,是被所有中间 timestep“加工”过的形态。self-attention 里,当前位置可以按需要直接读到远处的 representation,两个位置之间的关系直接建立,不再必须通过一串 intermediate states 间接蒸馏出来。
一个不太严谨但好记的类比:RNN 像传话,员工 1 的话要经过 99 次转述才到员工 100;self-attention 像员工 100 直接去找当前最相关的人问。差别不在谁更聪明,而在 architecture 改变了谁必须经过谁才能交流。类比到此为止。
sequence 有顺序,为什么计算要串行#
想通 path length 之后,我发现自己还有个一直没质疑过的默认假设:
1数据是 sequence → 有时间顺序 → 所以计算也应该按顺序执行RNN 把这三件事捆在一起卖。但它们其实不是一回事:数据具有 sequence structure,并不意味着 computation graph 必须是 sequential 的。顺序是数据的属性;计算要不要串行,是 architecture 的选择。RNN 选择把时间顺序直接编码进计算的执行顺序里,这是一个设计决定,不是义务。
这句话是后面敢把 recurrence 整个拆掉的思想基础之一。
parallelism 首先是 architecture property#
做 ML Infra 这些年,我第一次听到“self-attention 快是因为能用矩阵乘法”时是不以为意的——matmul 快谁不知道。后来才想明白这个因果讲反了:不是“能写成 dense matmul 所以快”,而是 dependency graph 先变了,dense matmul 才成为可能。
RNN 的依赖图:
\(h_3\) 必须等 \(h_2\) 算完,这是 algorithmic dependency,不是实现问题。就算 CUDA kernel 无限快,也消不掉这条依赖;GPU 再宽,sequence 方向上仍然只能一步一步走。
self-attention 同一层内部:
同一层里不同位置都只依赖上一层的 representations,位置之间没有先后约束——不需要 position 1 算完 position 2 才能算。sequence-level serialization 被大幅削掉。
dependency graph 摊平之后,才轮到后面那串好处出场:所有位置合进一个 batch、计算呈 dense matrix 形态、hardware utilization 上得去。顺序不能倒。
parallelism 首先是 architecture property,其次才是 implementation property。 很多性能问题不是写一个更好的 kernel 能救回来的,因为串行性写在算法的依赖结构里。
《Attention Is All You Need》真正押的注#
到这里可以谈这篇论文了。先分清一件容易糊的事:self-attention 和《Attention Is All You Need》不在同一个抽象层级。
self-attention 是一个 mechanism:sequence 内部不同 representations 直接交互。它完全可以和 RNN 共存——RNN backbone 加 self-attention,在当时没有任何矛盾,也确实有模型这么做。
AIAYN 进一步问的是:如果 self-attention 已经能承担 sequence 内部的 information routing,recurrence 还是不是不可替代的 backbone?
这才是论文最关键的跳跃。所以准确的写法不是“AIAYN 发明了 self-attention”,而是:self-attention 的存在,让“去掉 recurrence”成为一个合理的 architecture hypothesis;AIAYN 把这个 hypothesis 做成了完整模型,并证明它在 sequence transduction / machine translation 上可行而且有效——一个既不依赖 recurrence、也不依赖 convolution 的架构。
attention 的地位变化可以这么概括:
1attention = 辅助 recurrent backbone 的附加组件(Bahdanau 2014)
2 ↓
3attention = 核心 information-routing mechanism,backbone 可以被拆掉(AIAYN 2017)论文最激进的地方不是“attention 更强”,而是开始质疑 recurrence 是不是 sequence modeling 的必要条件。
被解绑的两件事#
回头看 RNN 的 \(h_t = f(x_t, h_{t-1})\),我注意到它其实同时在干两件事:维护 state,和传播信息。在 RNN 里这两件事几乎是同一件——information routing 本身就是通过 state update 完成的,想让信息流动,就必须维护一条 state 链。
attention 提供了一种新 primitive:representation 可以根据内容直接从其他 representations 获取信息,不需要一条中间 state 的搬运链。representation interaction 和 state propagation 不必绑定。
这两种 architecture,我各用一句话收束:RNN 是 information moves through state,信息沿着 state 移动;attention 是 information is retrieved from relevant representations,信息从相关的 representation 那里被取回。
另一个变化在 inductive bias 上。RNN 的默认 topology 是“前一个位置 → 下一个位置”,CNN 的默认 topology 是“当前位置 ↔ local neighborhood”——两者都把“谁能和谁交互”写死在结构里。attention 允许当前 representation 根据内容直接寻找和自己相关的位置:距离近不一定相关,距离远不一定不相关。relationship 从结构的默认假设,变成了一等公民。
和 CV 的那点缘分#
补一段个人经历。2017 年我在商汤实习时,最早听到《Attention Is All You Need》其实是在 CV 的环境里。多年后重新从 RNN 走到 self-attention,才反应过来:这篇论文并不是 CV 工作,它来自 machine translation;但它承载的想法为什么这么容易跨到 CV,其实不难理解。
convolution 也是 local operation。feature map 上两个远距离区域 A 和 B 需要交互时,传统 convolution 只能靠堆层数扩大 receptive field:
1A → local conv → local conv → ... → B“long-range interaction 必须经过 repeated local transformations”——这和 RNN 里 \(A \to \text{state} \to \cdots \to B\) 的困境在结构上是相似的。所以 CV 的人看到“任意两个位置可以直接按内容建立关系”,会感兴趣是自然的。attention 真正强的地方,是它提供了一种跨 domain 的 information-routing primitive,不绑定在序列这种数据形态上。
再往后的 CV attention 发展就不展开了,超出这篇的范围。
账的另一面#
self-attention 不是免费的,tradeoff 要老实记。
RNN 提供的东西其实不少:天然的 sequential inductive bias、明确的 temporal progression、每一步只和固定大小的 state 交互——计算和内存都随长度线性增长。
self-attention 换来的是更短的 computational path、更直接的 long-range interaction、sequence 级的 parallelism。代价是:
- 一个位置要和大量其他位置建立关系,interaction 数量随 sequence length 平方级增长,computation 和 memory cost 都跟着涨;
- 去掉 recurrence 之后,顺序不再天然编码在计算过程里——RNN 串行执行这件事本身就体现了“先后”,摊平的 attention 层需要额外把顺序信息补回去(具体怎么补,下一篇再说)。
这两条后来各自养出了一大片工作,但已经属于优化和变体的范畴,这篇停在 architecture 假设的层面。
结论#
这条演进链走完,我最想留下来的几句话:
- LSTM / GRU 改善的是 information 如何沿 recurrent path 传播;self-attention 开始质疑为什么一定需要这条 path。
- attention 的核心不是“模拟人的注意力”,而是 content-dependent dynamic read / information routing。
- early attention 的关键变化:从提前总结,变成按需读取。
- self-attention 把这种按需读取从 encoder–decoder 之间推广到 sequence 内部——变的不是机制,是谁在查询谁。
- long-range dependency 的问题不只是“能不能记住”,还有两个 representations 之间的 computational path 有多长——path length 同时决定 information flow、gradient propagation 和学习难度。
- sequence structure 不意味着 sequential computation。
- parallelism 首先是 architecture property,然后才是 implementation property。
- 《Attention Is All You Need》真正大胆之处,不是发明 attention,而是认真验证了:recurrence 可能并不是 sequence modeling 必需的 backbone。
一句话收束:从 RNN 到 self-attention,真正发生的变化,是我们不再默认 information 必须沿着 state 一步一步移动,而开始允许 representations 根据当前内容直接建立关系。
问题也随之落到下一站:既然允许一个位置直接读取其他位置,模型到底怎么判断——谁和我相关?相关程度是多少?我该从对方那里读什么? early attention 还能借翻译任务的对齐先验打个底,把 encoder–decoder 拆掉之后,这一切都要在一个 sequence 内部自己回答。这就是 Transformer 下一层设计要解决的事了。
参考资料#
- Bahdanau et al., Neural Machine Translation by Jointly Learning to Align and Translate, 2014 — https://arxiv.org/abs/1409.0473
- Sutskever et al., Sequence to Sequence Learning with Neural Networks, 2014 — https://arxiv.org/abs/1409.3215
- Cheng et al., Long Short-Term Memory-Networks for Machine Reading, 2016 — https://arxiv.org/abs/1601.06733
- Parikh et al., A Decomposable Attention Model for Natural Language Inference, 2016 — https://arxiv.org/abs/1606.01934
- Vaswani et al., Attention Is All You Need, 2017 — https://arxiv.org/abs/1706.03762
- 从 RNN / LSTM / GRU 到早期 Attention:为什么“压缩历史”最终变成了“按需读取”
- 从 RNN 到 LSTM,再到 GRU:循环网络真正改变的是什么?
- 从局部线性化到 RNN:理解 Jacobian 连乘