第07回 Transformer 横空出世——抛弃循环的全新架构
不靠回头翻旧账,满卷仍能照前尘。
千词同看一席坐,诸般关系一眼分。
上回我们学了注意力:问(Q)起,钥(K)应,账(V)来——一束灯光照要津。
可你也看到了它的野心:它不止想做“某一步的聚焦”,它想重写整座城的交通规则。
RNN/LSTM 像赶路写日记:一步一步来,顺序强、但难并行,长句很累。
Transformer 则像“把整句话摊在桌上同时审卷”:
每个词都能看见别的词,关系一网打尽;训练也能并行,像大工坊同开百炉。
这便是本回要讲的:Transformer 的骨架如何搭起,以及它为什么能成为大模型的正宗门派。
一、先看城门:Transformer 的两座大殿
经典 Transformer(2017 原版)有两座大殿:
- 编码器(Encoder):把输入句子“读懂”,变成一串更好的表示
- 解码器(Decoder):在生成时“边写边看”,一步一步吐出下一个词
若是翻译:
- 编码器读源语言
- 解码器写目标语言,并通过交叉注意力“看编码器”
若是纯生成(像 GPT):
- 只保留解码器这一路(因果遮罩保证不偷看未来)
若是理解任务(像 BERT):
- 多用编码器结构(双向看上下文)
本书后面第08回会把“预训练两条路”讲清:为何同是 Transformer,训练目标不同,性格就不同。
二、Transformer 的一层长什么样:四件套
不管是编码器层还是解码器层,核心都像“积木四件套”:
- 注意力(自注意力 or 因果自注意力)
- 前馈网络(FFN):对每个位置单独做一次小 MLP
- 残差连接(Residual):走一步留后路,训练更稳
- 归一化(LayerNorm):把激活拉回合适尺度,少走极端
你可以把它当作练功口诀:
- 注意力负责“找关系”
- FFN 负责“做变换”
- 残差负责“不忘旧功”
- 归一化负责“气息平衡”
层与层堆叠,就像功力层层加深:
浅层学词法,深层学语义,再深些学任务所需的抽象结构。
三、位置编码:抛弃循环后,顺序怎么办
Transformer 最大的“革命”,是抛弃循环。
可一旦抛弃循环,模型默认就“看不见顺序”——
因为注意力本身只看向量之间的相似度,不自带“第几个词”的概念。
于是必须引入位置信息。常见做法是:
- 绝对位置编码:给第 1、2、3… 个位置各一份向量,加到词向量上
- 相对位置编码:让“距离”进入注意力打分(更适合长上下文扩展)
你不必在本回背具体公式,只要抓住直觉:
词向量负责“这是谁”,位置负责“他站哪”。
后面讲长上下文与后 Transformer(第41回)时,你会更深刻地体会:
位置设计不只是装饰,它直接影响模型能走多远。
四、训练并行的秘密:全句同时过一层
RNN 的训练像排队:第 步必须等第 步算完。
Transformer 的训练像流水线:
同一层里,所有位置的 Q/K/V 可以一起算;注意力矩阵也可批量算。
这使得 Transformer 特别适合“堆规模”:
数据多、卡多、并行强,就能把模型越炼越大。
这条工程优势,直接推动了后来的 LLM 时代:
不是说 RNN 不聪明,而是 Transformer 更适合在现代硬件上“练大功”。
五、现代江湖的补丁:为何 2024 仍在“修 Transformer”
你若以为 Transformer 2017 一定型就不再变,那就小看江湖了。
2024–2026 的现实是:Transformer 仍是主力,但大家不断给它加“补丁”,原因多半绕不开两件事:
- 长上下文太贵
- 注意力实现太吃带宽与显存
因此你会看到两类研究/工程努力:
1)更深的关联:有人把注意力与状态空间模型的关系讲得更透,提出“Transformers are SSMs”一类的统一视角,甚至由此导出更快的结构(例如与 Mamba-2 相关的 SSD 框架)。1
2)更快的实现:有人不改数学,只改算法与硬件实现,例如 FlashAttention-3 之类,把注意力这块“最费钱的地段”修成高速路。2
你读到这里要明白:
Transformer 像一座大城,城里道路(注意力)最拥堵,大家一边扩城,一边修路。
第五篇我们会讲另一种思路:干脆换一种交通系统(SSM/Mamba)。
六、极简代码:手写一个“Transformer 块”(PyTorch 可跑)
下面这段代码实现一个最小的 Transformer block:
自注意力 + FFN + 残差 + LayerNorm。
它不是为了跑出最强效果,而是让你摸到骨架。
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
class TinySelfAttention(nn.Module):
def __init__(self, d_model):
super().__init__()
self.q = nn.Linear(d_model, d_model, bias=False)
self.k = nn.Linear(d_model, d_model, bias=False)
self.v = nn.Linear(d_model, d_model, bias=False)
self.o = nn.Linear(d_model, d_model, bias=False)
def forward(self, x, mask=None):
# x: [B, T, D]
q = self.q(x)
k = self.k(x)
v = self.v(x)
d = x.size(-1)
scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(d) # [B, T, T]
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
w = F.softmax(scores, dim=-1)
out = torch.matmul(w, v)
return self.o(out)
class TinyTransformerBlock(nn.Module):
def __init__(self, d_model=64, d_ff=128):
super().__init__()
self.ln1 = nn.LayerNorm(d_model)
self.attn = TinySelfAttention(d_model)
self.ln2 = nn.LayerNorm(d_model)
self.ff = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.GELU(),
nn.Linear(d_ff, d_model),
)
def forward(self, x, mask=None):
x = x + self.attn(self.ln1(x), mask=mask)
x = x + self.ff(self.ln2(x))
return x
if __name__ == "__main__":
torch.manual_seed(0)
B, T, D = 2, 5, 64
x = torch.randn(B, T, D)
block = TinyTransformerBlock(d_model=D, d_ff=128)
y = block(x)
print("out shape:", y.shape)
你若把这块“积木”堆上很多层,再加上词嵌入、位置编码、输出头、训练目标,
Transformer 就从“骨架”长成“能说会写的模型”。
第08回我们就要讲:它到底用什么目标去练,才能练成 BERT 或 GPT 的两种性格。
七、小结:本回搭骨架,下回讲“怎么练出性格”
本回你已把 Transformer 的城池看明白:
- 编码器/解码器两大殿
- 注意力 + FFN + 残差 + 归一化四件套
- 位置编码解决“站位”
- 并行训练让它适合规模化练功
可你仍会问:
“同样是 Transformer,为什么 BERT 擅长理解,GPT 擅长生成?”
下一回就讲“预训练的两条路”:
掩码语言模型与自回归目标,如何塑造模型的出招方式。
欲知后事如何,且听下回分解。
引用与溯源
Footnotes
-
Dao, T., Gu, A. Transformers are SSMs: Generalized Models and Efficient Algorithms Through Structured State Space Duality arXiv:2405.21060(2024-05)https://arxiv.org/abs/2405.21060 ↩
-
Shah, J., et al. FlashAttention-3: Fast and Accurate Attention with Asynchrony and Low-precision arXiv:2407.08608(2024-07)https://arxiv.org/abs/2407.08608 ↩