RNN 代码 - 修改隐藏层及总结
1 课程概览
本课演示如何修改 RNN 模型的隐藏层层数,并总结 RNN 参数变化规律。将 num_layers 从 1 改为 2,对应的 h0 和 hn 形状发生变化。讲解两层隐藏层的内部结构,以及实际开发中通常不使用多层隐藏层的原因。
2 核心概念与定义
- 隐藏层层数(num_layers):RNN 的隐藏层层数,默认为 1。
- 多层 RNN:多个隐藏层堆叠,前一层的输出作为后一层的输入。
- 过渡层:多层 RNN 中,中间层只起过渡作用,不直接输出结果。
3 模型与算法详解
修改隐藏层层数的流程
- 修改
num_layers:从 1 改为 2 - 修改
h0形状:(1, 3, 6)→(2, 3, 6) - 查看输出形状:
output形状不变,hn形状变化
参数变化对比
| 参数 | 修改前 | 修改后 |
|---|---|---|
| num_layers | 1 | 2 |
| input 形状 | (1, 3, 5) | (1, 3, 5) |
| h0 形状 | (1, 3, 6) | (2, 3, 6) |
| output 形状 | (1, 3, 6) | (1, 3, 6) |
| hn 形状 | (1, 3, 6) | (2, 3, 6) |
两层隐藏层的内部结构
单层 RNN:
时间步1 → [隐藏层1] → 输出1
时间步2 → [隐藏层1] → 输出2
时间步3 → [隐藏层1] → 输出3
两层 RNN:
时间步1 → [隐藏层1] → [隐藏层2] → 输出1
时间步2 → [隐藏层1] → [隐藏层2] → 输出2
时间步3 → [隐藏层1] → [隐藏层2] → 输出3
- 隐藏层1:过渡层,不直接输出
- 隐藏层2:最终层,输出结果
hn 与 output 的关系
hn[-1](最后一层)等于output[-1](最后一个时间步)hn[0](第一层)是过渡层的结果
实际开发建议
实际开发中,几乎不会使用两个隐藏层,通常一步到位。
4 数学原理与推导
单层 RNN 公式
$$H_t^{(1)} = \tanh(W_{ih}^{(1)} X_t + W_{hh}^{(1)} H_{t-1}^{(1)} + b^{(1)})$$
两层 RNN 公式
$$H_t^{(1)} = \tanh(W_{ih}^{(1)} X_t + W_{hh}^{(1)} H_{t-1}^{(1)} + b^{(1)})$$
$$H_t^{(2)} = \tanh(W_{ih}^{(2)} H_t^{(1)} + W_{hh}^{(2)} H_{t-1}^{(2)} + b^{(2)})$$
$$Y_t = W_{ho} H_t^{(2)} + b_o$$
5 代码示例
import torch
import torch.nn as nn
# 1. 定义 RNN 模型(修改 num_layers)
# 修改前:num_layers=1
# 修改后:num_layers=2
rnn = nn.RNN(input_size=5, hidden_size=6, num_layers=2)
# 2. 创建输入数据(形状不变)
input = torch.randn(1, 3, 5) # seq_len=1, batch=3, input_size=5
# 3. 初始化隐藏状态(形状变化)
# 修改前:h0 = torch.zeros(1, 3, 6)
# 修改后:h0 = torch.zeros(2, 3, 6)
h0 = torch.zeros(2, 3, 6) # num_layers=2, batch=3, hidden_size=6
# 4. 前向传播
output, hn = rnn(input, h0)
# 5. 查看输出形状
print("output shape:", output.shape) # torch.Size([1, 3, 6])
print("hn shape:", hn.shape) # torch.Size([2, 3, 6])
# 6. 验证 hn[-1] 等于 output[-1]
print("hn[-1]:", hn[-1])
print("output[-1]:", output[-1])
# 两者内容相同
输出示例
output shape: torch.Size([1, 3, 6])
hn shape: torch.Size([2, 3, 6])
6 重难点与易错提醒
- ❗重点:修改
num_layers时,h0和hn的第一个维度要同步修改。 - ⚠️易错:复制粘贴代码时容易漏改
h0的形状。 - ⚠️易错:
num_layers=1时默认不显示层数,num_layers=2时会显示。 - 💡深入理解:两层 RNN 中,隐藏层1 是过渡层,隐藏层2 是最终输出层。
- 💡深入理解:
hn[-1]等于output[-1],即最后一层的最后一个时间步。 - 💡深入理解:实际开发中通常不使用多层隐藏层,一步到位。
7 课堂问答精选
Q1:修改隐藏层层数时,哪些形状需要同步修改?
A:h0 和 hn 的第一个维度(num_layers)需要同步修改。input 和 output 形状不变。
Q2:两层 RNN 的内部结构是什么?
A:输入先经过隐藏层1(过渡层),再经过隐藏层2(最终层),最后输出。隐藏层1 不直接输出,只起过渡作用。
Q3:hn 和 output 的关系是什么?
A:hn[-1](最后一层)等于 output[-1](最后一个时间步)。hn[0](第一层)是过渡层的结果。
Q4:实际开发中是否使用多层隐藏层?
A:通常不使用。实际开发中一般一步到位,使用单层隐藏层即可。
8 本课小结
- 修改
num_layers:h0和hn的第一个维度同步修改。 - 两层 RNN:隐藏层1(过渡)→ 隐藏层2(输出)。
hn[-1]等于output[-1]。num_layers=1默认不显示,num_layers=2显示。- 实际开发:通常使用单层隐藏层。
9 延伸思考
- 多层 RNN 相比单层 RNN 有什么优势?
- 为什么实际开发中通常不使用多层隐藏层?