RNN(循环神经网络)是一种深度学习模型,可用于序列数据建模,例如语言模型或时间序列预测。以下是RNN的数学理论公式和torch实现示例。
需要注意的是,在训练循环中,我们不需要显式地传递隐藏状态。相反,模型的当前隐藏状态存储在RNN对象的hidden属性中,并在每个时间步自动更新。这是因为我们在forward方法中将模型的当前隐藏状态作为一个实例变量存储,并在每个时间步更新它。
其中,
h
t
h_t
ht 是当前时间步的隐藏状态;
x
t
x_t
xt 是当前时间步的输入;
W
i
h
W_{ih}
Wih 是输入到隐藏层的权重矩阵;
W
h
h
W_{hh}
Whh 是隐藏层到隐藏层的权重矩阵;
W
h
y
W_{hy}
Why 是隐藏层到输出层的权重矩阵;
b
i
h
b_{ih}
bih、
b
h
h
b_{hh}
bhh 和
b
h
y
b_{hy}
bhy 分别是输入到隐藏层、隐藏层到隐藏层和隐藏层到输出层的偏置项;
σ
\sigma
σ 是激活函数,常用的有tanh和ReLU;
softmax
\text{softmax}
softmax 是输出层的激活函数,用于将输出转换为概率分布。
当需要将RNN的输出传递到另一个线性层时,可以使用以下公式:
其中,
z
t
z_t
zt 是传递到下一个线性层的输出;
W
h
z
W_{hz}
Whz 是从隐藏层到下一个线性层的权重矩阵;
b
h
z
b_{hz}
bhz 是下一个线性层的偏置项。
假设我们有一个输入序列 x 1 , x 2 , . . . , x T x_1, x_2, ..., x_T x1,x2,...,xT,其中 T T T 是序列的长度。给定一个时间步 t t t,RNN模型的输出 h t h_t ht 是根据当前输入 x t x_t xt 和前一个时间步的隐藏状态 h t − 1 h_{t-1} ht−1 计算出来的。RNN模型的隐藏状态 h t h_t ht 可以看作是前面所有时间步的信息的累积。
具体地,RNN模型的计算公式为:
其中 W x h W_{xh} Wxh 是输入到隐藏状态的权重矩阵, W h h W_{hh} Whh 是隐藏状态到隐藏状态的权重矩阵, b h b_h bh 是隐藏状态的偏置向量, f f f 是非线性激活函数(例如tanh或ReLU)。
在某些情况下,RNN模型可能还需要输出一个预测结果 y t y_t yt。这可以通过将隐藏状态 h t h_t ht 传递到另一个线性层来实现:
其中 W h y W_{hy} Why 是隐藏状态到输出的权重矩阵, b y b_y by 是输出的偏置向量。
以下是一个使用torch实现的简单RNN模型,用于根据前一个时间步的输入 x t − 1 x_{t-1} xt−1 预测当前时间步的输出 y t y_t yt。在此示例中,我们使用单个隐藏层和tanh激活函数。
import torch
import torch.nn as nn
class RNN(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(RNN, self).__init__()
self.hidden_size = hidden_size
self.hidden = None
self.i2h = nn.Linear(input_size + hidden_size, hidden_size)
self.i2o = nn.Linear(input_size + hidden_size, output_size)
self.tanh = nn.Tanh()
def forward(self, input):
combined = torch.cat((input, self.hidden), 1)
self.hidden = self.tanh(self.i2h(combined))
output = self.i2o(combined)
return output, self.hidden
def init_hidden(self, batch_size):
self.hidden = torch.zeros(batch_size, self.hidden_size)
在此代码中,我们定义了一个名为RNN的torch模型。构造函数接受输入大小、隐藏大小和输出大小作为参数,并创建了一个包含两个线性层和一个tanh激活函数的模型。
forward
方法接受一个输入张量,并将其与当前隐藏状态组合在一起(使用torch.cat
函数)。然后,通过线性层和tanh激活函数计算下一个隐藏状态,并使用计算的输出值。最后,将输出和新的隐藏状态作为元组返回。
init_hidden
方法用于初始化模型的隐藏状态。在此示例中,我们使用torch.zeros
函数创建一个大小为(batch_size, hidden_size)
的张量作为初始隐藏状态。该方法用于每个新的序列或批次之前调用。
以下是使用上述RNN模型对序列数据进行预测的示例代码:
# 定义模型输入和超参数
input_size = 10
hidden_size = 20
output_size = 1
batch_size = 32
seq_len = 10
# 创建模型和损失函数
model = RNN(input_size, hidden_size, output_size)
criterion = nn.MSELoss()
# 随机生成一些序列数据
inputs = torch.randn(batch_size, seq_len, input_size)
targets = torch.randn(batch_size, seq_len, output_size)
# 初始化隐藏状态
model.init_hidden(batch_size)
# 训练模型
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
for i in range(100):
loss = 0
for j in range(seq_len):
output, hidden = model(inputs[:, j, :])
loss += criterion(output, targets[:, j, :])
optimizer.zero_grad()
loss.backward()
optimizer.step()
print("Epoch %d, Loss: %f" % (i, loss.item()))
在此代码中,我们首先定义了模型的输入大小、隐藏大小、输出大小、批次大小和序列长度。然后,我们创建了RNN模型和一个均方误差损失函数。接下来,我们生成一些随机序列数据作为训练数据,并使用init_hidden方法初始化模型的隐藏状态。
在训练循环中,我们循环遍历序列中的每个时间步,并使用模型计算输出和隐藏状态。然后,我们将损失累加到总损失中,并使用反向传播更新模型参数。最后,我们打印出每个时期的总损失。