传统NLP
Transformer一开始是为了解决NLP问题而提出的,在最初的论文中用的案例也是翻译任务。我们先来看看传统NLP到底在解决什么问题,并使用了哪些方法。理解传统NLP的局限性,是理解Transformer为何革命性的关键前提。
自然语言处理的核心任务
自然语言处理(Natural Language Processing)的目标是让机器"理解"和"生成"人类语言。核心任务包括:
| 任务 | 说明 | 示例 |
|---|---|---|
| 文本分类 | 给文本打标签 | 情感分析:这条评论是正面还是负面? |
| 命名实体识别 (NER) | 识别文本中的专有名词 | "乔布斯在加州创办了苹果" → 人名、地名、公司名 |
| 机器翻译 | 将一种语言翻译为另一种 | 法语 → 英语 |
| 问答系统 | 根据上下文回答问题 | 给定一段维基百科,回答"谁发明了电话?" |
| 文本生成 | 生成连贯的自然语言 | 自动写摘要、对话系统 |
| 序列标注 | 对每个词做标注 | 词性标注(名词、动词、形容词...) |
所有这些任务的第一个问题都是:如何让计算机表示一个"词"? 计算机只认数字,不认文字。
文本表示的演进
One-Hot Encoding(独热编码)
最朴素的想法:假设词典有 \(V\) 个词,那每个词就用一个 \(V\) 维的向量表示,只有对应位置是1,其余全是0。
致命问题:
- 维度灾难:如果词典有5万个词,每个词就是一个5万维的稀疏向量,计算极其低效
- 无法表达语义相似性:任意两个不同词的向量内积都是0——在这个表示下,"猫"和"狗"的相似度 = "猫"和"飞机"的相似度 = 0
词袋模型(Bag of Words)与 TF-IDF
词袋模型把一个句子/文档表示为词频向量:统计每个词出现了几次,完全忽略词序。
TF-IDF(词频-逆文档频率) 在此基础上加了一层权重:如果一个词在所有文档中都出现(如"的"、"是"),它的权重就低;如果只在少数文档中出现,权重就高。
- \(\text{TF}(t,d)\):词 \(t\) 在文档 \(d\) 中的出现频率
- \(\text{DF}(t)\):包含词 \(t\) 的文档数量
- \(N\):文档总数
局限性: "狗咬人"和"人咬狗"在词袋模型下完全相同——词序信息完全丢失。
词嵌入(Word Embeddings)
核心突破: 将每个词映射到一个低维稠密向量空间,使得语义相近的词在向量空间中距离也近。
什么是词向量?
词向量(Word Vector)就是一个词在低维嵌入空间中的坐标——一组具体的浮点数。例如一个4维(极度简化)的词向量可能长这样:
"猫"和"狗"的向量很接近(都是动物),而"汽车"和它们相距甚远。
稠密向量 vs 稀疏向量
One-Hot编码也是一种"向量",但它是高维稀疏的——5万维中只有1维有值。词嵌入是低维稠密的——每个维度都有非零值,都参与表达语义。
One-Hot(5万维): [0, 0, 0, ..., 1, ..., 0, 0, 0] ← 只有1维有用
├──────── 49999个0 ────────┤
Word2Vec(300维): [0.82, -0.31, 0.56, ..., 0.12] ← 每一维都有用
├────── 300个有意义的值 ──────┤
核心区别在于:One-Hot是一个符号标记("这是词典中第3724个词",不携带任何语义信息),而词向量是一个语义描述(每个维度都编码了某种隐含特征)。
每个维度编码了什么?
词向量的每个维度并非人为定义的,而是模型自动学出来的。但通过事后分析可以发现,某些维度确实隐约对应着可解释的语义方向:
| 维度方向(概念性) | 高值的词 | 低值的词 |
|---|---|---|
| "动物性" | 猫、狗、鸟 | 桌子、椅子、电脑 |
| "性别" | 女王、姐姐、她 | 国王、兄弟、他 |
| "大小" | 大象、建筑、海洋 | 蚂蚁、原子、细胞 |
| "情感极性" | 开心、美好、赞 | 悲伤、糟糕、差 |
需要强调:这些"语义方向"通常不与单个维度精确对齐,而是分布在多个维度的组合中。但这种分布式表示正是词向量强大的原因——它可以用有限的维度编码无穷多种语义关系。
常见词向量维度
| 模型/场景 | 典型维度 | 说明 |
|---|---|---|
| Word2Vec | 100 ~ 300 | Google预训练版本为300维 |
| GloVe | 50 / 100 / 200 /300 | 提供多种维度的预训练版本 |
| Transformer Base | 512 | "Attention is All You Need" 原论文 |
| GPT-2 | 768 ~ 1600 | Small到XL |
| GPT-3 | 12288 | 175B参数模型 |
| LLaMA-7B | 4096 | Meta开源模型 |
维度越高,表达能力越强,但计算成本也越高。早期的300维已经足够捕捉丰富的语义关系;到了大模型时代,更大的维度是为了支撑更复杂的上下文理解。
词向量间的相似度
既然词向量是空间中的坐标,我们就可以度量词与词之间的距离。最常用的是余弦相似度:
- 值域为 \([-1, 1]\),1表示方向完全相同,0表示正交无关,-1表示方向完全相反
- 只比较方向不比较长度,因此不受向量大小影响
实际的余弦相似度例子(基于预训练Word2Vec):
| 词对 | 余弦相似度 | 说明 |
|---|---|---|
| (猫, 狗) | ~0.76 | 同为宠物,高度相似 |
| (国王, 女王) | ~0.65 | 同为统治者,有关联 |
| (猫, 汽车) | ~0.05 | 几乎无关 |
| (好, 坏) | ~0.40 | 虽然语义相反,但经常出现在相似上下文中 |
注意最后一组:"好"和"坏"的相似度并不低!因为Word2Vec基于分布假说——"好"和"坏"常出现在相同的上下文中("这部电影很___"),所以它们的向量反而比较接近。这是词嵌入的一个著名局限:它捕捉的是共现关系而非语义对立。
分布假说(Distributional Hypothesis)
"You shall know a word by the company it keeps." — J.R. Firth, 1957
一个词的含义由它的上下文决定。"猫"和"狗"经常出现在相似的上下文中("喂养"、"在沙发上睡觉"),所以它们的向量应该相近。
Word2Vec (Mikolov et al., 2013)
两种训练模式:
CBOW (Continuous Bag of Words):用上下文预测中心词
给定窗口大小 \(c\) 内的上下文词,预测中间的词 \(w_t\)。
Skip-gram:用中心词预测上下文(实践中效果更好)
给定中心词 \(w_t\),预测周围窗口内每个位置的词。
训练过程本质上是一个浅层神经网络(只有一个隐藏层),训练完成后取隐藏层的权重矩阵作为词向量。
Word2Vec的经典发现——向量算术:
这说明词嵌入空间捕捉到了某种"语义方向",例如"性别方向"、"时态方向"等。
GloVe (Pennington et al., 2014)
GloVe (Global Vectors) 结合了全局统计信息和局部上下文。它直接对词-词共现矩阵进行分解:
其中 \(X_{ij}\) 是词 \(i\) 和词 \(j\) 在上下文窗口中的共现次数,\(f\) 是加权函数(防止高频词主导)。
词嵌入的局限性
静态嵌入问题:每个词只有一个固定向量,无法处理一词多义:
- "我吃了一个苹果" → 水果
- "我买了一部苹果手机" → 公司
在两个句子中,"苹果"的 Word2Vec/GloVe 向量完全相同。我们需要上下文相关的动态表示——这正是后来 ELMo、BERT、GPT 等模型要解决的问题。
序列模型
自然语言本质上是一个序列——词的顺序至关重要。为了处理这种序列结构,人们提出了循环神经网络系列。
RNN(循环神经网络)
核心思想:模型逐词阅读序列,每一步都维护一个"隐藏状态" \(h_t\),作为对之前所有信息的记忆。
x₁ → [RNN] → h₁
↓
x₂ → [RNN] → h₂
↓
x₃ → [RNN] → h₃
↓
...
数学公式:
- \(x_t\):第 \(t\) 步的输入(词嵌入向量)
- \(h_t\):第 \(t\) 步的隐藏状态
- \(W_{hh}, W_{xh}, W_{hy}\):权重矩阵
- 每一步的 \(h_t\) 都是 \(h_{t-1}\)(历史记忆)和 \(x_t\)(当前输入)的函数
致命问题——梯度消失与梯度爆炸:
反向传播需要沿时间展开(Backpropagation Through Time, BPTT),梯度会经过连乘:
- 如果 \(W_{hh}\) 的最大特征值 < 1:梯度指数衰减 → 梯度消失 → 模型学不到长距离依赖
- 如果 \(W_{hh}\) 的最大特征值 > 1:梯度指数增长 → 梯度爆炸 → 训练不稳定
实践中,梯度消失更常见。这意味着RNN在处理长句子时,前面的信息几乎无法影响后面的梯度更新。
具体示例:用RNN做情感分析
任务:判断电影评论 "这部 电影 太 好看 了" 是正面还是负面。
Step 1:词嵌入
每个词通过嵌入矩阵转为词向量(假设维度为4):
Step 2:RNN逐词处理
x₁"这部" → [RNN] → h₁ (知道了"这部",还不知道在说什么)
↓
x₂"电影" → [RNN] → h₂ (知道了"这部电影",开始有上下文)
↓
x₃"太" → [RNN] → h₃ (知道了程度词,在期待后面的情感词)
↓
x₄"好看" → [RNN] → h₄ (关键!正面情感信号进入隐藏状态)
↓
x₅"了" → [RNN] → h₅ (句末助词,h₅ 应该包含整句的信息)
每一步都执行 \(h_t = \tanh(W_{hh} h_{t-1} + W_{xh} x_t + b)\),隐藏状态像滚雪球一样逐步积累信息。
Step 3:分类
取最后一个隐藏状态 \(h_5\),通过一个全连接层 + Softmax输出类别概率:
如果 \(P(\text{正面}) = 0.92\),则模型判定为正面评论。
RNN做情感分析的问题: 如果评论很长——"虽然开头有点无聊,中间节奏也慢,但最后的反转真的太精彩了,这部电影整体来说非常好看"——等处理到"好看"时,"虽然"和"开头"的信息早已被挤出隐藏状态,模型可能无法正确理解"虽然...但..."这个转折结构。这就是梯度消失导致的长距离依赖问题。
LSTM(长短期记忆网络)
核心改进:引入门控机制(Gating Mechanism)和细胞状态(Cell State)\(C_t\),让信息可以"高速公路般"地直接传递,绕过非线性变换的挤压。
┌──────────────────────────────────────────────┐
│ Cell State Cₜ │
│ Cₜ₋₁ ──→ [×forget] ──→ [+input] ──→ Cₜ ──→│
└──────────────────────────────────────────────┘
↑ ↑ ↓
Forget Gate Input Gate Output Gate
fₜ iₜ oₜ
四个关键方程:
遗忘门(Forget Gate)——决定丢弃哪些旧信息:
输入门(Input Gate)——决定写入哪些新信息:
细胞状态更新——核心!加法结构使梯度可以直接流过:
输出门(Output Gate)——决定输出哪些信息:
其中 \(\sigma\) 是 Sigmoid 函数(输出0到1,控制"门"的开合度),\(\odot\) 是逐元素乘法。
为什么LSTM能缓解梯度消失?
关键在于 \(C_t = f_t \odot C_{t-1} + i_t \odot \tilde{C}_t\) 这个加法结构。梯度沿 \(C\) 传播时:
只要遗忘门 \(f_t\) 接近1(即网络学会"不要忘记"),梯度就能几乎无损地传递很远。相比RNN中梯度要反复乘以 \(W_{hh}\),LSTM的梯度通道要"宽敞"得多。
具体示例:用LSTM做命名实体识别(NER)
任务:在句子 "乔布斯 在 加州 创办 了 苹果 公司" 中识别出所有命名实体(人名、地名、组织名)。
NER是一个序列标注任务——对每个词输出一个标签,而不是对整个句子输出一个标签。常用的标签体系是BIO标注:
- B-PER:人名的开始(Begin-Person)
- I-PER:人名的内部(Inside-Person)
- B-LOC:地名的开始
- B-ORG:组织名的开始
- I-ORG:组织名的内部
- O:不属于任何实体(Outside)
Step 1:词嵌入
每个词转为词向量后输入LSTM。
Step 2:LSTM逐词处理,每一步都输出标签
与情感分析不同,NER需要每个位置都有输出,而不只是最后一步:
词: 乔布斯 在 加州 创办 了 苹果 公司
↓ ↓ ↓ ↓ ↓ ↓ ↓
[LSTM]→[LSTM]→[LSTM]→[LSTM]→[LSTM]→[LSTM]→[LSTM]
↓ ↓ ↓ ↓ ↓ ↓ ↓
标签: B-PER O B-LOC O O B-ORG I-ORG
Step 3:每个位置的分类
每一步的隐藏状态 \(h_t\) 都通过一个全连接层预测标签:
LSTM在NER中的关键优势:识别 "苹果 公司" 是组织名时,模型需要记住前面有 "创办" 这个动词——这暗示后面跟的是一个组织。如果是 "吃 了 苹果",同样的 "苹果" 就不是实体。LSTM的门控机制让它能保留这些远距离的上下文线索。
为什么RNN做NER会更差? 在长句中,比如 "据新华社今天上午的报道,著名企业家乔布斯在加利福尼亚州旧金山市创办了苹果公司"——"创办"和"苹果公司"之间隔了很多词,RNN的梯度消失会让模型难以学到"创办"对"苹果"标签的影响,而LSTM可以通过遗忘门保留这个信号。
实践中更好的方案:BiLSTM-CRF
单向LSTM只能看到前文。但NER中后文也很重要——看到"公司"才能确认"苹果"是组织名。因此实践中常用双向LSTM(BiLSTM):
前向: 乔布斯 → 在 → 加州 → 创办 → 了 → 苹果 → 公司
→ h⃗₆
后向: 乔布斯 ← 在 ← 加州 ← 创办 ← 了 ← 苹果 ← 公司
h⃖₆ ←
再加上CRF(条件随机场)层做全局标签解码,保证标签序列的合理性(比如 I-ORG 前面必须是 B-ORG 或 I-ORG,不能突然出现)。BiLSTM-CRF在Transformer出现之前是NER任务的标准方案。
GRU(门控循环单元)
GRU (Cho et al., 2014) 是LSTM的简化版本,将遗忘门和输入门合并为一个更新门,参数更少,训练更快:
更新门(Update Gate)——控制保留多少旧信息:
重置门(Reset Gate)——控制忽略多少旧信息:
候选隐藏状态:
最终隐藏状态——在旧状态和候选状态之间插值:
当 \(z_t \approx 0\) 时,信息直接复制(类似LSTM的遗忘门为1);当 \(z_t \approx 1\) 时,完全替换为新信息。
LSTM vs GRU:性能上通常非常接近,GRU参数更少适合小数据集,LSTM在非常长的序列上稍有优势。实践中两者差异不大。
Seq2Seq 架构与固定向量瓶颈
Seq2Seq (Sutskever et al., 2014)
机器翻译的核心范式:编码器-解码器(Encoder-Decoder) 架构。
Encoder Decoder
┌─────────────────────┐ ┌─────────────────────────┐
│ Bonjour → h₁ │ │ → I │
│ le → h₂ │ c │ I → am │
│ monde → h₃ ───────┼──→────│ am → happy │
│ │ │ happy → <EOS> │
└─────────────────────┘ └─────────────────────────┘
法语输入 上下文向量 英语输出
工作流程:
- 编码阶段:编码器(一个RNN/LSTM)逐词读入源语言句子,将最终的隐藏状态 \(h_T\) 作为上下文向量(Context Vector) \(c\)
- 解码阶段:解码器(另一个RNN/LSTM)以 \(c\) 作为初始隐藏状态,逐词生成目标语言句子
具体示例:用Seq2Seq做中译英(带详细数值)
任务:将 "我 喜欢 猫" 翻译为 "I like cats"。
为了把过程讲清楚,我们假设一个极度简化的设定:
- 中文词表:{我, 喜欢, 猫, ...} 共1000个词
- 英文词表:{I, like, cats, dog, the, am, happy, ...,
<BOS>,<EOS>} 共3万个词 - 词嵌入维度:\(d = 4\)(真实场景通常256~512)
- LSTM隐藏状态维度:\(d_h = 4\)(真实场景通常256~512)
第一阶段:编码(Encoding)——把中文句子压缩成一个向量
Encoder 就是前向传播。 它没有任何特殊的"压缩"或"理解"机制——就是把一个 LSTM 在输入序列上跑一遍前向传播(矩阵乘法 + 激活函数),每读入一个词就更新一次隐藏状态,最后一步的隐藏状态就是上下文向量。如果你理解了全连接网络的前向传播(\(y = \sigma(Wx + b)\)),那 Encoder 只是在此基础上加了"循环"和"门控"。
Step 1:词嵌入——把文字变成数字
每个中文词通过一个嵌入矩阵 \(E_{\text{zh}} \in \mathbb{R}^{1000 \times 4}\)(1000个词,每个词4维)查表得到词向量:
这些数值不是手动指定的,而是模型训练过程中自动学出来的。
Step 2:LSTM逐词处理——Encoder的核心就在这里
LSTM从左到右读入每个词向量,每一步更新隐藏状态 \(h_t\) 和细胞状态 \(C_t\)。下面我们完整展开 LSTM 内部的计算过程——每一步就是四次矩阵乘法加一些逐元素运算,没有任何更复杂的操作。
LSTM内部有四组可学习的参数(权重矩阵),分别对应四个"门":
- \(W_f \in \mathbb{R}^{4 \times 8}\),\(b_f \in \mathbb{R}^{4}\):遗忘门参数
- \(W_i \in \mathbb{R}^{4 \times 8}\),\(b_i \in \mathbb{R}^{4}\):输入门参数
- \(W_C \in \mathbb{R}^{4 \times 8}\),\(b_C \in \mathbb{R}^{4}\):候选记忆参数
- \(W_o \in \mathbb{R}^{4 \times 8}\),\(b_o \in \mathbb{R}^{4}\):输出门参数
为什么是 \(4 \times 8\)?因为每一步的输入是 \([h_{t-1};\ x_t]\)(把上一步的隐藏状态和当前词向量拼接起来),\(h\) 是4维 + \(x\) 是4维 = 8维。输出是4维(隐藏状态的维度)。
处理第1个词:"我"
初始状态:\(h_0 = [0, 0, 0, 0]\),\(C_0 = [0, 0, 0, 0]\)(全零)
(a) 拼接输入
就是把两个4维向量首尾相接,变成一个8维向量。
(b) 四次矩阵乘法——四个门各算一次
每个门做的事情完全一样:8维输入 × 权重矩阵 + 偏置 → 激活函数 → 4维输出。和普通全连接层 \(y = \sigma(Wx + b)\) 一模一样。
其中 \(\sigma\) 是 Sigmoid 函数(输出0~1,控制门的开合),\(\tanh\) 输出-1~1。
(c) 更新细胞状态——逐元素乘法和加法
展开计算(\(\odot\) 就是对应位置相乘):
因为 \(C_0\) 全是0(没有旧记忆可遗忘),所以遗忘门 \(f_1\) 在这一步实际上没有作用。细胞状态完全由输入门 \(i_1\) 和候选记忆 \(\tilde{C}_1\) 决定。
(d) 输出隐藏状态
\(h_1 = [0.09,\ -0.11,\ 0.04,\ 0.02]\) ← 这就是读完"我"之后的隐藏状态。
处理第2个词:"喜欢"
(a) 拼接:\([h_1;\ x_2] = [0.09,\ -0.11,\ 0.04,\ 0.02,\ 0.65,\ 0.33,\ -0.18,\ 0.51]\)
(b) 四次矩阵乘法——用同一组权重矩阵(\(W_f, W_i, W_C, W_o\) 在所有时间步共享):
(c) 更新细胞状态——现在遗忘门开始起作用了:
注意第三个维度:\(f_2[3] = 0.42\) 意味着遗忘门只保留了42%的旧信息(0.06 × 0.42 = 0.03),同时写入了新信息(-0.43)。这就是LSTM"选择性记忆"的过程。
(d) 输出隐藏状态:
处理第3个词:"猫"
完全相同的过程:拼接 \([h_2; x_3]\) → 四次矩阵乘法 → 更新 \(C_3\) → 输出 \(h_3\)。
Step 3:提取上下文向量
这就是上下文向量。整个句子"我喜欢猫"的全部信息都压缩在这4个数字里(真实场景是256~512个数字)。
回顾整个 Encoder 做了什么:
"我" → 查嵌入表 → x₁ ─┐
├→ [h₀; x₁] → Wf×+b→sigmoid → f₁ ─┐
│ Wi×+b→sigmoid → i₁ ├→ C₁, h₁
│ Wc×+b→tanh → C̃₁ │
│ Wo×+b→sigmoid → o₁ ─┘
│ ↓
"喜欢" → 查嵌入表 → x₂ ─┤→ [h₁; x₂] → 同样四次矩阵乘法 → C₂, h₂
│ ↓
"猫" → 查嵌入表 → x₃ ─┘→ [h₂; x₃] → 同样四次矩阵乘法 → C₃, h₃ = c
↑
上下文向量
就是这样。 三个词,每个词做四次矩阵乘法(\(W \times \text{input} + b\),然后过激活函数),一共12次矩阵乘法。所有时间步共享同一组权重。Encoder 没有任何超出前向传播的操作。
需要理解的关键点:\(c\) 不是"中文句子的某种编码",也不是"可以在英文中查找匹配的索引"。它是一个抽象的语义表示——一组数字,在训练过程中被调整到"能够让解码器从中提取出足够的信息来生成正确翻译"。它的每个维度没有人类可读的含义,但包含了生成所需的全部信号。
那"理解"从何而来? 不是来自 Encoder 的结构,而是来自训练。在反向传播中,梯度会告诉 \(W_f, W_i, W_C, W_o\) 这些权重矩阵应该怎么调整,使得 \(h_3\) 刚好包含解码器生成正确翻译所需的信息。训练几十万个句子对后,这些权重就被调到了"恰好能让前向传播产生有用的语义表示"的状态。
第二阶段:解码(Decoding)——从向量中逐词"生"出英文
解码器是另一个独立的LSTM(参数与编码器不共享)。它的工作方式不是"检索"或"匹配",而是逐步生成——每一步从3万个英文词中选出概率最高的那个。
解码器有两个关键组件:
- 解码器LSTM:维护解码过程中的隐藏状态
- 输出投影层:一个矩阵 \(W_{\text{vocab}} \in \mathbb{R}^{30000 \times 4}\),把4维隐藏状态映射到3万维(每个英文词一个维度)
Step 1:生成第一个英文词
输入: <BOS>(句首标记)的词向量: y₀ = Embed(<BOS>) = [0.01, 0.02, -0.01, 0.03]
初始: h₀ᵈᵉᶜ = c = [0.58, -0.07, 0.39, 0.44] ← 用上下文向量初始化!
(a) 解码器LSTM计算新的隐藏状态:
(b) 输出投影——将4维隐藏状态映射到3万个词上:
这一步做的是矩阵乘法。\(W_{\text{vocab}}\) 是一个 \(30000 \times 4\) 的矩阵,每一行对应一个英文词。矩阵乘法的本质是:计算当前隐藏状态与每个英文词的"相关度"分数。
(c) Softmax——将分数转为概率分布:
所有3万个概率加起来 = 1。"I"的概率最高(0.72),所以模型输出 "I"。
为什么 "I" 的概率最高?因为上下文向量 \(c\) 包含了"第一人称主语"的信号,在大量训练数据中模型学会了:当 \(c\) 中包含这种信号且处于句首位置时,"I" 是最可能的第一个词。
Step 2:生成第二个英文词
输入: "I" 的词向量: y₁ = Embed("I") = [0.55, 0.18, -0.42, 0.33]
状态: h₁ᵈᵉᶜ = [0.71, 0.15, -0.23, 0.52] ← 上一步的隐藏状态
(a) LSTM更新状态:
此时 \(h_2^{\text{dec}}\) 编码了两部分信息:原始的中文语义(继承自 \(c\))+ 已经生成了 "I"(来自 \(y_1\) 的输入)。
(b) 投影 + Softmax:
"like"概率最高(0.68),输出 "like"。注意 "love" 的概率也不低(0.15),因为"喜欢"也可以翻译为 "love"——模型在这里做出了一个概率选择。
Step 3:生成第三个英文词
输入: "like" 的词向量: y₂ = Embed("like")
状态: h₂ᵈᵉᶜ
"cats"概率最高,输出 "cats"。
Step 4:生成结束标记
模型输出 <EOS>,翻译结束。最终结果:"I like cats"。
解码过程的完整图示:
上下文向量 c ──→ 初始化解码器
│
↓
<BOS> ─→ Embed ─→ [LSTM] ─→ h₁ ─→ W_vocab × h₁ ─→ softmax ─→ [0.72, 0.03, ...] ─→ "I"
↑ 3万个词的概率 选最大
↓
"I" ──→ Embed ──→ [LSTM] ─→ h₂ ─→ W_vocab × h₂ ─→ softmax ─→ [0.01, 0.68, ...] ─→ "like"
选最大
↓
"like" → Embed ──→ [LSTM] ─→ h₃ ─→ W_vocab × h₃ ─→ softmax ─→ [..., 0.61, ...] ─→ "cats"
选最大
↓
"cats" → Embed ──→ [LSTM] ─→ h₄ ─→ W_vocab × h₄ ─→ softmax ─→ [..., 0.91, ...] ─→ <EOS>
停止
一个常见的误解需要澄清: 解码器不是在"查找"或"匹配"——它不会在英文词表中找一个和 \(c\) 最像的词。它做的是条件生成:在给定 \(c\)(原文含义)和已生成词的条件下,通过 \(W_{\text{vocab}}\) 这个学出来的映射,计算下一个词的概率分布。\(W_{\text{vocab}}\) 的每一行可以理解为"某个英文词需要什么样的隐藏状态才会被选中",而LSTM则负责在每一步产生合适的隐藏状态。整个过程是一个条件语言模型:
训练 vs 推理的区别
训练时使用 Teacher Forcing:不管模型自己预测了什么,每一步都把真实的目标词喂给下一步。这样做可以加速收敛,防止早期错误的预测雪球式传播。
训练时: <BOS> → [LSTM] → 预测"I" ✓
"I" → [LSTM] → 预测"like" ✓ ← 喂入的是真实标签"I",不是模型的预测
"like"→ [LSTM] → 预测"dogs" ✗ ← 即使预测错了
"cats"→ [LSTM] → 预测"<EOS>" ← 仍然喂入真实标签"cats"继续训练
推理时: <BOS> → [LSTM] → 预测"I"
"I" → [LSTM] → 预测"like" ← 喂入的是上一步自己的预测
"like"→ [LSTM] → 预测"cats"
"cats"→ [LSTM] → 预测"<EOS>" ← 如果中途预测错了,后面会一错到底
这也暴露了Seq2Seq的另一个问题:训练和推理的输入分布不一致(Exposure Bias)。训练时解码器总是看到正确答案,推理时却要依赖自己可能出错的预测。
固定向量瓶颈(Information Bottleneck)
核心问题:不管源语言句子有多长(10个词还是100个词),所有信息都被压缩到一个固定长度的向量 \(c\) 中。
这就像要求你读完一整本书后,只能写一句话来概括,然后另一个人要根据这句话把整本书翻译出来。
实验证据:Cho et al. (2014) 发现,当句子长度超过20-30个词时,Seq2Seq的BLEU分数急剧下降。
这个瓶颈直接催生了注意力机制(Attention Mechanism)——让解码器不再只依赖一个压缩向量,而是可以在生成每个词时"回头看"编码器的所有隐藏状态。
传统NLP的局限性总结
| 方法 | 核心局限 | 后续解决方案 |
|---|---|---|
| One-Hot / 词袋 / TF-IDF | 无语义信息、丢失词序 | Word2Vec / GloVe |
| Word2Vec / GloVe | 静态嵌入,一词多义无法处理 | ELMo / BERT(上下文嵌入) |
| RNN | 梯度消失,长距离依赖困难 | LSTM / GRU |
| LSTM / GRU | 仍然是顺序处理,无法并行;极长序列仍有困难 | Attention Mechanism |
| Seq2Seq | 固定向量瓶颈 | Attention |
| RNN + Attention | 仍然无法并行训练 | Transformer(完全基于Attention) |
Transformer的诞生逻辑:既然Attention已经成为最重要的组件,RNN反而成了并行化的瓶颈,那为什么不完全抛弃RNN,只用Attention?这就是2017年那篇 "Attention is All You Need" 的核心思想。
下一步:→ 注意力机制(详解 Attention 的数学原理)