课程版本:李宏毅《機器學習 2021》spring,Transformer (上)(2021-03-26) 官方讲义:
seq2seq_v9.pdf,60 页 — https://speech.ee.ntu.edu.tw/~hylee/ml/ml2021-course-data/seq2seq_v9.pdf 分P:BV1ou411N7X3?p=47,32:48 来源边界(上下讲切分):讲义页内标题的顺序很清楚:p2 "Sequence-to-sequence (Seq2seq)",p15 "Seq2seq Encoder Decoder",p16–p21 全在 Encoder(p19 Layer Norm、p20 BERT、p21 延伸阅读),p22 标题变为 "Decoder"。据此按讲义章节切:P47 = p1–p21(seq2seq 任务 + encoder),P48 = p22–p59(decoder、cross attention、训练与 tips)。metadata 只有总时长(32:48 / 1:00:34),不能确定视频逐页切点,故标注「讲义章节切分复习版」;无字幕,不引用逐字台词。
1. 这一讲解决什么问题
输入一个序列,输出另一个序列,且输出长度由模型决定(p2)。这句话里有两个"不固定":
- 输入长度不固定(一句话 5 个词还是 50 个词);
- 输出长度也不固定,而且不是输入长度的函数:翻译 "machine learning" → "機器學習" 是 2→4,语音识别是几百帧 → 几个字。
p2 列了三类:语音识别(vector sequence → character sequence)、机器翻译、语音翻译(没文字的闽南语语音直接到中文)。p3–p6 的闽南语例子很能说明"seq2seq 到底在学什么":乡土剧只有语音和中文旁白,没有闽南语文字、没有音素标注,讲义连问三遍 "Phonemes of Hokkien? / Background music & noises? / Noisy transcriptions?",答案都是 "Don't care"(p4)——模型直接从"语音序列"学到"中文字符序列",中间不需要人类先定义对齐。p7–p8 是聊天机器人与 QA,p10–p12 把句法树写成括号序列 (S (NP deep learning) (VP is (ADJV very powerful))),p13 多标签分类、p14 目标检测也被塞进 seq2seq 框架。
共同点:输出是一个变长序列,不能用固定维 softmax 一次给出。这就需要一个"读入整段输入"的 encoder 和一个"逐步产生输出"的 decoder。p15 明确把这套结构追溯到 Sutskever 2014 与 Transformer 2017 两条线。
2. Encoder:把 N 个输入变成 N 个输出
p16–p17:输入 \(x_1,\dots,x_N\),encoder 输出 \(h_1,\dots,h_N\)——每个位置一个向量,不是一个汇总向量。这一点常被误解:seq2seq 里确实有过"把整句压成一个 context vector"的做法(P48 的 p46 会看到),但 Transformer encoder 保留每个位置的表示,供 decoder 做 cross attention 时逐位置查询。
p17 右下角还有一句很重要的话:"You can use RNN or CNN." encoder 不是必须用 self-attention;Transformer 的选择是"用 self-attention 取代 recurrence",不是"encoder 只能这样"。
一个 block 里有什么(p18–p19)
p18 画的是一个重复若干层(图中三个 Block 加省略号)的结构:
x → Self-attention → (residual + norm) → FC → (residual + norm) → h
写成式子(\(\mathrm{LN}\) 表示 LayerNorm,\(\mathrm{FFN}\) 是逐位置的前馈网络):
p19 左侧是残差:\(\mathrm{FC}\) 旁边标着 \(a+b\),即输出 = 子层输出 + 子层输入。右侧是 LayerNorm 的定义:
其中 \(K\) 是一个 token 的特征维(不是序列长度,也不是 batch)。p19 图上写的 \(x_1,\dots,x_K\) 是同一位置的不同特征。
数值例:LayerNorm 到底在哪个方向归一(Python 计算)
取一个 token 的 4 维表示 \(x=[1,2,3,4]\):
归一化后均值 \(0.0\)、方差 \(1.0\)(脚本输出)。注意:只用了这一个 token 的 4 个数,与 batch 里其他样本无关。
对比 BatchNorm【补充,来自 normalization_v4.pdf p4/p6 的对照】:BN 是对一个 batch 的同一维统计。取 batch 两个样本、2 维:
batch = [[1, 10],
[3, 14]]
mean_per_dim = [2, 12] std_per_dim = [1, 2]
normalized = [[-1, -1],
[1, 1]]
同样是"减均值除标准差",BN 的均值和方差跨样本、按维;LN 的均值和方差在单样本内跨特征。这个方向差异决定了:序列长度变化、batch 很小甚至 batch=1 时,LN 仍然可用;BN 则需要足够大的 batch 来估统计量。
p21 给了两条延伸阅读:On Layer Normalization in the Transformer Architecture(2002.04745)与 PowerNorm(2003.07845),讨论 Post-LN / Pre-LN 的摆放差异。讲义 p19 的图是 residual 之后 norm(Post-LN);后来的实现常把 norm 放到子层之前(Pre-LN)以稳定深层训练——这是【补充】,讲义只给图与引用,没有展开比较。
p20 用 BERT 说明这套 encoder 的复用:"I use the same network architecture as transformer encoder." 注意这只说架构相同,不代表训练目标相同(BERT 还有 masked language model 预训练),这一句不要过度延伸。
还有两点容易被跳过。第一,encoder 不加因果 mask:它一次性看到整段输入,所以每个 \(h_i\) 都能融合左右两侧;decoder 的自注意力才需要遮住未来(P48)。这正是 encoder 能给 cross attention 提供"双向表示"的原因。第二,堆叠时只有第一层接收 \(x+\text{pos}\),之后每层的输入都是上一层的输出;所有层 shape 相同,但参数各自独立,不共享。
为什么一定要保留 \(N\) 个向量、而不是像旧式 seq2seq 那样压成一个?因为压成一个向量后,decoder 每一步拿到的输入完全相同,输入里的细节只能靠这个定长瓶颈携带;保留 \(N\) 个向量,decoder 每生成一个词都可以重新决定"现在该看输入的哪几个位置"(P48 的 cross attention)。代价是 encoder 输出从 \(d\) 维变成 \(N\times d\),cross attention 的分数矩阵变成矩形。
3. shape 与计算量
设序列长 \(N\)、模型维 \(d\)、FFN 隐层 \(d_{ff}\)(通常 \(4d\)):
| 位置 | shape |
|---|---|
| 输入 \(X\) | \(N\times d\) |
| \(Q,K,V\) | \(N\times d_k\)(多头时每头 \(d/M\)) |
| 注意力分数 | \(N\times N\) |
| 子层输出 | \(N\times d\) |
| FFN 第一层 | \(N\times d_{ff}\) |
| block 输出 | \(N\times d\) |
(本表按 \(N\times d\) 的行向量约定:每行一个 token。P36/P48 的注意力公式把 token 按列排成 \(d\times N\),两套写法互为转置,\(A'\) 的 softmax 轴也随之从列方向换到行方向,数值等价。)
block 不改变 shape,所以可以堆任意层。计算量的大头是 \(N^2d\)(注意力)与 \(N d^2\)(投影/FFN);序列短时 FFN 占比高,序列长时 \(N^2\) 占主导。
参数量也能这样估(\(d=512\)、\(d_{ff}=2048\)、\(M=8\),Python 计算):
| 组件 | 参数量 |
|---|---|
| 多头投影 \(W_q,W_k,W_v,W^O\) | \(4d^2\) = 1,048,576 |
| FFN \(d\to d_{ff}\to d\) | \(2d\,d_{ff}\) = 2,097,152 |
| 单个 block 合计 | 3,145,728 |
| 若堆 6 层(仅估算示例) | 18,874,368(约 \(1.887\times10^7\)) |
可以看到 FFN 占了三分之二——"Transformer 的参数主要在 FFN,不在 attention"这句话就是从这个比例来的。多头并不增加总参数:8 个头每头 64 维,拼起来仍是 \(d\) 维,\(W^O\) 还是 \(d\times d\)。
4. 伪代码
def encoder_block(x, Wq, Wk, Wv, Wo, W1, b1, W2, b2):
z = multi_head_attention(x, Wq, Wk, Wv, Wo) # (N,d)
x = layer_norm(x + z) # residual + norm
f = relu(x @ W1 + b1) @ W2 + b2 # 逐位置 FFN
return layer_norm(x + f) # (N,d)
h = x
for _ in range(num_layers):
h = encoder_block(h, ...)
5. 常见误区
- "encoder 把整句压成一个向量"。Transformer encoder 输出 \(N\) 个向量;"压成一个"是旧式 seq2seq 的 context vector,二者不要混。
- "LayerNorm 就是 BatchNorm 换个名字"。归一化方向不同:LN 在单样本内跨特征,BN 跨样本按维。数值例已给出两种结果。
- "residual 只是让梯度好传"。它同时保留了下层信息,使每个 block 学的是增量;去掉残差,深层 encoder 很难训。
- "self-attention 本身知道位置"。不知道(P37 p29 的置换实验);encoder 必须额外加 positional encoding,否则句序信息在进入第一个 block 时就丢了。
- "encoder 必须用 self-attention"。p17 明说可以用 RNN 或 CNN;选 self-attention 是为了并行与长距离,不是定义要求。
- "Post-LN 和 Pre-LN 等价"。摆放位置影响深层训练稳定性;讲义图是 Post-LN,延伸阅读讨论的是这个选择。
6. 自测
Q1. encoder 输入 \(N=20\)、\(d=512\),\(M=8\) 头。注意力分数矩阵多大?每个头的 \(d_k\) 是多少?
答案
分数矩阵 \(20\times20\)(每个头一个,共 8 个);每头 \(d_k=d_v=512/8=64\)。注意 \(N\times N\) 与 \(d\) 无关,长序列时它是主要开销。
Q2. 对一个 token 的表示 \([2,4,6,8]\) 做 LayerNorm(总体标准差),结果是什么?如果对 batch 里两个样本的同一维做 BN,统计量会不同吗?
答案
均值 5,偏差 \([-3,-1,1,3]\),方差 \((9+1+1+9)/4=5\),\(\sigma=2.236\),结果为 \([-1.3416,-0.4472,0.4472,1.3416]\)。BN 会用两个样本在该维上的均值/标准差,数值取决于另一个样本;LN 不会。
Q3. 为什么 encoder block 里每个子层后面都要 residual?如果去掉,可能发生什么?
答案
残差让子层只需学"相对于输入的修改量",并为梯度提供一条恒等路径。去掉后,深层堆叠时梯度要穿过每个子层的雅可比连乘,容易出现退化/难训;同时下层的有用信息也没有旁路保留。
Q4. p20 说 BERT 用了"同样的 encoder 架构",能否据此说 BERT 就是 Transformer encoder?
答案
不能。相同的是 block 结构(self-attention + FFN + residual + LN);不同的是训练目标(BERT 有 masked LM / next sentence 等预训练)与使用方式。讲义这句话是在讲架构复用,不是在讲训练方法等价。
7. 30 秒速记
seq2seq:输入序列 → 输出序列,输出长度由模型决定,所以不能用一个固定 softmax。encoder 把 \(N\) 个输入变成 \(N\) 个 \(d\) 维表示;每个 block = 多头 self-attention + residual + LayerNorm,再 + 逐位置 FFN + residual + LayerNorm,shape 不变所以能堆。LayerNorm 在单个 token 的特征维上算均值/方差(\([1,2,3,4]\to[-1.34,-0.45,0.45,1.34]\)),与 batch 无关;BN 才是跨样本按维。encoder 也可以用 RNN/CNN(p17),self-attention 不是定义。
跨 P 连接:P36/P37 给了 self-attention 与多头/位置编码,这一讲把它们组装成 encoder block;P48 从 p22 起讲 decoder——自回归、因果 mask、cross attention(Q 来自 decoder、K/V 来自 encoder)、teacher forcing 与 exposure bias。P38 的 RNN 是 p17 所说的另一种 encoder 选择,也是理解"为什么要去掉 recurrence"的对照。
引用页(PDF 物理页,1 起)
p2(seq2seq 定义与输出长度)、p3–p8(语音/翻译/TTS/ chatbot/QA)、p10–p14(parsing、多标签、检测)、p15–p17(encoder-decoder 框架、逐位置输出、可用 RNN/CNN)、p18(block 结构)、p19(residual 与 LayerNorm 公式)、p20(BERT 复用架构)、p21(Post-/Pre-LN 延伸阅读)。