从符号主义到Transformer:一个NLP工程师的进化笔记

47 阅读6分钟

从符号主义到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的"线性传话筒",优雅地解决了长距离依赖。