本文是「零基础学大模型原理」系列的第 3 篇。这个系列记录一个没有任何机器学习背景的工程师,在 ChatGPT 发布之后从零补原理的过程。
- 神经网络入门:从感知机到反向传播
- 词怎么变成向量:从 one-hot 到 Word2Vec
- Transformer 之前:RNN、seq2seq 与注意力机制(本文)
上一篇解决了「词怎么变成数字」:Word2Vec 把每个词压成几百维向量,语义关系变成向量空间里的几何关系。但那篇结尾留了两个问题:一个词只有一个固定向量,「苹果」在任何句子里都是同一个向量,一词多义无法处理;即使每个词都有向量,一句话仍是一串向量,「我打狗」和「狗打我」的词完全相同、意思相反,差别在顺序里。机器翻译资料里反复出现 RNN、seq2seq、attention 这几个词,这两周我一直在梳理它们之间的关系。前置知识还是上一篇:知道词向量是什么就够。本文介绍 Transformer 出现之前的主流做法,读语言模型资料时会遇到它们。
句子中的顺序信息
先明确问题。词向量解决了「词」的表示,但句子不是词袋。「我打狗」和「狗打我」由完全相同的三个词组成,把三个词向量相加后取平均,两句话得到相同的表示。模型要处理句子,需要按顺序读入词,并让后续计算能使用已经读过的信息。
这就是序列模型处理的问题。2014 到 2017 年间,机器翻译常用 RNN(循环神经网络),再在其上使用 seq2seq 编码器-解码器结构和注意力机制。它们也是后来 Transformer 的前序方案。
RNN 按顺序更新隐状态
RNN 按顺序逐个读词,每读一个词就更新一次隐状态。形式上:
是第 个词的向量, 是读到前一个词为止的隐状态, 是更新后的隐状态。 和 是需要训练的参数,所有时间步共享同一套参数。读完整句话后,最后的 可作为整句话的压缩表示。
这个结构对工程背景的人很眼熟:它是一个循环,隐状态是循环中持续携带的变量,每轮迭代读入一个新元素、更新一次状态。参数在时间上复用,因此序列长度不同也能使用同一套参数,输入是 5 个词还是 50 个词都不需要改结构。
最朴素的 RNN 存在梯度消失和梯度爆炸问题。训练时,梯度要沿时间步传回去,每传一步都会乘以局部导数。连乘几十次后,靠前词对应的梯度可能指数级缩小到接近零,也可能增大到不稳定的范围。句子变长时,开头信息对结尾状态的影响可能变弱。「我出生在中国,……中间隔着五十个词……所以我会说中文」这类长距离依赖,朴素 RNN 往往难以学习。
LSTM 使用门控更新记忆
LSTM 是对此问题的工程解法,1997 年已经提出。它把状态分成两部分:细胞状态用于携带长期信息,隐状态作为对外暴露的工作状态。三组门控制信息流动:
- 遗忘门:控制保留多少旧的细胞状态
- 输入门:控制写入多少当前候选信息
- 输出门:控制向下一时间步输出多少细胞状态
每个门通过 sigmoid 输出 0 到 1 之间的值,相当于可学习的连续开关。我读的是 colah 的 Understanding LSTM Networks,配图把细胞状态画成贯穿序列的传送带,门控制传送带上的信息。细胞状态提供了一条梯度较容易跨多个时间步传播的路径,减少了每一步都经过完整矩阵乘和非线性变换的情况,因此有助于学习长距离依赖;它不保证梯度在任意长度上都不会衰减或爆炸。
三个门和生成候选细胞状态的一层,共有四组参数,公式写出来接近一页,我没有逐个推。当时记下的判断是:LSTM 保留了顺序处理,只是让这种处理在较长句子上更可用。状态仍然按时间步依次更新。
seq2seq 的固定向量限制
2014 年 Sutskever 在 Google 的论文把 LSTM 用于机器翻译的编码器-解码器结构,简称 seq2seq。编码器用 LSTM 逐词读取源语言句子,将最终状态作为句子表示;解码器用另一个 LSTM,根据这个表示逐词生成目标语言。每生成一个词,解码器将上一步生成的词和自身状态用于后续计算。这种方法使神经机器翻译获得了可用结果;在此之前,短语统计机器翻译是常用方案,规则和特征需要人工设计。
这个结构有一个限制:无论源句是 5 个词还是 50 个词,编码器的信息都集中在最后的固定长度向量中,解码器只能直接访问这个向量。Jay Alammar 的图解中,编码器和解码器之间只有这条信息通道。句子越长,单个向量需要编码的信息越多。Bahdanau 论文引用的实验观察显示,编码器-解码器模型的翻译质量(BLEU 分数)会随句长增加而下降;论文将固定长度向量视为原因之一。
这里的限制来自结构:整句话通过一个向量传递。仅替换更强的 RNN 单元不能让解码器直接访问各个源词位置。
注意力让解码器访问编码器状态
同在 2014 年,Bahdanau 等人的论文提出注意力机制。解码器在生成每个词时,都可以访问编码器所有时间步的隐状态,并按相关程度计算当前的上下文向量,而非只使用最后一个状态。
计算分三步。第一步是打分:用解码器的前一状态和编码器各位置的隐状态计算相关分数,原论文使用参数可学习的小型前馈网络。第二步是归一化:对所有分数做 softmax,得到和为 1 的权重,分数较高的位置权重较大。第三步是加权求和:用这些权重对各位置的隐状态加权求和,得到当前上下文向量,再将其用于解码器的状态更新和词预测。每一步解码都会重新计算权重,上下文向量也会变化。
有两个细节值得记。Bahdanau 的编码器使用双向 RNN,每个位置的注释向量由从左到右和从右到左的隐状态拼接而成,因此包含该位置两侧的上下文。上一篇遗留的一词多义问题在这里有了初步回应:编码器各位置的隐状态不再是孤立词向量。以单向 RNN 为例,隐状态包含此前词的信息;双向 RNN 还包含后续词的信息,因此「苹果」在「吃苹果」和「苹果公司」中对应的状态会不同。词向量是静态的,隐状态依赖上下文。
手算注意力权重
拿一个小例子走查。「我 爱 吃 苹果」翻译成 “I love eating apples”,看解码器生成最后一个词 “apples” 的那一步。
真实模型里的隐状态有几百维,这里压成 2 维,便于手算。假设编码器读完四个词后的隐状态是:
- 我:
- 爱:
- 吃:
- 苹果:
解码器走到生成 “apples” 这一步,设用于计算注意力的前一状态为 。用最简单的点积打分。真实论文的打分网络更复杂,点积足够展示流程:
对 2、1、3、5 做 softmax。e 的幂分别是 7.39、2.72、20.09、148.41,合计 178.61,归一化后:
权重和为 1。这一步的上下文向量:
0.83 的权重落在「苹果」上,上下文向量接近「苹果」的隐状态。这个向量会和解码器状态一起参与词预测,但仅凭上下文向量不能推出 “apples” 一定是最高分,最终概率还取决于解码器参数和此前生成的词。生成 “I” 时会重新计算另一组权重,在这个例子中可设计为将较大权重分配给「我」。每一步解码都有各自的上下文。Bahdanau 论文标题中的 align 指软对齐:注意力权重可表示源词位置与目标词生成步骤之间的软对应关系。论文中的英法权重热力图展示了这种对应关系。
仍然存在的顺序处理限制
到这里,2014 年的关系可以这样整理:RNN 按顺序读句子,LSTM 通过门控改善长距离依赖的学习,seq2seq 用编码器-解码器做翻译,注意力让解码器能访问所有编码器状态。当时我理解到这一层,仍把注意力看作挂在 RNN 编码器和解码器之间的辅助结构。
RNN 的计算依赖仍然存在:第 步必须等第 步完成后才能开始。同一序列的时间步无法并行,序列长度增加时,循环部分的计算路径随之增加,GPU 难以在时间维度发挥并行能力。注意力的打分和加权可在各编码器位置上并行计算,但 RNN 部分仍然串行。注意力让解码器直接访问所有编码器状态;编码器状态本身仍由 RNN 逐步计算,长距离依赖的问题只是得到缓解。
翻资料时看到 2017 年有篇论文,标题是 Attention is All You Need。只保留注意力时,顺序信息从哪里来,逐词生成如何进行?下一篇讨论这些问题。
本篇参考的资料
- Sequence to Sequence Learning with Neural Networks(Sutskever et al., 2014):编码器-解码器结构,「整句话压成一个固定向量」的设计出自这里。
- Neural Machine Translation by Jointly Learning to Align and Translate(Bahdanau et al., 2014):注意力机制的原始论文,打分、softmax、加权求和三步与软对齐热力图出自这里。
- On the Properties of Neural Machine Translation: Encoder-Decoder Approaches(Cho et al., 2014):长句 BLEU 分数随句长下降这一观察的原始出处,Bahdanau 论文开头引用的就是它。
- Understanding LSTM Networks(colah’s blog, 2015):LSTM 门控的图解,本篇对门和细胞状态的直觉全部来自这篇。
- Visualizing A Neural Machine Translation Model(Jay Alammar, 2018):seq2seq 与注意力的图解,编码器-解码器之间「唯一通道」的画面来自这篇。

