深度学习 《RNN模型》

前言:
前几篇博文里面我们学习了传统的BP神经网络,你可以称为她是全连接的网络,也可以称之为DNN(denisty nextwork),也学习了卷积神经网络,在卷积神经网络里面还学习了池化等结构,并且在pytorch 上都拿MNIST数据集做了测试,总体感觉提取了特征后的CNN确实要比普通的神经网络效果要好一些。今天我们学习另外一种网络结构RNN,它具有什么特征结构呢?我们来一起学习下,只能说网络种类太多了。

一:RNN的介绍
之前我们学习的BP,CNN都具有一个共同的特点就是,每时每刻的输出都是且仅仅与当前的输入是相关的,不会与之前或者之后某个时刻联合作用,不过对于物体识别,比如字符识别,图像识别这类的没有事件维度上先后顺序的数据来说(独立事件的数据),是完全足够了的,但是对于像语音,文本,视频等一些与时间前后也有联系的数据来说就表现的不那么好,因为,很多事件的数据同样也是受之前或者之后的事件的影响的,比如语言的上下文就是个典型的例子。语言序列的表达,词的含义,就是会受到之前和之后语言环境的影响。

所以呢,RNN就应运而生,它的输入不仅仅考虑了当前的数据输入,还考虑了前面时刻的输入,共同来作用当前时刻的输出,而且当前的时刻的输出不仅仅输出,还可以提供给下一个时刻进行继续参与作用下一时刻的输出,这样的结构使得网络具有了一定的记忆功能。

因此RNN可以被用在,自然语言处理,机器翻译,语音识别,文本相似度计算,视频/隐约推荐等领域。

二:单层RNN的网络结构
这里我们用图示将其画出来,它的结构在这里插入图片描述
相对简单,就是由输出层,隐藏层,输出层组成。这里我先用单层RNN进行描述,请记住这是一个层的RNN,也成为一个RNN Cell。
在这里插入图片描述

上图是折叠后的结构,循环的意思也就是来自于此

上图是拆开后结构,这个结构在计算的时候比较清晰。

结构都是一样的,展开后,也就是说所有的参数都是一样的。
我们先写出数学计算公式,也就是前向传播的计算公式,
在这里插入图片描述

其中f和g都是激活函数,f一般会选择tanh函数,relu函数,sigmoid函数等激活函数,g通常是softmax函数也可以是其他。

我们需要注意的是,在一次迭代中,也就是所有时刻,这一层的所有权重参数都是一样的,因此图中UVW没有上下标,都是一样的。

接下来我们给出反向传播的计算:我们每一时刻都有一个输出O_t,那么损失函数可以是使用softmax交叉熵损失函数或者是均方差损失函数。每个输出都对应有一个误差E_t,即总误差为:
在这里插入图片描述

我们的任务就是通过反向传播来更新求U,V,W。那么依然使用梯度下降来更新吧,即需要求出E对U,V,W的偏导。
在这里插入图片描述

1)我们先求对V的偏导吧,因为V只存在于输出层,仅仅与O_t存在函数关系,根据链式法则可得。
在这里插入图片描述

2)我们再对W求偏导。根据求导链式法则。
在这里插入图片描述
在这里插入图片描述
3)我们再对U求偏导,这里跟W其实是类似的,过程是基本一致的,所以就不多说了。
以上求偏导都是思路和大致步骤,没有给出具体的更加细致的数值计算,有兴趣的同学可以自行推导,这个不难的。

三:多层RNN的网络结构

在这里插入图片描述

这图是三层RNN神经网络,展开后如下:
在这里插入图片描述

上图是展开后的多层RNN的示意图,上标代表层数,下标代表时刻序列,请记住,每一层的参数都是一样的,一般为了简单起见,中间层的输出点,也就是中间层的输出H直接输入到下一层当做输入,中间层暂时不做g函数激活运算,所以稍微变一下就是如下图:
在这里插入图片描述

此图的前向传播很明显,由于每一层的参数都是一样的,不同层的参数不一样,推导过程就不多说了,至于反向传播,只要写出了前向传播的公式,确实比单层RNN要复杂一些,篇幅有限,这里也不写了,有兴趣同学们可以自行推导啊。

四:其他RNN的结构
上述我们画的是many-to-many型的RNN,也就是多个输入的数量对应于多个输出的数量,那自然而然也会想到输出数量不等于输出数量的情况,如下图
在这里插入图片描述

没错,此图也是截图自男神Andrew Ng的深度学习视频
这里是从输入输出数量的角度来分类不同的类型,它们可能有不同的用处,这里不细讲了,感兴趣的同学可以参考
Andrew 的视频或者其他博文资料,比如:
https://www.cnblogs.com/jimlau/p/13391767.html

下一博文我们将看看其他不同结构的RNN,比如双向RNN和优化后的LSTM,以及GRU,种类还有很多很多,在后面的博文便不再细说了,只会挑几个学习,具体的种类可以参考该博文:
https://blog.csdn.net/qq_35082030/article/details/73368962

五:Pytorch 实践。
最后一章节我门一起学下怎么在Pytorch上实现一个RNN。
实例一:使用RNNCell类操作前向传播
这里就是简单写了一个前向传播的例子,就是看看怎么使用,注意一些关键维度的设置,其他的没有什么的。

import torch

batch_size = 1  # 批量数据的大小,数据的批次
seqence_len = 3 # 一共是多少个时间序列,是指时间的维度。
input_size = 4  # 某一时刻下,输入的X的维度向量,比如这里的4指的是维度是[4, 1]
hidden_size = 2 # 每一层的激活后 h 的维度,比如这里的2指的是维度是[2, 1]

# 默认的激活函数是tanh
cell = torch.nn.RNNCell(input_size=input_size, hidden_size=hidden_size)

# 自己随便定义的数据,只是为了观察一下前向输出
dataset = torch.randn(seqence_len, batch_size, input_size)
# 初始化h0是全0向量
hidden = torch.zeros(batch_size, hidden_size)
# 每一个时间序列需要自己手动写迭代
for idx, inuput_data in enumerate(dataset):
    print('*' * 20, idx, '*' * 20)
    print('Input size', inuput_data.shape)
    hidden = cell(inuput_data, hidden)
    print('Output size', hidden.shape)
    print(hidden)

有如下输出:
在这里插入图片描述

某一个时刻,这里的输入数据是一个批量大小,也就是1,每个批量下数据是4维的,隐藏层也是一个批量的,每个批量下数据是2维的,和输出output是一致的。
上述实例得到:
Input.shape = (batch_size, input_size)
H_0.shape = (batch_size, hidden_size)

Output.shape = (batch_size, hidden_size)
H_N.shape = (batch_size, hidden_size)

RNNCell在每个时间序列下的hidden值都是自己写循环计算。

=====================================

实例二:使用RNN类操作前向传播

上个例子使用的是RNNCell实现的一个前向传播,我们会看到每一个时间序列hidden的数值,都是我们手写的循环,从一个时间序列到下一个时间序列,太麻烦了,于是我们可以使用RNN类来重写一遍,代码如下。

import torch

batch_size = 1  # 批量数据的大小,数据的批次
seqence_len = 3 # 一共是多少个时间序列,是指时间的维度。
input_size = 4  # 某一时刻下,输入的X的维度向量,比如这里的4指的是维度是[4, 1]
hidden_size = 2 # 每一层的激活后 h 的维度,比如这里的2指的是维度是[2, 1]
num_layers = 5   # RNN的层数

# 默认的激活函数是tanh
cell = torch.nn.RNN(input_size=input_size, hidden_size=hidden_size, num_layers=num_layers)

# 自己随便定义的数据,只是为了观察一下前向输出
dataset = torch.randn(seqence_len, batch_size, input_size)
# 初始化h0是全0向量
hidden = torch.zeros(num_layers, batch_size, hidden_size)


# 前向传播,不用自己写循环
out, hidden = cell(dataset, hidden)

print('Output size', out.shape)
print('Output ', out)
print('Hidden size', hidden.shape)
print('Hidden ', hidden)

有如下输出:
在这里插入图片描述

Output Size是3个序列,1个批量,2个维度的值
最后前向传播结束后,最后输出的hidden的是5层,1个批量,2个维度值。需要注意的是,这里需要指定RNN的层数。
上述实例得到:
Input.shape = (sequence_len, batch_size, input_size)
H_0.shape = (num_layers, batch_size, hidden_size)

Output.shape = (sequence_len, batch_size, hidden_size)
H_N.shape = (num_layers, batch_size, hidden_size)

切记后面还有很多种RNN的变种结构,上面也给出了链接,能让我们看到很多不同的RNN结构,详细资料请自行查询了,篇幅实在有限。

猜你喜欢

转载自blog.csdn.net/qq_29367075/article/details/108946064