引言

  • 本文是运用pytorch对循环神经网络RNN(Recurrent Neural Network)的代码完成,作为之前介绍RNN原理的一个代码弥补。

  • RNN原理介绍

  • 本文代码相关介绍相对较为详细,也为自己的一个学习过程,对RNN的了解仍是比较粗浅,有过错的地方欢迎纠正。

简述RNN结构

  • 详细原理介绍能够参考上述链接,此处简述RNN结构实为便利了解后面代码剖析部分。
  • 单向循环神经网络
    • RNN的应用场景一般是当时输入与前一个输入是有联系的,所以下图x部分的参数会与X_t-1有关
      • x:数据输入
      • u:输入层到躲藏层的权重
      • s:躲藏层的输出成果
      • v:躲藏层到输出层的权重
      • w:上一次的值S_t-1作为这一次输入的权重矩阵
        深度学习5 -- 循环神经网络(代码实现篇+付详细流程文件)
      • 关于数学计算公式
        深度学习5 -- 循环神经网络(代码实现篇+付详细流程文件)
  • 双向循环神经网络
    • 单项循环神经网络只能作为与前一个数据树立衔接,双向循环神经网络则能够同后一个数据树立衔接
    • 当然这个躲藏层只要一层,也能够多加几层构成深度循环神经网络
      深度学习5 -- 循环神经网络(代码实现篇+付详细流程文件)
      深度学习5 -- 循环神经网络(代码实现篇+付详细流程文件)

RNN实例

  • 关于完成RNN的实例,我觉得有一个比较简略但是又比较符合RNN运用场景的序列数据比如,那便是正弦和余弦函数。
    • 该比如来自参考资料1
    • 以sin函数值作为输入,其对应的cos函数值作为输出,在相同sin值的状况下会对应不同的cos值的状况,这便是由于输出成果不只要看输入数据,还要依赖前后值的信息,且FC,CNN就不适合该比如了。
      深度学习5 -- 循环神经网络(代码实现篇+付详细流程文件)

Pytorch中RNN函数torch.nn.RNN()参数介绍

  • 参数其实主要写input_size和hidden_size,其他的参数运用默许的即可,当然有特殊需要再设置

    深度学习5 -- 循环神经网络(代码实现篇+付详细流程文件)

  • 其重要参数解析如下

    参数 意义
    input_size 输入RNN的维度/输入x的特征数量
    hidden_size 躲藏层节点数量/躲藏层的特征数量
    num_layers RNN的层数
    nonlinearity 指定激活函数运用tanh仍是relu。默许是tanh
    bias 假如是 False , 那么 RNN 层就不会运用偏置权重 b_ih 和 b_hh, 默许: True
    batch_first 假如 True, 那么输入 Tensor 的 shape 应该是 (batch, seq, feature),并且输出也是一样(详见参3)
    dropout 假如值非零, 那么除了终究一层外, 其它层的输出都会套上一个 dropout 层
    bidirectional 假如 True , 将会变成一个双向 RNN, 默许为 False
  • 主要是介绍了节点数的一些细节 参考资料2

  • 贴两个询问ChatGPT的截图,作为比对

    深度学习5 -- 循环神经网络(代码实现篇+付详细流程文件)
    深度学习5 -- 循环神经网络(代码实现篇+付详细流程文件)

  • 主要介绍了数据维度的一些细节,介绍的挺详细的,可惜我仍是没看懂,今后看懂了补上参考资料3

RNN代码剖析

  • 具体print输出成果以及打印出来的成果见文章结尾的测验文件资源)
  • 测验文件中还包含几个函数测验的成果,用来辅佐剖析代码
  • 注:个人剖析的注释或许也有过错,仅供参考,且数据维度的变换那里现在了解才能有限,思考了好久仍是一知半解,待后续有才能完全解析再做弥补。
  • 该代码是他人写好的代码,并不是本人的完成代码,主要是确实个人才能缺乏现在难以自己写出来……
# encoding:utf-8
import torch
import numpy as np
import matplotlib.pyplot as plt
from torch import nn
# 界说RNN模型(能够类别下方RNN简略测验代码了解)
class Rnn(nn.Module):
    def __init__(self, input_size):
        super(Rnn, self).__init__()
        # 界说RNN网络
        ## hidden_size是自己设置的,貌似取值都是32,64,128这样来取值
        ## num_layers是躲藏层数量,超越2层那便是深度循环神经网络了
        self.rnn = nn.RNN(
                input_size=input_size,
                hidden_size=32,
                num_layers=1,
                batch_first=True  # 输入形状为[批量巨细, 数据序列长度, 特征维度]
                )
        # 界说全衔接层
        self.out = nn.Linear(32, 1)
    # 界说前向传达函数
    def forward(self, x, h_0):
        r_out, h_n = self.rnn(x, h_0)
        # print("数据输出成果;躲藏层数据成果", r_out, h_n)
        # print("r_out.size(), h_n.size()", r_out.size(), h_n.size())
        outs = []
        # r_out.size=[1,10,32]即将一个长度为10的序列的每个元素都映射到躲藏层上
        for time in range(r_out.size(1)):  
            # print("映射", r_out[:, time, :])
            # 依次抽取序列中每个单词,将之通过全衔接层并输出.r_out[:, 0, :].size()=[1,32] -> [1,1]
            outs.append(self.out(r_out[:, time, :])) 
            # print("outs", outs)
        # stack函数在dim=1上叠加:10*[1,1] -> [1,10,1] 同时h_n已经被更新
        return torch.stack(outs, dim=1), h_n 
TIME_STEP = 10
INPUT_SIZE = 1
LR = 0.02
model = Rnn(INPUT_SIZE)
print(model)
# 此处运用的是均方误差损失
loss_func = nn.MSELoss()  
optimizer = torch.optim.Adam(model.parameters(), lr=LR)
h_state = None  # 初始化h_state为None
for step in range(300):
    # 人工生成输入和输出,输入x.size=[1,10,1],输出y.size=[1,10,1]
    start, end = step * np.pi, (step + 1)*np.pi
    # np.linspace生成一个指定巨细,指定数据区间的均匀分布序列,TIME_STEP是生成数量
    steps = np.linspace(start, end, TIME_STEP, dtype=np.float32) 
    # print("steps", steps)
    x_np = np.sin(steps)
    y_np = np.cos(steps)
    # print("x_np,y_np", x_np, y_np)
    # 从numpy.ndarray创建一个张量 np.newaxis增加新的维度
    x = torch.from_numpy(x_np[np.newaxis, :, np.newaxis])
    y = torch.from_numpy(y_np[np.newaxis, :, np.newaxis])
    # print("x,y", x,y)
    # 将x通过网络,长度为10的序列通过网络得到终究躲藏层状态h_state和长度为10的输出prediction:[1,10,1]
    prediction, h_state = model(x, h_state)
    h_state = h_state.data  
    # 这一步只取了h_state.data.由于h_state包含.data和.grad 放弃了梯度
    # print("precision, h_state.data", prediction, h_state)
    # print("prediction.size(), h_state.size()", prediction.size(), h_state.size())
    # 反向传达
    loss = loss_func(prediction, y)
    optimizer.zero_grad()
    loss.backward()
    # 更新优化器参数
    optimizer.step()
# 对终究一次的成果作图检查网络的猜测效果
plt.plot(steps, y_np.flatten(), 'r-')
plt.plot(steps, prediction.data.numpy().flatten(), 'b-')
plt.show()

另外一种构建RNN的办法

  • 除了上面介绍的torch.nn.RNN()外,还有RNNCell办法,运用办法如下:
  • cell = torch.nn.RNNCell(input_size=input_size, hidden_size=hidden_size)
  • hidden = cell(input, hidden)
    • input_size与hidden_size与上面的参数介绍的意义是共同的,但是输入类型不一样。
    • 和RNN不同的是RNN cell要自己写处理序列的循环,个人通俗了解便是,比如要处理3个语句,每个语句10个单词,每个单词用20长度的向量表示,假如运用nn.RNN(),那输出的tensor的shape应该是[10,3,20],而运用nn.RNNCell()需要将序列上的每个时刻分隔处理,即送入的tensor的shape是[3,100],然后将该单元运行10次,灵活的价值当然便是比较麻烦。
    • 关于hidden_size输入shape与input_size同样有改变,归纳来说便是(能够看上面代码的具体解析那有关于维度的详细剖析)
    • input:batch_size*input_size
    • 输入的hidden: batch_size*hidden_size
    • 输出的hidden: batch_size*hidden_size

其他参考资料

  • 数据集为MNIST运用RNN完成(未测验,找资料时分看到了仅供参考)

M1

M2

  • RNN两种完成方法的区别以及代码完成比较

RR1

RR2

  • 本文代码详细测验过程文件(待上传)
  • Github代码文件资源