从符号主义到Transformer:一个NLP工程师的进化笔记
这篇文章源于我的一次毕设学习经历。当我第一次打开《Attention Is All You Need》时,满篇的QKV矩阵让我怀疑人生。但经过几个月的折磨,我发现Transformer的本质意外简单——它只是一个擅长"高效八卦"的注意力网络。本文用学习笔记的形式,记录从CNN到RNN再到Transformer的完整思维跃迁。
一、符号主义的崩溃:当精确函数遇到真实世界
早期人工智能的梦想很朴素:用数学函数描述万物。f(x)=wx+b,只要参数调得好,就能预测房价、分类猫狗。但现实很快给了当头一棒——语言、图像、情感这些"模糊"概念,根本找不到精确表达式。
于是联结主义登场:不懂就猜,用非线性函数暴力拟合。一个简单的感知机变成了千层蛋糕:
Python
复制
# 线性+激活 无限套娃
f(x) = g(w3·g(w1·x + b1) + b2)
这就是神经网络的本质:线性变换嵌套非线性激活,一层不够就堆一百层。
但参数怎么找?—— 反向传播 。定义损失函数L(y, ŷ) = (真实值-预测值)²,然后用链式法则逐层求偏导,让所有参数朝着"让误差更小"的方向挪一点点。这个过程反复迭代,模型就"学会"了拟合数据。
二、CNN与RNN:两个领域的权宜之计
CNN:图像处理的"局部信仰"
当神经网络杀入图像领域,全连接层立刻崩溃。一张256×256的图片,拉平后是65536维,权重矩阵直接炸显存。CNN的天才在于:假设像素只和邻居有关。用3×3卷积核扫一遍,参数暴降99%,还能抓住边缘、纹理等局部特征。
Python
复制
# CNN的信念:远处像素不相关
特征图 = 卷积核(3×3领域) # O(n)复杂度
RNN:文本处理的"记忆幻觉"
文本是序列数据,CNN的3词窗口不够用。RNN提出"循环结构":把上一步的隐藏状态h_t-1作为输入传给下一步,理论上能记住全文。但悲剧的是:梯度消失让它的实际记忆只有3-5个词。
Python
复制
# RNN的尴尬:想记住,但记不住
h_t = tanh(W·h_t-1 + U·x_t) # 10个词后梯度≈0
致命对比:
- CNN:O(n)效率,但看不到长距离依赖
- RNN:理论上无限记忆,但O(n)串行+梯度消失=实际残废
三、Transformer的破局:放弃递归,拥抱注意力
2017年,Transformer登场,核心思想简单粗暴:既然记不住,那就别记——所有人同时看所有人。
第一步:从Token到Embedding
词汇不是直接变成向量,而是分两步:
Python
复制
# 1. Tokenization:文本→整数ID
"我 爱 AI" → [1024, 2048, 3072]
# 2. Embedding:查表→高维向量
embedding_table = nn.Embedding(50000, 768) # 可训练矩阵
vectors = embedding_table([1024, 2048, 3072]) # 形状: [3, 768]
关键:Embedding是模型的第一层权重,不是静态词典,训练时同步更新。
第二步:自注意力机制——全员八卦大会
每个词生成三个向量:Query(我想问谁)、Key(谁能被问到)、Value(我有什么信息) 。然后所有词同时互相打分:
Python
复制
# QK点积得注意力分数
scores = Q @ K.T # 形状: [seq_len, seq_len]
# Softmax归一化
weights = softmax(scores) # 每行之和=1
# 加权融合所有V
output = weights @ V # 形状: [seq_len, dim]
结果:每个新向量都直接融合了全文的加权和。改变任意一个词,所有输出同时变化——这是矩阵乘法的暴力美学,不是间接传递。
第三步:多头注意力——8个平行宇宙
单头注意力只能捕获一种关系(如句法)。多头是空间并行不是时序重复:
Python
复制
# 头1: 关注语法关系
# 头2: 关注指代关系
# 头3: 关注语义相似度
# ...8个头同时计算
head_outputs = [attention(Q_i, K_i, V_i) for i in range(8)]
output = concat(head_outputs) @ W_o # 拼接后投影
核心:把768维向量切成8份96维,每组独立算注意力,最后拼接。不是"再算一次",是"8路并行"。
四、架构拆解:编码器-解码器的双城记
编码器:只做理解,不做生成
Python
复制
输入 → Embedding → 位置编码
→ 多头自注意力(全员看全员)→ 残差/Norm
→ 全连接网络(每个词独立MLP)→ 残差/Norm
→ **输出上下文矩阵C**
C不是QK! C是编码器最后一层的输出,形状[seq_len, dim]。解码器用C来生成K和V,自己生成Q来查询原文。
解码器:理解+生成,步步为营
解码器每层两个注意力,功能截然不同:
Python
复制
解码器每层 =
输入 → Embedding → 位置编码
→ **掩码自注意力**(只能看已生成的,防止偷看未来)
→ 残差/Norm
→ **编码-解码注意力**(Q来自解码器,K,V来自C)
→ 残差/Norm
→ 全连接网络
训练 vs 推理:
- 训练:输入是右移的真答案(teacher forcing),快速收敛
- 推理:输入是自己生成的词(自回归),逐字预测
五、常见误区:我踩过的坑
误区1:Token和Embedding是替换关系
真相:Token是地址,Embedding是地址里的内容。不是替换,是查表。
误区2:多头是重复计算
真相:多头是并行分组,不是时序循环。8个头=8个独立小模型同时跑。
误区3:编码器输出QK
真相:编码器只输出C。只有解码器做交叉注意力时,临时用C生成K和V。
误区4:全连接网络有交流
真相:全连接是位置独立的,每个词各走各的MLP。交流全靠注意力。
六、总结:为什么Transformer赢了?
CNN:O(n)效率,但看不到远处
RNN:理论上无限记忆,但串行O(n)+梯度消失=实际残废
Transformer:O(n²)暴力直连,但并行计算+全局视野抵消了复杂度代价
本质:当硬件允许O(n²)时,结构简单性战胜算法巧思。注意力机制用"全员同步八卦"替代RNN的"线性传话筒",优雅地解决了长距离依赖。