当前位置:   article > 正文

PyTorch 实现 循环神经网络(RNN)、长短期记忆网络(LSTM)、门控循环单元网络(GRU)_用pytorch实现长短时记忆神经网络

用pytorch实现长短时记忆神经网络

原文链接:https://xiets.blog.csdn.net/article/details/131712791

版权声明:原创文章禁止转载

专栏目录:PyTorch 专栏(总目录)

PyTorch 相关网站:

循环神经网络(Recurrent Neural Network, RNN)是以 序列(Sequence)数据作为输入,沿序列的演进方向进行 递归(Recurrent)且所有循环单元节点按链式连接的一类神经网络。循环神经网络具有记忆性,对序列数据的非线性特征进行学习时具有一定优势。还有 RNN 的加强版 长短期记忆网络(LSTM, Long Short-Term Memory)和 门控循环单元网络(GRU, Gated Recurrent Unit networks)拥有更强的“记忆力”。

循环神经网络相关模型:recurrent-layers

1. 循环神经网络: RNN

RNN 相关类:

  • nn.RNNCell:具有 tanh 或 ReLU 非线性的 RNN 单元。
  • nn.RNN:用于序列输入的多层 tanh 或 ReLU 非线性 RNN 单元(包含多层 RNNCell 的神经网络层)。

普通神经网络的数据是单向传递的,而循环神经网络(RNN)的数据是循环传递的,即后面的输入依赖于前面的输出。例如样本 xt 和一个隐藏向量 ht 一起输入 RNN 网络层处理后输出 ytht+1yt 就是样本 xt 对应的输出,而 ht+1 作为下一次输入的一部分和 xt+1 一起输入网络层得到 yt+1ht+2,以此不断循环传递。

对于首个样本(首次输入)x0,需要手动或自动初始化一个初始隐藏向量 h0

循环神经网络(RNN)结构示意图:

rnn.png

由于循环神经网络的循环和记忆特性,因此非常适合用于处理序列问题。例如:将英文翻译成中文时,输入的是英文句子,输出的是中文句子;用于语音识别时,输入的是音频数据序列,输出的是文本序列。再比如根据过去 N 天的天气预测未来的天气等。

1.1 nn.RNNCell 类

nn.RNNCell 是循环神经网络(RNN)中隐藏层的基础单元,其结构示意图:

rnn_cell.png

RNNCell 数学表达式: h’ = tanh(Wih*x + bih + Whh*h + bhh),如果非线性是 “relu”,则使用 ReLU 替换 tanh。

从公式可以看出,RNNCell 的数学本质是将 xt 和 ht 分别进行线性变换并相加,随后经过非线性激活函数,得到下一个输入的 ht+1

nn.RNNCell 类详情:

class torch.nn.RNNCell(input_size, hidden_size, bias=True, nonlinearity='tanh', device=None, dtype=None)
"""
具有 tanh 或 ReLU 非线性的 RNN 单元 (是一个 nn.Module), 支持 单样本处理 和 批样本处理。


参数:
    input_size (int)    输入 x 中预期的特征数量 (单个样本的特征数)
    hidden_size (int)   隐藏状态 h 中的特征数
    bias (bool)         如果是 False, 则该层不使用偏置权重 b_ih 和 b_hh, 默认为 True
    nonlinearity (str)  要使用的非线性函数, 可以是 "tanh" 或 "relu", 默认为 "tanh"


输入: input, hidden

    input (Tensor)      包含输入特征的张量
    hidden (Tensor)     包含初始隐藏状态的张量, 如果未提供则默认为 zeros。


输出: hidden'

    hidden' (Tensor)    包含批次中每个样本的下一个隐藏状态张量


形状:
    input: (N, H_in) 或 (H_in,)
        包含输入特征的张量, H_in = input_size, H_in 表示单个样本的输入特征数量。
        如果输入的是单个样本数据, 则形状为 (H_in,); 如果是批处理, 则形状为 (N, H_in), N 表示批大小 (样本数量)。

    hidden: (N, H_out) 或 (H_out,)
        包含初始隐藏状态的张量, H_out = hidden_size, 如果未提供则默认为 zeros 张量。
        如果输入的是单个样本数据, 则形状为 (H_out,); 如果是批处理, 则形状为 (N, H_out), N 表示批大小 (对应输入的每一个样本)。
    
    output (hidden'): (N, H_out) 或 (H_out,)
        包含用于下一个样本的输入的隐藏状态张量, H_out = hidden_size。
        如果输入的是单个样本数据, 则形状为 (H_out,); 如果是批处理, 则形状为 (N, H_out), N 表示批大小 (对应输入的每一个样本)。


变量:
    weight_ih (Tensor)  可学习的 输入隐藏权重(input-hidden weights), 形状为 (hidden_​​size, input_size)
    weight_hh (Tensor)  可学习的 隐藏权重(hidden-hidden weights), 形状为 (hidden_​​size, hidden_​​size)
    bias_ih             可学习的 输入隐藏偏差(input-hidden bias), 形状为 (hidden_​​size)
    bias_hh             可学习的 隐藏偏差(hidden-hidden bias), 形状为 (hidden_​​size,)
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

nn.RNNCell 类代码示例:

import torch
from torch import nn

# 创建 RNNCell (nn.Module) 实例, 样本输入特征数为2, 隐藏状态特征数为3
rnn_cell = nn.RNNCell(input_size=2, hidden_size=3, dtype=torch.float32)

print(rnn_cell)
# 输出: RNNCell(2, 3)

# -------------------------------------------------------------------------------------

# 输入数据 (单样本, 有2个特征)
input0 = torch.tensor([1, 2], dtype=torch.float32)

# 初始隐藏状态 (和 input0 一起输入 RNNCell 单元隐藏状态张量, 有3个特征)
hidden0 = torch.tensor([1, 2, 3], dtype=torch.float32)

# 输入 RNNCell 单元 (单样本输入), 返回 输入用于下一个样本输入的隐藏状态张量
hidden1 = rnn_cell(input0, hidden0)

print(hidden1)
# 输出: tensor([0.2513, 0.9635, 0.6665], grad_fn=<SqueezeBackward1>)

# -------------------------------------------------------------------------------------

# 下一个输入数据 (下一个样本)
input1 = torch.tensor([3, 4], dtype=torch.float32)

# 把 当前输入 和 上一个样本返回的隐藏状态张量 一起输入 RNNCell, 返回 输入用于下一个样本输入的隐藏状态张量
hidden2 = rnn_cell(input1, hidden1)

print(hidden2)
# 输出: tensor([0.8123, 0.8857, 0.9854], grad_fn=<SqueezeBackward1>)

# -------------------------------------------------------------------------------------

# 再下一个数据
input2 = torch.tensor([5, 6], dtype=torch.float32)
hidden3 = rnn_cell(input2, hidden2)
print(hidden3)
# 输出: tensor([0.9777, 0.9607, 0.9995], grad_fn=<SqueezeBackward1>)

# -------------------------------------------------------------------------------------
# 批样本数据处理
# -------------------------------------------------------------------------------------

# 输入也可以批数据, input0_ls 批数据有 3 个样本 (每个样本2个特征)
input0_ls = torch.tensor([[1, 2], [11, 12], [21, 22]], dtype=torch.float32)

# 隐藏状态也必须是批数据, hidden0_ls 表示有 3 个隐藏状态 (每1个样本对应1个隐藏状态), 每个隐藏状态有 3 个特征
hidden0_ls = torch.tensor([[1, 2, 3], [1, 2, 3], [1, 2, 3]], dtype=torch.float32)

# 批数据输入 RNNCell 单元, 返回用于下一批样本输入的隐藏状态张量, 形状为 (3, 3), 每1个样本 对应 一个隐藏状态(包括3个特征)
hidden1_ls = rnn_cell(input0_ls, hidden0_ls)

print(hidden1_ls)
# 输出:
# tensor([[0.2513, 0.9635, 0.6665],
#         [0.9999, 0.9999, 1.0000],
#         [1.0000, 1.0000, 1.0000]], grad_fn=<TanhBackward0>)

# -------------------------------------------------------------------------------------

# 下一批数据
input1_ls = torch.tensor([[3, 4], [13, 14], [23, 24]], dtype=torch.float32)

hidden2_ls = rnn_cell(input1_ls, hidden1_ls)

print(hidden2_ls)
# 输出:
# tensor([[0.8123, 0.8857, 0.9854],
#         [1.0000, 0.9996, 1.0000],
#         [1.0000, 1.0000, 1.0000]], grad_fn=<TanhBackward0>)

# -------------------------------------------------------------------------------------

# 再下一批数据
input2_ls = torch.tensor([[5, 6], [15, 16], [25, 26]], dtype=torch.float32)

hidden3_ls = rnn_cell(input2_ls, hidden2_ls)

print(hidden3_ls)
# 输出:
# tensor([[0.9777, 0.9607, 0.9995],
#         [1.0000, 0.9999, 1.0000],
#         [1.0000, 1.0000, 1.0000]], grad_fn=<TanhBackward0>)

# 注意:
#     批样本处理时, 同一批次的多个样本相互独立, 即不是序列组成, 多个批数据相同位置的样本才是组成的序列。
#     如多批数据:
#         第1批: [[1, 2], [11, 12], [21, 22]]
#         第2批: [[3, 4], [13, 14], [23, 24]]
#         第3批: [[5, 6], [15, 16], [25, 26]]
#
#     [1, 2], [3, 4], [5, 6] 是一个序列,
#     即 样本[1, 2] 输出的隐藏状态作为 样本[3, 4] 的输入隐藏状态, 样本[3, 4] 的输出隐藏状态作为 样本[5, 6] 的输入隐藏状态。
#
#     同样的 [11, 12], [13, 14], [15, 16] 是一个序列; [21, 22], [23, 24], [25, 26] 也是一个序列。
#
#     [1, 2], [11, 12], [21, 22] 并不是一个序列, 而是由多个序列中的一个样本组成的一批数据。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100

1.2 nn.RNN 类

nn.RNN 包含多层 nn.RNNCell 的神经网络层。nn.RNNCell 处理序列时,需要将序列中的样本按顺序逐个循环输入。而 nn.RNN 模块可以直接把一个序列当做输入,内部自动使用 RNNCell 逐个循环处理每一个样本,最后输出由每一个样本对应的输出组成的一个输出序列和最后一个样本输出的隐藏向量 h。

nn.RNN 类详情:

class torch.nn.RNN(*args, **kwargs)
"""
包含多层 RNNCell 的神经网络层。支持 单序列处理 和 批序列处理。序列是由多个样本按顺序组成的一组样本。


参数:
    input_size      输入 x 中预期的特征数量 (单个样本的特征数)
    
    hidden_size     隐藏状态 h 中的特征数 (也是 x 对应输出 y 的特征数)
    
    num_layers      循环层数, 包含的 RNNCell 数量。例如设置 num_layers=2 意味着将两个 RNNCell 堆叠在一起
                    形成一个堆叠的 RNN, 第二个 RNNCell 接收第一个 RNNCell 的输出并计算最终结果。默认值为 1。

    nonlinearity    要使用的非线性函数, 可以是 "tanh" 或 "relu", 默认为 "tanh"。
    
    bias            如果是 False, 则 RNNCell 层不使用偏置权重 b_ih 和 b_hh, 默认为 True。
    
    batch_first     批序列数据处理时, 最先一维是否作为批大小(有多少条序列)。
                    如果 batch_first=True, 则输入和输出张量的形状组成为 (batch, seq, feature), 
                    而不是 (seq, batch, feature)。默认为 False。请注意, 此参数不适用于 隐藏状态 或 Cell状态。
                    batch       表示批大小, 即有多少条序列。
                    seq         表示一条序列的长度, 即一条序列中的样本数量。
                    feature     表示一个样本的特征数。
                    
    dropout         如果非0, 则在除最后一层之外的每个 RNNCell 层的输出上引入一个 Dropout 层, dropout 概率等于此值。
                    默认值为 0。
    
    bidirectional   如果为 True, 则成为双向 RNN。默认:False。
    
    还可指定设备和数据类型, 如 device="cuda", dtype=torch.float64。


输入: input, h_0

    input           输入张量。
                    如果非批处理: 形状为 (L, H_in)。
                    如果是批处理: batch_first=False 时, 形状为 (L, N, H_in); 
                                batch_first=True 时, 形状为 (N, L, H_in)。

                    N       表示批大小, 即有多少条序列。
                    L       表示表示一条序列的长度, 即一条序列中的样本数量。
                    D       如果是双向 RNN, 则 D=2, 否则 D=1。
                    H_in    表示一个样本的特征数, 即 H_in == input_size。

    h_0             初始隐藏状态张量。
                    如果非批处理: 形状为 (D * num_layers, H_out)。
                    如果是批处理: 则形状为 (D * num_layers, N, H_out)。
                    请注意, 隐藏状态张量形状不受 batch_first 参数影响。
                    如果未提供, 默认为 zeros 张量。
                    
                    H_out   表示隐藏状态特征数 (也是 x 对应输出 y 的特征数), 即 H_out == hidden_size。


输出: output, h_n

    output          输出张量。
                    如果非批处理: 形状为 (L, D * H_out)。
                    如果是批处理: batch_first=False 时, 形状为 (L, N, D * H_out); 
                                batch_first=True 时, 形状为 (N, L, D * H_out)。
    
    h_n             输出的隐藏状态张量, 形状与 h_0 相同, 作为下一批数据的隐藏状态输入。


变量:
    weight_ih_l[k]  第 k 层的可学习的输入隐藏权重, 形状为 (hidden_​​size, input_size)    
    weight_hh_l[k]  第 k 层的可学习的隐藏权重, 形状为 (hidden_​​size, hidden_​​size)
    bias_ih_l[k]    第 k 层的可学习的输入隐藏偏置, 形状为 (hidden_​​size,)
    bias_hh_l[k]    第 k 层的可学习的隐藏-隐藏偏差, 形状为 (hidden_​​size,)
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

nn.RNN 类代码示例:

import torch
from torch import nn

# RNN 网络实例: 样本输入特征数为2, 隐藏层特征数为3, RNNCell层数为4, 用于批处理时最先一维作为批大小
rnn = nn.RNN(input_size=2, hidden_size=3, num_layers=4, batch_first=True, dtype=torch.float32)

#
# 单序列处理
#
# 一条输入序列: 包括3个样本, 每个样本2个特征
input_seq_0 = torch.tensor([[1, 2],
                            [3, 4],
                            [5, 6]], dtype=torch.float32)

# 初始隐藏状态: 有4个RNNCell层(num_layers), 每层有3个输出特征(hidden_size)
hidden_0 = torch.tensor([[1, 2, 3],
                         [3, 4, 5],
                         [7, 8, 9],
                         [1, 4, 9]], dtype=torch.float32)

# 输入RNN网络: 返回 输出序列 和 用于下一次输入的隐藏状态
output_seq_0, hidden_1 = rnn(input_seq_0, hidden_0)

print(output_seq_0.shape)       # 输出: torch.Size([3, 3])
print(hidden_1.shape)           # 输出: torch.Size([4, 3])

#
# 批序列处理
#
# 输入的批序列: 2条输入序列, 每条序列3个样本, 每个样本2个特征, 形状为 (2, 3, 2)
input_seq_ls_0 = torch.tensor([[[1, 2],
                                [3, 4],
                                [5, 6]],

                               [[7, 8],
                                [9, 10],
                                [11, 12]]], dtype=torch.float32)

# 初始隐藏状态: 形状为 (num_layers, seq_n, hidden_size), seq_n 表示输入序列的条数
hidden_ls_0 = torch.randn((4, 2, 3), dtype=torch.float32)

# 批序列输入RNN网络, 返回 输出批序列 和 用于下一次输入的隐藏状态
input_seq_ls_1, hidden_ls_1 = rnn(input_seq_ls_0, hidden_ls_0)

print(input_seq_ls_1.shape)     # 输出: torch.Size([2, 3, 3])
print(hidden_ls_1.shape)        # 输出: torch.Size([4, 2, 3])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

1.3 RNN 实例: 用 sin 预测 cos

使用 RNN 模型,根据 sin 序列去预测 cos 序列,即输入一条 sin 序列,输出对应的一条 cos 序列:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from typing import Optional

import matplotlib.pyplot as plt
import torch
from torch import nn
from torch import optim


class RNNNet(nn.Module):
    """
    神经网络模型
    """

    def __init__(self, *, input_size: int, hidden_size: int):
        super().__init__()

        self.hidden_size = hidden_size

        # 循环神经网络层
        self.rnn = nn.RNN(
            input_size=input_size,
            hidden_size=hidden_size,
            num_layers=1,
            dtype=torch.float32,
        )

        # 线性输出层, 把 RNN 层的多个输出经过全连接层转换为 1 个特征输出
        self.out = nn.Linear(in_features=hidden_size, out_features=1, dtype=torch.float32)

    def forward(self, inputs: torch.Tensor, hidden_state: Optional[torch.Tensor]):
        """
        前向传播
        """
        rnn_outputs, hidden_state = self.rnn(inputs, hidden_state)
        outputs = self.out(rnn_outputs)
        return outputs, hidden_state


def main():
    # 网络模型: 样本的输入特征数为1, 隐藏层的特征数为20
    rnn_net = RNNNet(input_size=1, hidden_size=20)
    # 优化器: Adam优化器, 学利率为 0.001
    optimizer = optim.Adam(rnn_net.parameters(), lr=0.001)
    # 损失函数: 均方误差损失函数
    loss_func = nn.MSELoss()

    # 随机生成初始隐藏向量
    hidden_state = torch.randn((1, 20), dtype=torch.float32)

    # plt 开启交互式模式
    plt.ion()

    steps = 1000
    # 从 [0, 1002*pi] 对应的 sin 值组成的序列分段依次输入 RNN 模型, 每次输入长度跨度为 2*pi 的序列
    for step in range(0, steps + 1, 2):
        start, end = step * torch.pi, (step + 2) * torch.pi
        steps_seq = torch.linspace(start, end, 100, dtype=torch.float32)

        # 输入 sin序列, 形状为 (seq_len, features), seq_len 表示本次输入序列的长度(样本个数), features 表示样本的输入特征数
        inputs_sin_seq = torch.sin(steps_seq).reshape((-1, 1))
        # 目标 cos序列
        targets_cos_seq = torch.cos(steps_seq).reshape((-1, 1))

        # 前向传播: 输入一个 sin序列, 输出 另一个序列(需要拟合目标cos序列的序列)
        # sin序列 和 初始隐藏向量 输入 RNN模型, 返回 预测的输出序列 和 用于下一次输入的隐藏向量
        predicts_cos_seq, hidden_state = rnn_net(inputs_sin_seq, hidden_state)

        # 对 预测的输出序列 和 目标cos序列 求损失值
        loss = loss_func(predicts_cos_seq, targets_cos_seq)

        # 模型参数梯度清零
        optimizer.zero_grad()
        # 误差反向传播
        loss.backward()
        # 更新模型参数
        optimizer.step()

        # 隐藏状态张量不需要自动计算梯度, 必须使用不带梯度的张量才能用于下次输入
        hidden_state = hidden_state.data

        # 定时显示训练状态
        if (step % 100 == 0) or (step == steps - 2):
            # 清除当前绘图区域
            plt.cla()
            # 绘制绘图区域的标题
            plt.title(f"step: {step}/{steps}, loss = {loss.item():.6f}")
            # 绘制 目标cos序列 (红色实线)
            plt.plot(steps_seq, targets_cos_seq.data.numpy().reshape((-1,)), "r-", label="target")
            # 绘制 预测的输出序列 (蓝色虚线), 根据训练次数观察 预测输出序列 与 目标cos序列 的拟合程度
            plt.plot(steps_seq, predicts_cos_seq.data.numpy().reshape((-1,)), "b--", label="predict")
            # 绘制标签
            plt.legend()
            # 重绘图形
            plt.draw()
            # 短暂暂停以运行 GUI 事件循环, 并更新窗口 (没有更新窗口, 前面 draw 绘制的不会显示)
            plt.pause(0.01)

    # plt 关闭交互式模式
    plt.ioff()
    # plt 显示图形 (关闭了交互式模式, 显示时阻塞)
    plt.show()


if __name__ == "__main__":
    main()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108

其中几次训练拟合结果:

rnn_sin_cos_01.png

rnn_sin_cos_02.png

rnn_sin_cos_03.png

2. 长短期记忆网络: LSTM

长短期记忆网络(LSTM, Long Short-Term Memory)是一种加强版的时间循环神经网络,可以解决一般循环神经网络(RNN)存在的长期依赖问题,以及长序列训练过程中的梯度消失和梯度爆炸问题。

nn.RNNnn.LSTM 均继承自 nn.RNNBase。与 RNN 不同的是,LSTM 的输入和输入包括两个隐藏向量:隐藏状态(hidden_state) 和 单元状态(cell_state)。

RNN 和 LSTM 结构比较:

rnn_lstm.png

LSTM 相关的类:

  • nn.LSTMCell:长短期记忆 (LSTM) 单元。
  • nn.LSTM:将多层长短期记忆 (LSTM) RNN 应用于输入序列(包含多个 LSTMCell 的神经网络模型)。

2.1 nn.LSTMCell 类

nn.LSTMCell 类详情:

class torch.nn.LSTMCell(input_size, hidden_size, bias=True, device=None, dtype=None)
"""
长短期记忆 (LSTM) 单元, LSTM 网络组成的单元。

参数:
    input_size (int)    输入 x 中预期的特征数量 (单个样本的特征数)
    hidden_size (int)   隐藏状态 h 中的特征数
    bias (bool)         如果是 False, 则该层不使用偏置权重 b_ih 和 b_hh, 默认为 True


输入: input, (h_0, c_0)
    input               包含输入特征的张量, 形状为 (batch, input_size) 或 (input_size)
    h_0                 包含初始隐藏状态的张量, 形状为 (batch, hidden_​​size) 或 (hidden_​​size)
    c_0                 包含初始单元状态的张量, 形状为 (batch, hidden_​​size) 或 (hidden_​​size)
    
    input, h_0, c_0 非批处理时形状为一维, 批处理时形状为二维。如果 (h_0, c_0) 未提供, 则默认均值 zeros 张量。

输出: (h_1, c_1)
    h_1                 包含下一个隐藏状态的张量, 形状为 (batch, hidden_​​size) 或 (hidden_​​size)
    c_1                 包含下一个单元状态的张量, 形状为 (batch, hidden_​​size) 或 (hidden_​​size)

变量:
    weight_ih (Tensor)  可学习的 输入隐藏权重, 形状为 (4*hidden_​​size, input_size)
    weight_hh (Tensor)  可学习的 隐藏权重, 形状为 (4*hidden_​​size, hidden_​​size)
    bias_ih             可学习的 输入隐藏偏差, 形状为 (4*hidden_​​size)
    bias_hh             可学习的 隐藏偏差, 形状为 (4*hidden_​​size)
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

nn.LSTMCell 类代码示例:

lstm_cell = nn.LSTMCell(input_size=10, hidden_size=20)
inputs = torch.randn((2, 3, 10))                        # (time_steps, batch, input_size)
hx, cx = torch.randn((3, 20)), torch.randn((3, 20))     # (batch, hidden_size)
for i in range(inputs.shape[0]):
    input = inputs[i]
    hx, cx = lstm_cell(input, (hx, cx))

# batch表示单条序列的长度(样本数量), input_size表示单个样本的特征数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.2 nn.LSTM 类

nn.LSTM 类详情:

class torch.nn.LSTM(*args, **kwargs)
"""
将多层长短期记忆 (LSTM) RNN 应用于输入序列(包含多个 LSTMCell 的神经网络模型)。

LSTM 类的各参数与 RNN 基本相同。

参数:
    input_size      输入x中预期特征的数量
    hidden_size     隐藏状态h中的特征数
    num_layers      循环层数, 包含的 LSTMCell 数量, 默认为 1。
    bias            如果是 False, 则 RNNCell 层不使用偏置权重 b_ih 和 b_hh, 默认为 True。
    batch_first     如果 True,则输入和输出张量作为 (batch, seq, feature) 而不是 (seq, batch, feature) 提供。
    dropout         如果非0, 则在除最后一层之外的每个 LSTMCell 层的输出上引入一个 Dropout 层, dropout 概率等于此值。默认值为 0。
    bidirectional   如果 True, 则成为双向 LSTM。默认: False。
    proj_size       如果 > 0, 将使用具有相应大小投影的 LSTM, 默认为 0。


输入: input, (h_0, c_0)
    input           输入张量。
                    如果非批处理: 形状为 (L, H_in)。
                    如果是批处理: batch_first=False 时, 形状为 (L, N, H_in); 
                                batch_first=True 时, 形状为 (N, L, H_in)。

                    N       表示批大小, 即有多少条序列。
                    L       表示表示一条序列的长度, 即一条序列中的样本数量。
                    D       如果是双向 RNN, 则 D=2, 否则 D=1。
                    H_in    表示一个样本的特征数, 即 H_in == input_size。

    h_0             初始隐藏状态张量。
                    如果非批处理: 形状为 (D * num_layers, H_out)。
                    如果是批处理: 则形状为 (D * num_layers, N, H_out)。
                    请注意, 隐藏状态张量形状不受 batch_first 参数影响。
                    如果未提供, 默认为 zeros 张量。

                    H_out = proj_size if (proj_size > 0) else hidden_size
​

    c_0             初始化单元状态张量。
                    如果非批处理: 形状为 (D * num_layers, H_cell)。
                    如果是批处理: 则形状为 (D * num_layers, N, H_cell)。
                    请注意, 单元状态张量形状不受 batch_first 参数影响。
                    如果未提供, 默认为 zeros 张量。

                    H_cell = hidden_size
    

输出: output, (h_n, c_n)
    output          输出张量。
                    如果非批处理: 形状为 (L, D * H_out)。
                    如果是批处理: batch_first=False 时, 形状为 (L, N, D * H_out); 
                                batch_first=True 时, 形状为 (N, L, D * H_out)。
    
    h_n             输出的隐藏状态张量, 形状与 h_0 相同, 作为下一批数据的隐藏状态输入。
                    当 bidirectional=True 时, h_n 将分别包含最终前向和反向隐藏状态的串联。

    c_n             输出的单元状态张量, 形状与 c_0 相同, 作为下一批数据的单元状态输入。
                    当 bidirectional=True 时, c_n 将分别包含最终正向和反向单元状态的串联。

变量:
    包含每一层 LSTMCell 的变量。
"""
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

nn.LSTM 类代码示例:

lstm = nn.LSTM(input_size=10, hidden_size=20, num_layers=2, batch_first=True)
input = torch.randn(3, 5, 10)               # (batch_size, batch, input_size)
h0 = torch.randn(2, 3, 20)                  # (num_layers, batch_size, hidden_size)
c0 = torch.randn(2, 3, 20)                  # (num_layers, batch_size, hidden_size)
output, (hn, cn) = lstm(input, (h0, c0))

# output: (batch_size, batch, hidden_size)
# batch_size表示有多少条序列, batch表示单条序列的长度(样本数量), input_size表示单个样本的特征数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3. 门控循环单元网络: GRU

门控循环单元网络(GRU, Gated Recurrent Unit networks)也是一种增强版的循环神经网络(RNN)。和 LSTM 一样,也是为了解决长期依赖问题,以及反向传播中的梯度问题。GRU 的实验效果和 LSTM 相似,但 GRU 更容易计算,可以节省计算资源。

GRU 的构造参数、输入和输出结构与普通的 RNN 一样。

GRU 相关类:

  • nn.GRUCell:门控循环单元 (GRU) 单元。
  • nn.GRU:将多层门控循环单元 (GRU) RNN 应用于输入序列(包含多个 GRUCell 的神经网络模型)。

nn.GRUCell 类代码示例:

gru_cell = nn.GRUCell(input_size=10, hidden_size=20)
inputs = torch.randn((6, 3, 10))    # (time_steps, batch, input_size)
hx = torch.randn((3, 20))           # (batch, hidden_size)
for i in range(inputs.shape[0]):
    input = inputs[i]
    hx = gru_cell(input, hx)

# batch表示单条序列的长度(样本数量), input_size表示单个样本的特征数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

nn.GRU 类代码示例:

gru = nn.GRU(input_size=10, hidden_size=20, num_layers=2, batch_first=True)
input = torch.randn(3, 5, 10)   # (batch_size, batch, input_size)
h0 = torch.randn(2, 3, 20)      # (num_layers, batch_size, hidden_size)
output, hn = gru(input, h0)
print(output.shape)

# output: (batch_size, batch, hidden_size)
# batch_size表示有多少条序列, batch表示单条序列的长度(样本数量), input_size表示单个样本的特征数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/373293?site
推荐阅读
相关标签
  

闽ICP备14008679号