一、首先先上代码:这个是接pytorch API的调用代码
import torch
import torch.nn as nn
bs, T = 2,3#批次大小,输入序列长度
input_size,hidden_size=2,3#输入特征大小,隐含层特征大小
input = torch.randn(bs,T, input_size)
h_prev = torch.zeros(bs, hidden_size)#初始隐含状态
#step1:调用pytorch API
rnn = nn.RNN(input_size,hidden_size,batch_first=True)
rnn_output,state_final = rnn(input,h_prev.unsqueeze(0))
print(rnn_output)
print(state_final)
参数解释一下:
1、bs(batch_size):就每个批次输入的数据量
2、T(也就是序列长度):
如果是纯时间序列预测的模型,那么在纯时间序列模型中,T。时间步数代表着历史数据中的每个时间点,而模型通过学习这些历史数据来预测未来的结果。
假设我们有一段时间内的房价数据,每个月记录一个时间点的房价。我们想要根据过去几个月的房价数据来预测下个月的房价。在这种情况下,时间步数就是过去几个月的数量。
举个例子,假设我们有过去 12 个月的房价数据,现在想要预测下个月的房价。那么在这个例子中,时间步数就是 12。我们可以用过去 12 个月的房价数据作为输入特征,然后预测下个月的房价作为模型的输出。
如果是NLP模型的话:
举个例子,假设我们要用RNN模型来预测一个句子中的下一个单词。我们可以将句子分割成单词,并按照单词的顺序依次输入到模型中。在这个例子中,每个时间步对应着句子中的一个单词。
考虑句子:"The cat is sitting on the mat.",如果我们按照单词的顺序将其输入到RNN模型中,则每个单词对应一个时间步。例如:
- 时间步 1:输入单词 "The"
- 时间步 2:输入单词 "cat"
- 时间步 3:输入单词 "is"
- 时间步 4:输入单词 "sitting"
- 时间步 5:输入单词 "on"
- 时间步 6:输入单词 "the"
- 时间步 7:输入单词 "mat"
在这个例子中,整个句子被分成了7个时间步,每个时间步上都有相应的输入数据。模型在每个时间步上接收输入并产生输出,从而对整个序列进行处理。
3、layer 层
-
input_size
:输入特征的维度大小。 -
hidden_size
:隐含层(隐藏层)的特征维度大小。 -
input
:输入数据,形状为(batch_size, sequence_length, input_size)。 -
h_prev
:初始隐含状态,形状为(batch_size, hidden_size)。在这里,初始隐含状态被初始化为全零张量。
-
rnn_output
:这是 RNN 模型的输出结果。它是一个张量,包含了每个时间步的隐含状态的输出。形状为(batch_size, sequence_length, hidden_size)
,其中batch_size
表示批次大小,sequence_length
表示序列长度,hidden_size
表示隐含状态的特征维度大小。 -
state_final
:这是 RNN 模型的最终隐含状态。它表示 RNN 模型在整个序列上的最后一个时间步的隐含状态。形状为(num_layers, batch_size, hidden_size)
,其中num_layers
表示 RNN 模型的层数,通常为 1。
以上解释还是有些抽象,我举个例子:
import torch
import torch.nn as nn
# 输入数据 input,形状为 (3, 5, 1),表示 3 个样本,每个样本序列长度为 5,每个时间步的输入特征维度为 1
input = torch.randn(3, 5, 1)
# 初始隐含状态 h_prev,形状为 (3, 10),表示 3 个样本,隐含状态的特征维度为 10
h_prev = torch.zeros(3, 10)
# 定义 RNN 模型
rnn = nn.RNN(input_size=1, hidden_size=10, batch_first=True)
# 将输入数据和初始隐含状态输入到 RNN 模型中
rnn_output, state_final = rnn(input, h_prev.unsqueeze(0))
# 输出结果 rnn_output 的形状为 (3, 5, 10),表示 3 个样本,每个样本序列长度为 5,每个时间步的隐含状态的特征维度为 10
# 最终的隐含状态 state_final 的形状为 (1, 3, 10),表示 1 层 RNN 模型,3 个样本,隐含状态的特征维度为 10
这里的 rnn_output
包含了每个时间步的隐含状态的输出,state_final
是整个序列上最后一个时间步的隐含状态。h_prev.unsqueeze(0)这个什么意思?
h_prev.unsqueeze(0)
表示对初始隐含状态 h_prev
进行操作,使用 unsqueeze(0)
方法在第 0 维度上增加一个维度。
具体来说,假设 h_prev
的形状是 (3, 10)
,其中 3 是批次大小,10 是隐含状态的特征维度。那么使用 unsqueeze(0)
方法之后,h_prev
的形状将变为 (1, 3, 10)
。
这个操作是为了符合 RNN 模型接受初始隐含状态的要求。在 PyTorch 中,RNN 模型期望初始隐含状态的形状为 (num_layers, batch_size, hidden_size)
,其中 num_layers
是 RNN 模型的层数,通常为 1。因此,我们需要对初始隐含状态进行相应的维度调整,以适应模型的输入要求。
所以,h_prev.unsqueeze(0)
的作用是将原始的初始隐含状态 h_prev
转换为符合 RNN 模型输入要求的形状。
二、手写一个rnn_forward函数,实现RNN计算原理
解释一下这里的ht到底是什么意思在这个公式中,ht 表示RNN模型在时间步 t 的隐含状态,通常也被称为隐藏状态或者记忆状态。RNN(循环神经网络)的核心是根据当前的输入xt 和前一个时间步的隐含状态 ℎt−1来计算当前时间步的隐含状态 ht。
具体来说:会有一个叠加的过程
- xt 是当前时间步的输入,通常是一个特征向量。
- Wih 是输入到隐含层的权重矩阵。
- bih 是输入到隐含层的偏置向量。
- Whh 是隐含层到隐含层的权重矩阵。
- bhh 是隐含层到隐含层的偏置向量。
- tanh 是双曲正切函数,用于加入非线性。
#step2: 手写一个RNN forward,实现RNN的计算原理 def run_forward(input,weight_ih,weitht_hh,bias_ih,bias_hh,h_prev): bs, T, input_size = input.shape h_dim = weight_ih.shape[0] h_out = torch.zeros(bs, T, h_dim)#初始化一个输出(状态)矩阵 for t in range(T): x = input[:,t,:].unsqueeze(2)#获取当前时刻输入特征,bs*input_size*1 w_ih_batch = weight_ih.unsqueeze(0).tile(bs,1,1)#ba*h_dim*input_size w_hh_batch = weight_hh.unsqueeze(0),tile(bs,1,1)#bs*h_dim w_times_x = torch.bmm(w_ih_batch,x).squeeze(-1)#bs*h_dim w_times_h = torch.bmm(w_hh_batch,h_prev.unsqueeze(2).squeeze(-1))#bs*h_dim h_prev = torch.tanh(w_times_h+bias_ih+weitht_hh) h_out[:,t,:] = h_prev return h_out,h_prev.unsqueeze(0)
最后得到的就是ht