循环神经网络基础与PyTorch实现

循环神经网络(RNN)是深度学习中处理序列数据的重要工具。本文将带了解RNN的基本概念,并使用PyTorch框架进行实现。请系好安全带,即将探索RNN的基本细节。

RNN的三个术语

输入(Input):RNN的输入。

隐藏状态(Hidden):所有层在最后一个时间步的隐藏状态。

输出(Output):所有时间步的最后一层的隐藏状态,以便将隐藏状态传递给下一层。

PyTorch中的单向RNN

在上述图中,有N个时间步(水平方向)和M层(垂直方向)。在t=0时输入,并最初将隐藏状态传递给RNN单元,然后输出隐藏状态再传递给下一个输入序列的RNN单元,以此类推。在PyTorch中实现时,如果对PyTorch不熟悉,想给一个非常有用的提示,这真的非常重要。如果遵循这个提示,保证会很快学会:关注形状。

输入数据的形状

假设有以下一维数组输入数据(行=7,列=1):

# 假设数据

现在需要将这个数据分成批次。假设取批次大小=2。

RNN的输入

RNN应该有3个维度:(批次大小,序列长度和输入维度)。

批次大小(Batch Size):一次发送给模型的样本数量。在这个例子中,批次大小=2,但可以取4, 8, 16, 32, 64等,这取决于内存(基本上是2的幂)。

序列长度(Sequence Length):输入数据序列的长度(时间步:0,1,2…N),RNN学习数据集中的序列模式。这里的灰色部分是序列长度,所以序列长度=3。

假设有基于日频率(1天)的股票市场数据,希望网络学习30天的数据序列。那么序列长度将是30。

输入维度或输入大小(Input Dimension):在数据集中使用的特征或维度的数量。在这种情况下,是一(列/特征)。假设有以下特征的股票市场数据:最高价、最低价、开盘价和收盘价,想要预测收盘价。在这种情况下,有输入维度=4:最高价、最低价、开盘价和收盘价。将在后面更详细地看到输入维度。

PyTorch接受输入的两种形状

输入类型1:序列长度,批次大小,输入维度

输入类型2:批次大小,序列长度,输入维度

如果选择输入类型1,形状将是=3, 2, 1。如果选择输入类型2,形状将是=2, 3, 1。

从输入数据中分离出标签

因为只向模型提供输入序列,然后将详细探讨这两种输入形状。首先,创建了一个自定义的Dataset类,它返回输入序列和标签。这里输入是一个浮点数组,seq_len是窗口大小或输入数据的序列长度。

class MyDataset(Dataset): def __init__(self, input, seq_len): self.input = input self.seq_len = seq_len def __getitem__(self, item): return input[item:item + self.seq_len], input[item + self.seq_len] def __len__(self): return len(self.input) - self.seq_len

定义序列并将其转换为PyTorch张量

input = np.arange(1,8).reshape(-1,1) input = torch.tensor(input, dtype=torch.float)

现在可以调用MyDataset类来获取数据:

ds = MyDataset(input, 3) dl = DataLoader(ds, batch_size=2)

对于inp, label in dl:

for inp, label in dl: print(inp.numpy())

如果仔细观察,第一个维度是2,这是批次大小,接下来是3,这是序列长度,最后一个是1,特征或输入维度。现在有形状为2, 3, 1(BS, SeqLen, Input Dim)。

如果有2个输入特征

那么输入数据将如下所示。唯一改变的是第三维变为2(2是特征的数量)。

将输入类型2转换为输入类型1

如果想将这个转换为输入类型1,需要置换输入。要做到这一点,只需在输入数据中交换批次维度和序列维度,如下所示:

inp.permute(1,0,2) # 交换维度0和维度1

如果现在注意到,第一个维度是3,而不是2,它是序列长度。第二个维度是2,这是批次大小。第三个维度是2,这是输入维度/特征。输入形状是=(3, 2, 2),这是输入类型1。如果有点困惑,那么花点时间在这个图像上...

实现

让实现小型循环神经网络类,继承基类nn.Module。HL_size = 隐藏大小可以定义为32, 64, 128(再次最好是2的幂)和输入大小是数据中的特征数量(输入维度)。这里输入大小对于数据类型2是2,对于数据类型1是1。

class RNNModel(torch.nn.Module): def __init__(self, input_size, HL_size): super(RNNModel, self).__init__() self.rnn = torch.nn.RNN(input_size=input_size, hidden_size=Hidden_Size(HS), num_layers=number_of_stacked_RNN, bidirectional=True/False, batch_first=True default is False) # 如果想使用输出作为下一层的输入 self.linear2 = torch.nn.Linear(#Direction * HS , Output_size) # 如果想使用隐藏作为下一层的输入 self.linear2 = torch.nn.Linear(HS , Output_size)

RNN返回输出和隐藏状态。

输出形状

如果使用batch_first=True,那么输出形状是(批次大小,序列长度,#方向 * 隐藏大小)。如果使用batch_first=False,那么输出形状是(序列长度,批次大小,方向数量 * 隐藏大小)。

隐藏状态形状

(方向数量 * 层数,批次大小,隐藏大小),它保存有关最终隐藏状态的信息。所以大多数时候把隐藏状态作为输入传递给self.linear2。

如果正在进行回归或二元分类,那么线性变换中的output_size应该是1,如果正在进行多类分类,那么Output_size将是类别的数量。

def forward(self, input): out, hidden_ = self.rnn(input) #out: 选择想要传递给线性层的时间步数据 out = self.linear2(out) out = self.linear2(hidden_)
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485