RNN的理解
对于RNN的理解
import torch
import torch.nn as nn
import torch.nn.functional as F# 手动实现一个简单的RNN
class RNN(nn.Module):def __init__(self, input_size, hidden_size, output_size):super(RNN, self).__init__()# 定义权重矩阵和偏置项self.hidden_size = hidden_sizeself.W_xh = nn.Parameter(torch.randn(input_size, hidden_size)) # 输入到隐藏层的权重#
#注:
input_size = 4
hidden_size = 3
W_xh = torch.randn(input_size, hidden_size)
生成的 W_xh 会是一个形状为 (4, 3) 的张量,可能是这样的(数字是随机生成的):
tensor([[ 0.2973, -1.1254, 0.7172],
[ 0.0983, 0.2856, -0.4586],
[-0.0105, 0.2317, 0.2716],
[ 1.0431, -1.3894, -0.1525]])
这个张量有 4 行 3 列。
self.W_hh = nn.Parameter(torch.randn(hidden_size, hidden_size)) # 隐藏层到隐藏层的权重self.b_h = nn.Parameter(torch.zeros(hidden_size)) # 隐藏层偏置self.W_hy = nn.Parameter(torch.randn(hidden_size, output_size)) # 隐藏层到输出层的权重self.b_y = nn.Parameter(torch.zeros(output_size)) # 输出层偏置def forward(self, x):# 初始化隐藏状态为0h_t = torch.zeros(x.size(0), self.hidden_size) # 初始隐藏状态 [[5]]
注:
x 是输入数据,形状是 (3, 5, 4),其中:
3 是批量大小(batch_size),即我们一次性输入网络的样本数是 3。
5 是序列长度(seq_len),每个样本有 5 个时间步。
4 是每个时间步的输入特征数量。
self.hidden_size 假设是 6,表示隐藏层的维度是 6。
x.size(0) 获取输入张量 x 的第一个维度的大小,也就是批量大小 3。
torch.zeros(3, 6) 会创建一个形状为 (3, 6) 的张量,表示有 3 个样本,每个样本有 6 个隐藏状态神经元(即隐状态的维度是 6)。所有的元素都初始化为 0。
# 遍历时间步,逐个处理输入序列for t in range(x.size(1)): # x.size(1) 是序列长度x_t = x[:, t, :] # 获取当前时间步的输入 (batch_size, input_size)
`
注:x = torch.tensor([[[0.1, 0.2, 0.3, 0.4], # 第 0 时间步的输入 (第一个样本)
[0.5, 0.6, 0.7, 0.8], # 第 1 时间步的输入 (第一个样本)
[0.9, 1.0, 1.1, 1.2]], # 第 2 时间步的输入 (第一个样本)
[[1.3, 1.4, 1.5, 1.6], # 第 0 时间步的输入 (第二个样本)
[1.7, 1.8, 1.9, 2.0], # 第 1 时间步的输入 (第二个样本)
[2.1, 2.2, 2.3, 2.4]]]) # 第 2 时间步的输入 (第二个样本)
第一次循环 t=0:
x_t = x[:, 0, :]
x[:, 0, :] 会提取出所有样本在第 0 时间步的输入:
第一个样本在第 0 时间步的输入是 [0.1, 0.2, 0.3, 0.4]。
第二个样本在第 0 时间步的输入是 [1.3, 1.4, 1.5, 1.6]。
因此,x_t 的值是:
tensor([[0.1, 0.2, 0.3, 0.4],
[1.3, 1.4, 1.5, 1.6]])
# 更新隐藏状态:h_t = tanh(W_xh * x_t + W_hh * h_t + b_h)h_t = torch.tanh(x_t @ self.W_xh + h_t @ self.W_hh + self.b_h) # [[4]]
`
注:1. 计算 x_t @ W_xh
x_t @ W_xh 是输入 x_t 和权重矩阵 W_xh 的矩阵乘法。我们有 2 个样本,每个样本有 3 个输入特征,权重矩阵 W_xh 的形状是 (3, 4),所以乘法的结果是一个形状为 (2, 4) 的张量,即每个样本的隐藏状态更新的部分。
对于第一个样本:
[0.5, 0.6, 0.7] @ [[0.1, 0.2, -0.1, 0.4],
[0.3, 0.5, 0.2, -0.2],
[0.7, -0.1, 0.3, 0.5]]
我们可以计算它的结果:
= [0.5 * 0.1 + 0.6 * 0.3 + 0.7 * 0.7,
0.5 * 0.2 + 0.6 * 0.5 + 0.7 * (-0.1),
0.5 * -0.1 + 0.6 * 0.2 + 0.7 * 0.3,
0.5 * 0.4 + 0.6 * (-0.2) + 0.7 * 0.5]
= [0.05 + 0.18 + 0.49,
0.1 + 0.3 - 0.07,
-0.05 + 0.12 + 0.21,
0.2 - 0.12 + 0.35]
= [0.72, 0.33, 0.28, 0.43]
对于第二个样本:
[1.0, 1.2, 1.3] @ [[0.1, 0.2, -0.1, 0.4],
[0.3, 0.5, 0.2, -0.2],
[0.7, -0.1, 0.3, 0.5]]
计算结果:
= [1.0 * 0.1 + 1.2 * 0.3 + 1.3 * 0.7,
1.0 * 0.2 + 1.2 * 0.5 + 1.3 * (-0.1),
1.0 * -0.1 + 1.2 * 0.2 + 1.3 * 0.3,
1.0 * 0.4 + 1.2 * (-0.2) + 1.3 * 0.5]
= [0.1 + 0.36 + 0.91,
0.2 + 0.6 - 0.13,
-0.1 + 0.24 + 0.39,
0.4 - 0.24 + 0.65]
= [1.37, 0.67, 0.53, 0.81]
因此,x_t @ W_xh 的结果是:
tensor([[0.72, 0.33, 0.28, 0.43],
[1.37, 0.67, 0.53, 0.81]])
x_t @ self.W_xh:
x_t 是当前时间步的输入,形状是 (batch_size, input_size)。
self.W_xh 是输入到隐藏层的权重矩阵,形状是 (input_size, hidden_size)。
h_t @ self.W_hh:
h_t 是前一时间步的隐藏状态,形状是 (batch_size, hidden_size)。
self.W_hh 是隐藏层到隐藏层的权重矩阵,形状是 (hidden_size, hidden_size)。
# 最后一个时间步的隐藏状态通过全连接层得到输出y_t = h_t @ self.W_hy + self.b_y # 输出层return y_t
超参数设置
input_size = 10 # 输入特征维度
hidden_size = 20 # 隐藏层维度
output_size = 5 # 输出类别数
seq_length = 5 # 序列长度
batch_size = 3 # 批量大小
实例化模型
model = RNN(input_size, hidden_size, output_size)
打印模型结构
print(model)
创建随机输入数据 (batch_size, seq_length, input_size)
x = torch.randn(batch_size, seq_length, input_size)
前向传播
output = model(x)
print(“Output shape:”, output.shape) # 输出形状应为 (batch_size, output_size)