• 【深度学习实验】循环神经网络(三):门控制——自定义循环神经网络LSTM(长短期记忆网络)模型


    目录

    一、实验介绍

    二、实验环境

    1. 配置虚拟环境

    2. 库版本介绍

    三、实验内容

    0. 导入必要的工具包

    1. LSTM类

    a.__init__(初始化)

    b. init_state(初始化隐藏状态)

    c. forward(前向传播)

     2. RNNModel类

    a.__init__(初始化)

    b. forward(前向传播)

    c. begin_state(初始化隐藏状态)

    3. 代码整合


            经验是智慧之父,记忆是智慧之母。

    ——谚语

    一、实验介绍

              LSTM(长短期记忆网络)是一种循环神经网络(RNN)的变体,用于处理序列数据。它具有记忆单元和门控机制,可以有效地捕捉长期依赖关系。

    • 基于门控的循环神经网络(Gated RNN)
      • 门控循环单元(GRU)
        • 门控循环单元(GRU)具有比传统循环神经网络更少的门控单元,因此参数更少,计算效率更高。GRU通过重置门更新门来控制信息的流动,从而改善了传统循环神经网络中的长期依赖问题。
      • 长短期记忆网络(LSTM)
        • 长短期记忆网络(LSTM)是另一种常用的门控循环神经网络结构。LSTM引入了记忆单元输入门输出门以及遗忘门等门控机制,通过这些门控机制可以选择性地记忆、遗忘和输出信息,有效地处理长期依赖和梯度问题。
    • LSTM示意图

    二、实验环境

            本系列实验使用了PyTorch深度学习框架,相关操作如下:

    1. 配置虚拟环境

    conda create -n DL python=3.7 
    conda activate DL
    pip install torch==1.8.1+cu102 torchvision==0.9.1+cu102 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html
    
    conda install matplotlib
     conda install scikit-learn

    2. 库版本介绍

    软件包本实验版本目前最新版
    matplotlib3.5.33.8.0
    numpy1.21.61.26.0
    python3.7.16
    scikit-learn0.22.11.3.0
    torch1.8.1+cu1022.0.1
    torchaudio0.8.12.0.2
    torchvision0.9.1+cu1020.15.2

    三、实验内容

    导入必要的工具包

    1. import torch
    2. from torch import nn
    3. import torch.nn.functional as F
    4. from d2l import torch as d2l
    5. import math

    (一)自定义LSTM类

            循环神经网络(RNN)是一种经典的神经网络架构,用于处理序列数据,其中每个输入都与先前的信息相关。长短期记忆网络(LSTM)是RNN的一种特殊类型,它通过引入记忆单元和门控机制来解决传统RNN中的梯度消失和梯度爆炸问题。

            LSTM的关键思想是通过门控单元来控制信息的流动和存储。它由三个主要的门组成,分别是输入门(input gate)、遗忘门(forget gate)和输出门(output gate)。这些门通过学习参数来决定是否传递、遗忘或输出信息,从而使LSTM能够更好地处理长期依赖关系。

    在LSTM中,记忆单元(memory cell)是网络的核心组件。记忆单元类似于存储单元,可以存储和读取信息。它通过遗忘门来决定要删除哪些信息,通过输入门来决定要添加哪些新信息,并通过输出门来决定要输出哪些信息。

            LSTM模型的训练过程通常使用反向传播算法和梯度下降优化器来最小化损失函数。在自然语言处理(NLP)任务中,LSTM广泛应用于语言建模、机器翻译、情感分析等领域,因为它能够有效地捕捉和利用文本序列中的上下文信息。

    class LSTM(nn.Module):

    1.__init__(初始化

    1. def __init__(self, input_size, hidden_size):
    2. super(LSTM, self).__init__()
    3. self.input_size = input_size
    4. self.hidden_size = hidden_size
    5. # 初始化模型,即各个门的计算参数
    6. self.W_i = nn.Parameter(torch.randn(input_size, hidden_size))
    7. self.W_f = nn.Parameter(torch.randn(input_size, hidden_size))
    8. self.W_o = nn.Parameter(torch.randn(input_size, hidden_size))
    9. self.W_a = nn.Parameter(torch.randn(input_size, hidden_size))
    10. self.U_i = nn.Parameter(torch.randn(hidden_size, hidden_size))
    11. self.U_f = nn.Parameter(torch.randn(hidden_size, hidden_size))
    12. self.U_o = nn.Parameter(torch.randn(hidden_size, hidden_size))
    13. self.U_a = nn.Parameter(torch.randn(hidden_size, hidden_size))
    14. self.b_i = nn.Parameter(torch.randn(1, hidden_size))
    15. self.b_f = nn.Parameter(torch.randn(1, hidden_size))
    16. self.b_o = nn.Parameter(torch.randn(1, hidden_size))
    17. self.b_a = nn.Parameter(torch.randn(1, hidden_size))
    18. self.W_h = nn.Parameter(torch.randn(hidden_size, hidden_size))
    19. self.b_h = nn.Parameter(torch.randn(1, hidden_size))
    • 定义LSTM模型的各个参数以及参数的计算方式:

      • input_size表示输入的特征维度;
      • hidden_size表示隐藏状态的维度;
      • 一系列可学习的参数,用于定义LSTM的各个门和计算
        • W_iW_fW_oW_a: 输入到隐藏状态的权重矩阵,形状为(input_size, hidden_size)
        • U_iU_fU_oU_a: 隐藏状态到隐藏状态的权重矩阵,形状为(hidden_size, hidden_size)
        • b_ib_fb_ob_a: 各个门的偏置项,形状为(1, hidden_size)
        • W_hb_h: 隐藏状态到输出的权重矩阵和偏置项,用于计算最终的输出,形状分别为(hidden_size, hidden_size)(1, hidden_size)

    2.init_state(初始化隐藏状态

    1. def init_state(self, batch_size):
    2. hidden_state = torch.zeros(batch_size, self.hidden_size)
    3. cell_state = torch.zeros(batch_size, self.hidden_size)
    4. return hidden_state, cell_state
    • 接收batch_size参数,返回一个大小为(batch_size, hidden_size)的全零张量作为隐藏状态和细胞状态的初始值。

    • 前向传和细胞状态的初始值。

    3.forward(前向传播)

    1. def forward(self, inputs, states=None):
    2. batch_size, seq_len, input_size = inputs.shape
    3. if states is None:
    4. states = self.init_state(batch_size)
    5. hidden_state, cell_state = states
    6. outputs = []
    7. for step in range(seq_len):
    8. inputs_step = inputs[:, step, :]
    9. i_gate = torch.sigmoid(torch.mm(inputs_step, self.W_i) + torch.mm(hidden_state, self.U_i) + self.b_i)
    10. f_gate = torch.sigmoid(torch.mm(inputs_step, self.W_f) + torch.mm(hidden_state, self.U_f) + self.b_f)
    11. o_gate = torch.sigmoid(torch.mm(inputs_step, self.W_o) + torch.mm(hidden_state, self.U_o) + self.b_o)
    12. c_tilde = torch.tanh(torch.mm(inputs_step, self.W_a) + torch.mm(hidden_state, self.U_a) + self.b_a)
    13. cell_state = f_gate * cell_state + i_gate * c_tilde
    14. hidden_state = o_gate * torch.tanh(cell_state)
    15. y = torch.mm(hidden_state, self.W_h) + self.b_h
    16. outputs.append(y)
    17. return torch.cat(outputs, dim=0), (hidden_state, cell_state)
    • 接收输入inputs和可选的初始状态states
      • inputs的形状为(batch_size, seq_len, input_size),表示一个批次的输入序列
      • 如果没有提供初始状态,则调用init_state函数初始化隐藏状态
    • 对于输入序列中的每一时间步:
      • 获取当前时间步的输入inputs_step,形状为(batch_size, input_size)
      • 根据输入、隐藏状态和模型参数计算输入门、遗忘门、输出门和细胞更新值:
        • i_gate表示输入门;
        • f_gate表示遗忘门;
        • o_gate表示输出门;
        • c_tilde表示细胞更新值;
      • 这些门和细胞更新值的计算都是基于输入、隐藏状态和模型参数的矩阵乘法和激活函数的组合;
      • 更新细胞状态和隐藏状态:
        • cell_state根据输入门、遗忘门和细胞更新值更新;
        • hidden_state根据输出门和细胞状态计算;
      • 计算当前时间步的输出y,形状为(batch_size, hidden_size),通过对隐藏状态应用线性变换得到;
      • 将输出y添加到outputs列表中;
    • 返回所有时间步的输出outputs拼接的结果,形状为(batch_size * seq_len, hidden_size),以及最后一个时间步的隐藏状态和细胞状态。

     (二)定义RNNModel类

    0. 基础RNN模型

    参照前文;

    【深度学习实验】循环神经网络(二):使用循环神经网络(RNN)模型进行序列数据的预测-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_63834988/article/details/133778440

            将LSTM作为RNN层,和一个线性层封装为RNNModel类,用于完成从输入到预测词典中的词的映射,并实现了初始化隐状态的函数。

    class RNNModel(nn.Module):

    1.__init__(初始化

    1. def __init__(self, rnn_layer, vocab_size, **kwargs):
    2. super(RNNModel, self).__init__(**kwargs)
    3. self.rnn = rnn_layer
    4. self.vocab_size = vocab_size
    5. self.num_hiddens = self.rnn.hidden_size
    6. self.num_directions = 1
    7. self.linear = nn.Linear(self.num_hiddens, self.vocab_size)
    • 初始化函数接收三个参数
      • rnn_layer表示RNN层的类型
      • vocab_size表示词表大小
      • **kwargs表示可变数量的关键字参数
    • 首先调用父类nn.Module的初始化函数,然后将传入的rnn_layer赋值给self.rnn,将vocab_size赋值给self.vocab_size
    • 根据rnn_layer的隐藏状态大小,将其赋值给self.num_hiddens
    • self.num_directions表示RNN层的方向数,默认为1。
    • 最后,创建一个全连接层self.linear,该层的输入大小为self.num_hiddens,输出大小为self.vocab_size

    2.forward(前向传播)

    1. def forward(self, inputs, state):
    2. X = F.one_hot(inputs.T.long(), self.vocab_size)
    3. X = X.to(torch.float32)
    4. Y, state = self.rnn(X, state)
    5. output = self.linear(Y.reshape((-1, Y.shape[-1])))
    6. return output, state
    • 前向传播函数接收两个参数
      • inputs表示输入数据
      • state表示隐藏状态
    • 使用F.one_hot函数将inputs转换为独热编码表示的张量X
      • 其中inputs.T.long()将输入进行转置并转换为整型。
    • X转换为torch.float32数据类型
    • Xstate传入RNN层self.rnn进行计算,得到输出Y和更新后的隐藏状态state
    • 接下来,通过将Y的形状改变为(-1, Y.shape[-1]),将其展平,并将其输入到全连接层self.linear中,得到最终的输出output

    3.begin_state(初始化隐藏状态)

    1. def begin_state(self, device, batch_size=1):
    2. if not isinstance(self.rnn, nn.LSTM):
    3. return torch.zeros((self.num_directions * self.rnn.num_layers,
    4. batch_size, self.num_hiddens),
    5. device=device)
    6. else:
    7. return (torch.zeros((
    8. self.num_directions * self.rnn.num_layers,
    9. batch_size, self.num_hiddens), device=device),
    10. torch.zeros((
    11. self.num_directions * self.rnn.num_layers,
    12. batch_size, self.num_hiddens), device=device))
    • 两个参数
      • device表示计算设备
      • batch_size表示批量大小,默认为1。
    • 判断RNN层的类型是否为nn.LSTM
      • 如果不是,则返回一个形状为(num_directions * num_layers, batch_size, num_hiddens)的全零张量作为初始隐藏状态。
      • 如果是nn.LSTM类型,则返回一个由两个元组组成的元组,每个元组包含一个形状为(num_directions * num_layers, batch_size, num_hiddens)的全零张量,用作LSTM层的初始隐藏状态。

    (三)代码整合

    1. # 导入必要的库
    2. import torch
    3. from torch import nn
    4. import torch.nn.functional as F
    5. from d2l import torch as d2l
    6. import math
    7. class LSTM(nn.Module):
    8. def __init__(self, input_size, hidden_size):
    9. super(LSTM, self).__init__()
    10. self.input_size = input_size
    11. self.hidden_size = hidden_size
    12. # 初始化模型,即各个门的计算参数
    13. self.W_i = nn.Parameter(torch.randn(input_size, hidden_size))
    14. self.W_f = nn.Parameter(torch.randn(input_size, hidden_size))
    15. self.W_o = nn.Parameter(torch.randn(input_size, hidden_size))
    16. self.W_a = nn.Parameter(torch.randn(input_size, hidden_size))
    17. self.U_i = nn.Parameter(torch.randn(hidden_size, hidden_size))
    18. self.U_f = nn.Parameter(torch.randn(hidden_size, hidden_size))
    19. self.U_o = nn.Parameter(torch.randn(hidden_size, hidden_size))
    20. self.U_a = nn.Parameter(torch.randn(hidden_size, hidden_size))
    21. self.b_i = nn.Parameter(torch.randn(1, hidden_size))
    22. self.b_f = nn.Parameter(torch.randn(1, hidden_size))
    23. self.b_o = nn.Parameter(torch.randn(1, hidden_size))
    24. self.b_a = nn.Parameter(torch.randn(1, hidden_size))
    25. self.W_h = nn.Parameter(torch.randn(hidden_size, hidden_size))
    26. self.b_h = nn.Parameter(torch.randn(1, hidden_size))
    27. # 初始化隐藏状态
    28. def init_state(self, batch_size):
    29. hidden_state = torch.zeros(batch_size, self.hidden_size)
    30. cell_state = torch.zeros(batch_size, self.hidden_size)
    31. return hidden_state, cell_state
    32. def forward(self, inputs, states=None):
    33. batch_size, seq_len, input_size = inputs.shape
    34. if states is None:
    35. states = self.init_state(batch_size)
    36. hidden_state, cell_state = states
    37. outputs = []
    38. for step in range(seq_len):
    39. inputs_step = inputs[:, step, :]
    40. i_gate = torch.sigmoid(torch.mm(inputs_step, self.W_i) + torch.mm(hidden_state, self.U_i) + self.b_i)
    41. f_gate = torch.sigmoid(torch.mm(inputs_step, self.W_f) + torch.mm(hidden_state, self.U_f) + self.b_f)
    42. o_gate = torch.sigmoid(torch.mm(inputs_step, self.W_o) + torch.mm(hidden_state, self.U_o) + self.b_o)
    43. c_tilde = torch.tanh(torch.mm(inputs_step, self.W_a) + torch.mm(hidden_state, self.U_a) + self.b_a)
    44. cell_state = f_gate * cell_state + i_gate * c_tilde
    45. hidden_state = o_gate * torch.tanh(cell_state)
    46. y = torch.mm(hidden_state, self.W_h) + self.b_h
    47. outputs.append(y)
    48. return torch.cat(outputs, dim=0), (hidden_state, cell_state)
    49. class RNNModel(nn.Module):
    50. def __init__(self, rnn_layer, vocab_size, **kwargs):
    51. super(RNNModel, self).__init__(**kwargs)
    52. self.rnn = rnn_layer
    53. self.vocab_size = vocab_size
    54. self.num_hiddens = self.rnn.hidden_size
    55. self.num_directions = 1
    56. self.linear = nn.Linear(self.num_hiddens, self.vocab_size)
    57. def forward(self, inputs, state):
    58. X = F.one_hot(inputs.T.long(), self.vocab_size)
    59. X = X.to(torch.float32)
    60. Y, state = self.rnn(X, state)
    61. # 全连接层首先将Y的形状改为(时间步数*批量大小,隐藏单元数)
    62. # 它的输出形状是(时间步数*批量大小,词表大小)。
    63. output = self.linear(Y.reshape((-1, Y.shape[-1])))
    64. return output, state
    65. # 在第一个时间步,需要初始化一个隐藏状态,由此函数实现
    66. def begin_state(self, device, batch_size=1):
    67. if not isinstance(self.rnn, nn.LSTM):
    68. # nn.GRU以张量作为隐状态
    69. return torch.zeros((self.num_directions * self.rnn.num_layers,
    70. batch_size, self.num_hiddens),
    71. device=device)
    72. else:
    73. # nn.LSTM以元组作为隐状态
    74. return (torch.zeros((
    75. self.num_directions * self.rnn.num_layers,
    76. batch_size, self.num_hiddens), device=device),
    77. torch.zeros((
    78. self.num_directions * self.rnn.num_layers,
    79. batch_size, self.num_hiddens), device=device))

  • 相关阅读:
    接口自动化测试
    第2次作业
    Python实现Token过期自动刷新并重试原请求
    【科技素养】蓝桥杯STEMA 科技素养组模拟练习试卷D
    用于数据增强的十个Python库
    前端ES6-ES11新特性
    宏定义实现二进制数的奇偶位交换
    【每日一题Day360】LC1465切割后面积最大的蛋糕 | 贪心
    Javase | StringBuffer、StringBuilder
    JAVAEE初阶相关内容第十三弹--文件操作 IO
  • 原文地址:https://blog.csdn.net/m0_63834988/article/details/133864731