🔄 RNN 是如何训练的
1️⃣ 假设任务
任务:根据前面的词预测下一个词。
数据:句子 “I like AI”
目标:输入前 2 个词 → 预测第三个词 “AI”。
2️⃣ 前向传播(Forward)
-
句子词向量化(embedding):
- “I” → [1, 0, 0]
- “like” → [0, 1, 0]
- “AI” → [0, 0, 1]
第 1 步(输入 “I”)
h1=f(Whh0+Wxx1+b) h_1 = f(W_h h_0 + W_x x_1 + b) h1=f(Whh0+Wxx1+b)
输出隐藏状态 h1h_1h1。
第 2 步(输入 “like”)
h2=f(Whh1+Wxx2+b) h_2 = f(W_h h_1 + W_x x_2 + b) h2=f(Whh1+Wxx2+b)
第 3 步(输入 “AI”)
h3=f(Whh2+Wxx3+b) h_3 = f(W_h h_2 + W_x x_3 + b) h3=f(Whh2+Wxx3+b)
最后输出:
y3=softmax(Wyh2) y_3 = \text{softmax}(W_y h_2) y3=softmax(Wyh2)
👉 预测第三个词的概率分布。
3️⃣ 损失函数
假设模型预测结果是:
- “AI” 的概率 = 0.6
- “like” 的概率 = 0.3
- “I” 的概率 = 0.1
真实标签是 “AI”,所以损失函数 = 交叉熵:
L=−log(0.6) L = -\log(0.6) L=−log(0.6)
4️⃣ 反向传播(BPTT: Backpropagation Through Time)
这是 RNN 最特别的地方。
- 普通神经网络只反向传播一层层参数。
- RNN 有“时间展开”的结构,误差要沿着时间序列反传。
举例:
- 损失在第 3 步产生(预测错了)。
- 误差不仅影响第 3 步的参数,还要往回传到第 2 步、第 1 步,因为它们的隐藏状态影响了后面的预测。
数学上:
∂L∂Wh=∑t∂L∂ht⋅∂ht∂Wh \frac{\partial L}{\partial W_h} = \sum_t \frac{\partial L}{\partial h_t} \cdot \frac{\partial h_t}{\partial W_h} ∂Wh∂L=t∑∂ht∂L⋅∂Wh∂ht
👉 也就是说,参数 WhW_hWh 的更新要考虑所有时间步。
5️⃣ 参数更新
使用梯度下降 / Adam 等优化器:
Wh=Wh−η⋅∂L∂Wh W_h = W_h - \eta \cdot \frac{\partial L}{\partial W_h} Wh=Wh−η⋅∂Wh∂L
Wx=Wx−η⋅∂L∂Wx W_x = W_x - \eta \cdot \frac{\partial L}{\partial W_x} Wx=Wx−η⋅∂Wx∂L
Wy=Wy−η⋅∂L∂Wy W_y = W_y - \eta \cdot \frac{\partial L}{\partial W_y} Wy=Wy−η⋅∂Wy∂L
其中 η\etaη 是学习率。
6️⃣ 小结(举例解释)
比如:
- 第一次训练时,模型预测 “like” 的概率最高(错了)。
- 反向传播会调整参数,让 “AI” 的概率升高,下次预测更准。
- 训练很多次后,模型学会“当我看到 ‘I like’,下一个词更可能是 ‘AI’”。
✅ 一句话总结:
RNN 训练的流程就是:
- 把序列展开成一连串的时间步;
- 前向传播得到输出;
- 计算损失(预测和真实差别);
- 反向传播误差,跨时间更新参数(BPTT);
- 迭代很多次,模型逐渐学会在序列上下文中预测。
2 啥是隐藏状态 为啥要有隐藏状态
1️⃣ 隐藏状态是什么?
简单说:
隐藏状态就是 RNN 的“记忆”,用来存储到当前时刻为止的上下文信息。
公式回顾:
ht=f(Whht−1+Wxxt+b) h_t = f(W_h h_{t-1} + W_x x_t + b) ht=f(Whht−1+Wxxt+b)
- xtx_txt:当前输入(比如“今天”这个词的向量)
- ht−1h_{t-1}ht−1:上一个时刻的隐藏状态(存的“昨天”的信息)
- hth_tht:新的隐藏状态(融合了“昨天 + 今天”的信息)
2️⃣ 为什么需要隐藏状态?
如果没有隐藏状态,神经网络每次只看到一个输入:
- 输入 “今天” → 模型只知道今天,没有上下文
- 输入 “下雨” → 模型只知道下雨,不知道时间背景
👉 就像失忆症:模型每次只看一个词,没法记住前文。
有了隐藏状态,模型可以:
- 在看到 “今天” 时,记住“时间是今天”;
- 在看到 “下雨” 时,把“今天 + 下雨”都存起来;
- 在预测 “要带什么” 时,利用前面存下来的“上下文”。
3️⃣ 举个例子
句子:
“我在北京出生,所以我最喜欢的城市是 ___”
-
没有隐藏状态:
模型看到“最喜欢的城市是”,预测可能是“上海”“深圳”,它不知道“北京”出现过。 -
有隐藏状态:
- 当看到“北京”时,隐藏状态 hth_tht 把“北京”存进去;
- 当读到最后时,隐藏状态里还有“北京”的信息;
- 于是模型可以预测“北京”。
👉 隐藏状态就是模型的“记忆仓库”。
4️⃣ 为什么叫“隐藏”?
因为它不是直接给我们看的 输出结果,而是模型在内部维护的一组数字。
- 对人来说是“黑箱子”;
- 对模型来说是“思维过程”。
比如:
- 输入词向量维度可能是 300
- 隐藏状态可以是 128 维的向量
- 每一维都代表“记住了一点上下文信息”,但人类很难直接解读。
3 RNN 理解的难点 & 常见易错点
🔄 RNN 理解的难点
1. 隐藏状态的本质
- 很多人觉得 隐藏状态 hth_tht 是某种“确定的知识点”,但其实它就是一个 向量(很多数字),存储了到目前为止的上下文信息。
- 每一维没有明确的语义,比如第 27 维不一定就是“天气”,它是模型自己学出来的。
2. 时间展开 (Unrolling)
-
RNN 在训练时会 展开成一个多层网络,每个时间步都是一层。
-
很多人没搞清楚:
- RNN 的参数在所有时间步是共享的,不是每一步都不同。
- 训练时,反向传播要跨越多个时间步 (BPTT: Backpropagation Through Time)。
3. 长依赖问题
-
RNN 容易 遗忘很久以前的信息,这是它的核心弱点。
-
难点在于理解为什么:
- 因为反向传播时,梯度会指数级衰减(梯度消失)或爆炸。
-
所以 RNN 记短语还行,记长句就力不从心。
4. 输入与输出的多种形式
RNN 有不同的结构,很多人容易混淆:
- 一对一:输入一句话 → 输出一个分类(情感分析)
- 多对一:输入一段语音 → 输出一句话(语音识别)
- 多对多:输入英文句子 → 输出中文句子(机器翻译)
👉 很多人一开始没分清楚任务形式,导致理解错误。
5. 训练中的梯度传播
- RNN 的训练不是只在当前时间步更新参数,而是要把误差 往回传。
- 很多人以为:“我预测最后一个词错了,只会更新最后一步的参数”。
- 其实:最后一步的误差会沿时间反传,影响所有前面的隐藏状态和参数。
⚠️ RNN 学习常见易错点
-
以为隐藏状态是固定知识
- ❌ 错误理解:h 就是“记住北京”这种概念。
- ✅ 正确理解:h 是一堆数字,经过训练,模型会把这些数字和“北京”对应上。
-
没理解参数共享
- ❌ 错误理解:每个时间步用不同的权重。
- ✅ 正确理解:所有时间步共享同一组参数(保证模型能泛化到任意长度序列)。
-
把 RNN 当成黑箱子,不知道怎么训练
- ❌ 错误理解:RNN 训练和 CNN/MLP 一样简单。
- ✅ 正确理解:RNN 的训练更难,需要 BPTT,容易梯度消失/爆炸,所以后来才有 LSTM/GRU 改进。
-
混淆输入序列和输出序列
- 有些任务输入和输出长度不一样(比如翻译),很多人以为 RNN 只能一对一。
-
没理解长依赖问题
- ❌ 错误理解:RNN 可以无限记忆。
- ✅ 正确理解:RNN 在理论上可以,但实际上梯度问题让它记不了太长。
📌 总结
RNN 的难点和易错点主要集中在:
- 隐藏状态到底是啥
- 训练时时间展开 + 参数共享
- 长依赖问题(为什么记不住很久以前的内容)
- 任务形式的不同(多对一、多对多等)
4 展开 和 参数共享 两个核心点
1. 为什么要“展开”?
RNN 本身看起来像是一个循环:
ht=f(Wxxt+Whht−1+b) h_t = f(W_x x_t + W_h h_{t-1} + b) ht=f(Wxxt+Whht−1+b)
这里:
- xtx_txt:第 t 个输入
- ht−1h_{t-1}ht−1:上一步的隐藏状态
- Wx,WhW_x, W_hWx,Wh:权重矩阵(参数,训练时要更新)
- hth_tht:当前隐藏状态
这个循环很难直接训练,所以我们要 把时间轴展开,比如输入一个长度为 3 的序列:
x1 → [RNN] → h1
x2 → [RNN] → h2
x3 → [RNN] → h3
训练时,把它画成 3 层网络:
x1 → (RNN单元, 参数 W) → h1
x2 → (RNN单元, 参数 W) → h2
x3 → (RNN单元, 参数 W) → h3
注意:虽然看起来像 3 层,但它们的参数 其实是同一份 W,只是数据不同。
2. 参数共享到底是啥?
很多人误解 RNN 以为每个时间步都有独立的参数:
-
❌ 错误理解:
W1 用于 h1 W2 用于 h2 W3 用于 h3 -
✅ 正确理解:
所有时间步都用同一套参数 W
这样做的原因是:
- 让模型能处理任意长度的序列(句子有 5 个词还是 100 个词都可以)。
- 保证在时间上保持一致性。
所以展开成 3 层只是 计算图上的复制,而不是参数上的复制。
3. BPTT:反向传播穿越时间
普通神经网络的反向传播,只要把误差从最后一层传回去就行。
但在 RNN 里,最后的输出不仅依赖最后的输入,还依赖前面所有隐藏状态。
举个例子:
序列:x1, x2, x3
输出:y3
如果 y3 错了,反向传播的时候:
- 不仅要调整 W 对应 x3 的那次计算,
- 还要往前传到 h2、h1,因为 h3 依赖 h2,h2 依赖 h1。
所以训练过程叫 Backpropagation Through Time (BPTT):
- 就像把误差“倒流”回整个时间序列。
4. 一个具体例子
假设我们用 RNN 来预测句子最后一个词。输入 “我 爱 北”,目标输出 “京”。
-
正向传播:
- x1=“我” → h1
- x2=“爱” → h2
- x3=“北” → h3 → y3=预测
-
输出 y3 错了(比如预测成“海”),计算误差。
-
反向传播:
- 调整 h3 相关的参数(因为 y3 出错)
- 误差往回传 → h2
- 误差再传 → h1
- 所有用到的参数 W_x, W_h 都会被更新。
✅ 所以总结:
- RNN 展开成多层只是训练时的计算方式。
- 每层不是独立的,参数在时间步之间共享。
- BPTT 就是让误差沿着时间展开图往回传,从而更新所有时间步的参数。
看看案列
📝 输入序列
句子切成词或字(这里用字来举例):
[今][天][天][气][真][好][啊][,][我][喜][欢][太][阳]
假设我们的任务是:预测下一个字。
👉 输入“今”,目标是“天”;输入“天”,目标是“天”;直到输入“太”,目标是“阳”。
🔄 训练过程(逐步展开)
1. 嵌入层 (Embedding)
每个字会被转成一个向量。
比如 “今” → [0.2, 0.5, -0.1, …] (128维向量)。
2. RNN 正向传播
以 前 3 个字为例:
-
t=1 输入 “今”
h1 = f(Wx * x1 + Wh * h0 + b)得到隐藏状态 h1。
-
t=2 输入 “天”
h2 = f(Wx * x2 + Wh * h1 + b)h2 记住了 “今+天”的信息。
-
t=3 输入 “天”
h3 = f(Wx * x3 + Wh * h2 + b)h3 记住了 “今+天+天”的上下文。
依次类推直到 “阳”。
3. 输出层
在每个时间步,都会基于隐藏状态 h_t 输出一个预测:
y_t = softmax(Wy * h_t + c)
比如:
- 输入 “今” → 输出 “天”(预测下一个字)
- 输入 “天” → 输出 “天”
- 输入 “气” → 输出 “真”
- 输入 “喜” → 输出 “欢”
- 输入 “太” → 输出 “阳”
4. 计算损失 (Loss)
每个时间步都有一个预测 y_t,和真实下一个字 y*_t。
损失函数一般用交叉熵:
Loss = Σ cross_entropy(y_t, y*_t)
5. 反向传播 (BPTT)
误差会“穿越时间”传回去:
- 预测 “阳” 错了,更新 h12 → h11 → h10 … → h1
- 同时更新共享参数 (Wx, Wh, Wy, b, c)。
这就是 Backpropagation Through Time。
🚀 一个小例子(简化)
输入:“我 喜 欢 太 阳”
目标:“喜 欢 太 阳 ☀”
| 时间步 | 输入字 | 隐藏状态 h | 输出预测 | 目标字 | Loss |
|---|---|---|---|---|---|
| t=1 | 我 | h1 | 喜 | 喜 | ✅ 0 |
| t=2 | 喜 | h2 | 欢 | 欢 | ✅ 0 |
| t=3 | 欢 | h3 | 太 | 太 | ✅ 0 |
| t=4 | 太 | h4 | 海 ❌ | 阳 | ❌ 1 |
| t=5 | 阳 | h5 | ☀️ | ☀️ | ✅ 0 |
总 Loss = 1。
反向传播时,会把 t=4 的错误梯度传回 h4、h3、h2、h1,更新所有参数。
✅ 总结:
- 输入字 → 向量化 → RNN 逐步处理 → 输出预测
- 每个时间步的输出和真实下一个字对比,计算损失
- 误差沿着时间展开的计算图反传 (BPTT),更新共享参数
📖 RNN 关键专业术语 & 通俗解释
1. 输入向量 (Input vector, xtx_txt)
- 专业解释:序列在第 t 个时间步的输入,可以是词向量、像素、特征等。
- 通俗解释:比如一句话“我爱北京”,在 t=1 时,输入就是“我”的数字表示。
2. 隐藏状态 (Hidden state, hth_tht)
- 专业解释:RNN 在每个时间步保存的“记忆向量”,通过递归方式传递。
- 通俗解释:就像大脑的短期记忆,看了“我”,再看到“爱”,脑子里记的就是“我+爱”。
3. 参数共享 (Parameter sharing)
- 专业解释:RNN 在不同时间步使用相同的权重矩阵 Wx,WhW_x, W_hWx,Wh。
- 通俗解释:就像你用同一本字典去翻译一本书的每一页,而不是每一页换一本字典。
4. 时间展开 (Unrolling)
- 专业解释:把 RNN 在时间维度的循环展开成一条链式结构,便于计算和训练。
- 通俗解释:原本 RNN 像一只“走圈圈的时钟”,展开后就像“把指针走过的轨迹画成直线”,方便看清楚。
5. 输出 (Output, yty_tyt)
- 专业解释:在每个时间步,基于隐藏状态预测出的结果,比如下一个字。
- 通俗解释:看到“我爱”,你大概率会说“北京”;RNN 的输出就是这种预测。
6. 损失函数 (Loss function)
- 专业解释:衡量预测结果和真实结果之间差距的函数。
- 通俗解释:就像考试成绩,分数差距越大,说明学得越差。
7. 反向传播 (Backpropagation)
- 专业解释:通过链式法则,把误差从输出层往输入层逐层传回去,更新参数。
- 通俗解释:老师告诉你“最后一步写错了”,你就会反思“是不是前面公式写错了”,一步步找原因。
8. 时间反向传播 (BPTT, Backpropagation Through Time)
- 专业解释:把普通的反向传播扩展到时间维度,误差会沿着时间步往回传递。
- 通俗解释:如果最后一句话答错了,老师会追溯整个推理过程,甚至回到第一步,看看从哪开始偏的。
9. 梯度消失 / 梯度爆炸 (Gradient vanishing / exploding)
- 专业解释:在长序列中,误差信号会因为链式相乘变得极小(消失)或极大(爆炸)。
- 通俗解释:就像一串电话传话,传到第 100 个人时,要么声音小得听不见(消失),要么吼得太大声全乱套(爆炸)。
10. 长依赖问题 (Long-term dependency problem)
- 专业解释:RNN 很难捕捉序列中相隔很远的依赖关系。
- 通俗解释:比如听了“我昨天在北京”,过了很久才听到“吃了烤鸭”,普通 RNN 已经忘记“北京”了。
🎯 总结(精简版对照表)
| 专业术语 | 通俗解释 |
|---|---|
| 输入向量 | 当前看到的字/词的数字表示 |
| 隐藏状态 | 大脑的短期记忆 |
| 参数共享 | 所有时间步用同一本字典 |
| 时间展开 | 把循环拉直成一条链 |
| 输出 | 模型预测的下一个字 |
| 损失函数 | 考试分数差距 |
| 反向传播 | 错题往前查原因 |
| BPTT | 错题往前查到整个时间过程 |
| 梯度消失/爆炸 | 传话时声音越来越小/越来越大 |
| 长依赖问题 | 前面很久的信息记不住 |
1047

被折叠的 条评论
为什么被折叠?



