当前位置: 代码迷 >> 综合 >> Pytorch ----- 循环神经网络 RNN --基础部分(RNN Cell) 附代码解读~~学习笔记
  详细解决方案

Pytorch ----- 循环神经网络 RNN --基础部分(RNN Cell) 附代码解读~~学习笔记

热度:36   发布时间:2023-12-26 11:11:53.0

前两天学的卷积神经网络,今天学循环神经网络 RNN。

之前在用卷积神经网络的是,一张图片上像素做卷积,所用到的卷积核明确且一整张都用这个卷积核(权重共享)。所以用到的权重少,而全连接层,每一个点对应下一个节点都有一个权重,所以整个网络,全链接层运算量占比大。

RNN,专门处理带有 序列模式 的数据(数据前后有序 ,或者本项数据需要前一项数据特征),且采用了共享权重来减少运算量。

RNN cell:

其中 下面的黄色框的 x1 x2 x3 x4 表示 1 2 3 4 时刻x的输入。
上面蓝色框的 h1 h2 h3 h4 表示相应的输出 也叫hidden。

而中间的 RNN Cell相当于一个Linear线性层。
且前面的h0表示前一层的特征信息。

每一层的RNN Cell都是让输入进来的X和上一层输出的h做运算然后给到下一层,当作下一层的h。

在这里插入图片描述
这么多的RNN Cell可以表示为下图:
即他们都使用同一个线性层。
在这里插入图片描述
在RNN中最常用的激活函数是 tanh 就是sin cos 的那个tan。

自行创建RNN Cell:
参数 输入维度、隐层维度。
在这里插入图片描述

其中的张量维度关系:

input输入维度: batch ,input_size batch 批量样本N,和输入维度x (N * x)

hidden 隐层维度 : 同样的 batch 和hidden_size 。(N * h)

hidden 输出的隐层维度: 同样的batch 和hidden_size 。(N * h)

显然,因为要让输入数据X和上一层输出的H做加法,所以输入的数据X的维度要和H的维度一致,输入的维度X做一个线性变换变换成和H一个维度。

x做线性变换维度就是 输入的维度 * 隐层的维度(设此时batch为1.),即 inputsize * hidensize

看一个例子:
假设现在的数据:

batchsize = 1
seqlen = 3 也就是 x1 x2 x3 三个元素
inputsize = 4 向量中四个元素
hidden = 2 向量中两个元素

则 input.shape = (1,4) . output.shape = (1,2)
整个的tensor维度 (seqlen,batchsize,inputsize) = (3,1,4)

例子代码:

import torchbatch_size = 1
seq_len = 3
input_size = 4
hidden_size = 2
cell = torch.nn.RNNCell(input_size=input_size, hidden_size=hidden_size)
dataset = torch.randn(seq_len, batch_size, input_size)
hidden = torch.zeros(batch_size, hidden_size)  # h0
for idx, input in enumerate(dataset):print('=' * 20, idx, '=' * 20)print('输入维度', input.shape)hidden = cell(input, hidden)  # 这次的输入加 上次隐层的输出print('输出维度', hidden.shape)print(hidden)得到结果:
==================== 0 ====================
输入维度 torch.Size([1, 4])
输出维度 torch.Size([1, 2])
tensor([[0.1037, 0.7474]], grad_fn=<TanhBackward0>)
==================== 1 ====================
输入维度 torch.Size([1, 4])
输出维度 torch.Size([1, 2])
tensor([[0.4714, 0.4577]], grad_fn=<TanhBackward0>)
==================== 2 ====================
输入维度 torch.Size([1, 4])
输出维度 torch.Size([1, 2])
tensor([[0.5499, 0.4984]], grad_fn=<TanhBackward0>)

直接使用RNN:
参数 : 输入维度,隐层维度,RNN有多少层
在这里插入图片描述
下图,调用时候的参数传入, inputs就是整个的输入数据X。
输入的hidden就是 h0。
输出的两个张量,out 就是里面的h1,h2…
输出的hiden就是整个循环完输出的那个最后一个hn。

其中输入维度要求:
input : (seqsize,batch,inputsize)
hidden: (numlayers,batch,hiddensize)
输出维度:
output: (seqsize,batch,hiddensize)
hidden: (numlayers,batch,hiddensize)

在这里插入图片描述

像下图这样,就是有三层RNN,则 numlayers = 3.
注意其中的隐层h数值的传播,不仅从左到右,还有从下到上的传播。
输入就是左边的h (相当于上面说的h0)和最下面一行的x。

最后的输出,最上面哪一行h就是output。最右边这一列就是hidden。

在这里插入图片描述

练习

现在要将hello变成ohlol。

在这里插入图片描述

首先使用 RNN Cell:

则下图黄色输入的每一个对应 hellow,上面的蓝色输出则对应这ohlol。
在这里插入图片描述
因为程序需要向量,所以先将字母变成向量。
将字母对应到相应的数字代表(字典),然后hello就变成了 10223 如下图。

在这里插入图片描述
然后再将其变成向量:
如下图,很显然能看出来。 1,则用这一行的下标1填写成1表示,其余为0。

向量矩阵宽度就是 字典中有多少个字母。从0开始排嘛。
最后面的那个矩阵叫 独热向量,(one-hot vectors)
显然 下图中的 矩阵 inputsize = 4
在这里插入图片描述

代码数据准备:
如下图:

在这里插入图片描述
其中 X_one_hot 表示 遍历初始的x数据,拿出来每一个x去上面定义的矩阵中找对应的行,比如x进来的是1,则去上面的矩阵中拿走第一行,这样就是让一个数变成了一个向量供后面使用。

构造的查询矩阵维度 : inputsize * seqsize。

由于input的输入维度要求 (seqsize,batchsize,inputsize)。所以 变化一下维度。
labels 同样变化维度

模型类设计
在这里插入图片描述
其中 def init_hidden 是工具函数,用于设置初始的那个h0。

然后损失函数和优化器:
Adam 基于梯度下降的优化器 改进版。
在这里插入图片描述
开始训练:
训练之前别忘了归零优化器的梯度。
net.init_hidden() 初始化hidden 就是算那个h0
这里的loss并没有用item() 因为这里算出来的是RNN Cell中的一个,是需要构造计算图的,全部相加之后才是最终的loss。最后取值的时候 依旧需要 loss.item()

在这里插入图片描述
需要注意的是 这其中的维度:
inputs 维度前面说了 (seqsize,batchsize,inputsize)
而input维度是 (batchsize,inputsize)
labels维度 (seqsize,1)
label维度 (1)
在这里插入图片描述

下面使用RNN实现

数据集准备
在这里插入图片描述

模型类设计;

在这里插入图片描述

训练类:
在这里插入图片描述

这里的 inputs维度 (seqsize,batchsize,inputsize)
输出维度 outputs (seqsize,batchsize,hiddensize)
在这里插入图片描述

关于独热向量, 是有一些缺点的,里面的向量比较稀疏,数据量大的时候,维度非常高。其中字典的对应关系是硬编码。

改进的向量: Embedding vectors
下面的Embed表示。将独热向量变成稠密的表示,然后RNN Cell,然后加一个线性层,作用: 将隐层和输出的分类调整到 一致

在这里插入图片描述