transformer
核心创新点:Transformer 架构和 Self-Attention 自注意力机制
Transformer 之前,主流序列转导模型通常具有以下特征:
- 主体结构多基于 RNN,CNN 作为替代路线
- 通常采用 encoder-decoder 架构
- 使用注意力机制增强
Transformer 结构的创新:
- 用 Self-Attention 取代 RNN/CNN 作为序列建模核心
- 引入 Multi-Head Attention,让模型从多个子空间看关系
- 用 Positional Encoding 补足序列位置信息
Transformer是首个完全基于自注意力、不使用RNN或CNN的序列建模模型
Encoder-Decoder 架构
早期 seq2seq 的核心思想:encoder 先压缩,decoder 再生成
- 编码器负责“读懂输入序列”,把它压缩成一个向量
- 解码器负责“根据这个向量逐步生成输出序列
编码器解码器结构解决了输入输出不等长的问题
早期 encoder 通常是一个 RNN / LSTM / GRU,编码器按顺序读入 token
每读一个 token,更新一次隐藏状态
解码器 decoder 在第 $t$ 步根据:
- encoder 给出的上下文向量 $c$
- 上一步生成的 token $y_{t-1}$
- decoder 自己的隐藏状态 $s_{t-1}$
预测当前 token $P(y_t \mid y_{<t}, c)$
早期模型最大的问题是:不管输入句子多长,都要压缩进一个固定长度向量 $c$
这会导致:
- 长句信息容易丢失;
- 远距离依赖难以保存;
- decoder 生成后半句时,可能已经“忘了”输入前面的内容;
- 所有源句信息都挤在一个固定向量里,形成 bottleneck
Attention 机制
加入 attention 之后
1 | source sequence -> Encoder -> h1, h2, ..., hn |
decoder 不再只看一个固定向量 $c$,而是在生成每个词时动态查看 encoder 的所有隐藏状态
解决的问题:
解决模型处理长序列时的“遗忘”问题
解决不同时间步输入对当前时刻输出的“重要性”问题
decoder 每生成一个词,都计算当前解码状态和所有 encoder 隐藏状态的相关性,然后加权汇总
$$ c_t = \sum_{i=1}^{n} \alpha_{t,i} h_i $$
位置编码
因为没有 RNN 的时间递推,也没有 CNN 的局部窗口顺序结构,Self-Attention 本身不知道 token 的顺序
在 encoder 和 decoder 底部给输入 embedding 加入 positional encodings,用来注入 token 的位置信息
- 低维度:变化快,区分近距离位置
- 高维度:变化慢,表达长距离趋势
为什么不用简单的 1, 2, 3, 4?
Bert有这么做过,直接用整数位置有几个问题:
- 标量位置太粗糙,难以和高维词向量融合
- 数值大小会随序列长度变大,尺度不稳定
- 不容易表达相对位置关系,比如“前一个词”“后两个词”
正弦/余弦编码的好处是:每个位置被表示成一个稳定的高维模式,而且不同频率可以覆盖不同尺度的位置关系
论文中给出的一个重要动机是:对于固定偏移 $k$,$PE_{pos+k}$ 可以表示为 $PE_{pos}$ 的线性函数,因此模型可能更容易学习相对位置关系
多头注意力机制
多头注意力 Multi-Head Attention 的核心思想是:把同一批 token 映射到多个不同的表示子空间中,并行做多次 attention,然后把结果拼接起来
Transformer 使用的基础注意力是 Scaled Dot-Product Attention
$Q,K,V$ 都来自同一个输入序列,让输入序列内部的 token 互相建模关系
QK^T:计算每个 token 对其他 token 的关注程度- softmax:把关注分数归一化成权重
- 乘 V:根据权重加权汇聚信息
经过这个Attention以后的向量信息会融合其它token的信息
如果只有一个 attention head,那么模型只有一套 $Q,K,V$ 投影,那么token只能用一种方式去理解自己
但是语言中token的关系从来不是一维的,单头使得被迫将不同层面的关系压缩进一组权重,注意力权重变成一个折中的分布,一个注意力分布无法承载多种独立的关系模式,表达能力被严重限制
多头注意力的设计就是:允许模型在多个子空间中并行学习不同的相关性模式
多头注意力不是直接对原始 $Q,K,V$ 做很多次一样的 attention,而是先用不同的线性变换得到不同 head 的 $Q_i,K_i,V_i$
1 | q_proj = nn.Linear(d_model, d_model) |
第 $i$ 个 head 是
这种模式使得每个头只需要捕获一种或少量关系模式
这里也会有一个问题,拆成多个头降低了维度,可能会损失表达能力,但损失的远小于收益
拆分成多头在同等计算量上获取到的信息大于单头,并且低维子空间相当于隐含的正则化,保证每种学习关系独立,也防止了每个头在高维空间过拟合,多头保证了多种关系可以并行捕捉
head数量需要根据实际情况决定,head 太少,表达能力可能不足;head 太多,每个 head 的维度会变小,单个 head 的表示能力下降,而且计算和工程开销增加
通常 $h$ 是一个超参数,需要和 $d_{\text{model}}$ 配合
Mask
mask 不是加在 $Q$、$K$、$V$ 上,而是加在 attention logits 上
加Mask是为了避免关注到后面的信息,只注意自己和之前的信息
张量形状
每个 token 原始 hidden state 的维度是
1 | Q: [B, L, d_model] |
然后拆成 $h$ 个 head:
1 | Q: [B, h, L, d_head] = [B, 8, L, 64] |
每个 head 单独做 attention
1 | head_i: [B, L, 64] |
8 个 head 拼接后
1 | Concat(heads): [B, L, 512] |
最后再经过输出投影 $W^O$
分母 $\sqrt{d_k}$
注意力分数是:
那么单项乘积 $q_i k_i$ 的均值大约是 $0$,方差大约是 $1$
点积是 $d_k$ 个这样的项相加,所以方差大约是
为了把点积分数标准化到相对稳定的尺度,需要除以标准差
如果不除会导致
- attention 权重过早接近 one-hot,模型探索性变差
- softmax 进入饱和区,梯度很小,训练变慢或不稳定
本质上类似于初始化中的方差归一化思想
Post-LN和Pre-LN的区别
Post-LayerNorm是原文设计
1 | x |
Attention block:
所以梯度随网络深度呈指数衰减,导致低层(靠近输入的层)梯度几乎消失,梯度消失会导致Adam等优化器的更新变得不稳定
Layer Norm
对每个 token 的 hidden dimension 做归一化
- 不跨 batch 归一化
- 不跨 sequence length 归一化
- 只在每个 token 自己的特征维度上归一化
归一化:
Transformer 需要 Layer Norm是因为每一层都有复杂变换
如果不做归一化,层数加深后 hidden states 的尺度可能变得不稳定
Layer Norm 的作用是把每个 token 的 hidden state 拉回到相对稳定的尺度
Post-LayerNorm
Post-LN 是原始 Transformer 的写法;Pre-LN 是后来更常用的稳定训练写法
1 | x |
Attention block:
深层模型难训练(论文原文中提及)
对超参数(学习率、初始化、warm-up)更敏感 |
Pre-LayerNorm
把 LayerNorm 移到子层前面
1 | x |
Attention block:
对超参数(学习率、初始化、warm-up)更鲁棒
残差连接保留信息更直接,梯度传播更稳定 | 深层更新可能被残差主干稀释 |


