从网络架构方面简析循环神经网络RNN
一、前言
1.1 诞生原因
在普通的前馈神经网络(如多层感知机MLP,卷积神经网络CNN)中,每次的输入都是独立的,即网络的输出依赖且仅依赖于当前输入,与过去一段时间内网络的输出无关。但是在现实生活中,许多系统的输出不仅依赖于当前输入,还与过去一段时间内系统的输出有关,即需要网络保留一定的记忆功能,这就给前馈神经网络提出了巨大的挑战。除此之外,前馈神经网络难以处理时序数据,比如视频、语音等,因为时序数据的序列长度一般是不固定的,而前馈神经网络要求输入、输出的维度都是固定的,不能任意改变。出于这两方面的需求,循环神经网络RNN应运而生。
1.2 简介
循环神经网络(Recurrent Neural Network,RNN)是一类具有短期记忆能力的神经网络。在循环神经网络中,神经元既可以如同前馈神经网络中神经元那般从其他神经元那里接受信息,也可以接收自身以前的信息。且和前馈神经网络相比,循环神经网络更加符合生物神经网络的结构。
1.3 与前馈神经网络的差异
就功能层面和学习性质而言,循环神经网络也有异于前馈神经网络。前馈神经网络多用于回归(Regression)和分类(Classification),属于监督学习(Supervised learning);而循环神经网络多用于回归(Regression)和生成(Generation),属于无监督学习(Unsupervised learning)。
二、网络架构

上图所示为RNN的一个神经元模型。给定一个序列长度为T的输入序列
,在t时刻将该序列中的第t个元素xt送进网络,网络会结合本次输入xt及上一次的 “ 记忆 ” ht-1,通过一个非线性激活函数f()(该函数通常为tanh或relu),产生一个中间值ht(学名为隐状态,Hidden States),该值即为本次要保留的记忆。本次网络的输出为ht的线形变换,即g(ht),其中g()为简单的线性函数。
由于循环神经网络具有时序性,因此其网络架构可以从空间和时间两方面进行了解。
为方便理解,特以此情景举例:假设目前正在训练一个RNN,用于生成视频。训练用的train_data为1000段等长度的视频,划分的batch_size为10,即每次送给网络10段视频。假设每段视频都有50帧,每帧的分辨率为2X2。在此训练过程中,不需要外界给label,将某帧图片input进RNN时,下一帧图片即为label。
2.1 空间角度
在本例中,一个序列长度为T的输入序列即一段50帧视频,其中的x1,x2,x3...x50分别对应着第一帧图片、第二帧图片、第三帧图片......到第五十帧图片。随着时间的推移,依次将每帧图片送进网络,在每个时刻,只送进一帧图片,同时生成一帧图片。该网络架构如下图所示。就如普通的前馈神经网络一般,除了输入输出层的维度固定外,隐藏层的维度及层数都是可以自主设计的。这里假设只有一个隐藏层,该层有5个神经元。需要注意的是,每个具有记忆功能的神经元中所存储的隐状态h,不仅参与本神经元的下次输入,还同时参与本层其他具有记忆功能神经元的下次输入。

2.2 时间角度
从空间角度观察整个网络,可将网络视为1个4X5X4的循环神经网络RNN,其中隐藏层的5个神经元是具有记忆功能的;从时间角度展开整个网络,可将网络视为50个4X5X4的多层感知机MLP,每个MLP隐层神经元不仅向该MLP的下一层输出,同时还向下一个MLP中与之层数对应的所有神经元输出(下图为求清晰表示,将其化简为一对一,但实质上是一对多),该输出即为隐状态h。由于隐状态需要在MLP之间从前向后传递,因此这50个MLP只能依次运算,不能并行运算。

循环神经网络独特的神经元结构赋予其记忆功能,但有得有失,该结构也造成其在处理时序数据时不能进行并行运算。目前推动算法进步的一大助力就是算力,而RNN却无法充分利用算力,这也是一部分人不太看好其前景的原因。
三、pytorch demo 实现
对一段正弦函数进行离散采样作为输入,利用RNN生成滞后的正弦函数采样值。实现环境:Colab。
引入必要头文件。
import torch.nn as nn
import torch
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
采样获取input及label,并可视化。
# 制定画布大小
plt.figure(figsize=(8, 5)) # 每个batch中数据个数
num = 20 # 生成数据
time_steps = np.linspace(0, np.pi, num+1)
data = np.sin(time_steps)
data = data.reshape((num+1, 1)) x = data[0:num, :] # 除了最后一个数据外的所有其他数据
y = data[1:num+1, :] # 除了第一个数据外的所有其他数据 # 可视化数据
plt.plot(time_steps[1:num+1], x, 'r.', label='input_x')
plt.plot(time_steps[1:num+1], y, 'b.', label='output_y') plt.legend(loc='best')
plt.show()

通过torch.nn.RNN及torch.nn.fc自定义网络模型。
# 自定义网络
class myRNN(nn.Module):
def __init__(self, input_size, output_size, hidden_dim, n_layers):
super(myRNN, self).__init__()
self.hidden_dim = hidden_dim # 隐藏层节点个数
self.rnn = nn.RNN(input_size, hidden_dim, n_layers, batch_first=True) # rnn层
self.fc = nn.Linear(hidden_dim, output_size) # 全连接层 def forward(self, x, hidden):
batch_size = x.shape[0]
# 生成预测值和隐状态,预测值传向下一层,隐状态作为记忆参与下一次输入
r_out, hidden = self.rnn(x, hidden)
r_out = r_out.view(-1, self.hidden_dim)
output = self.fc(r_out) return output, hidden
实例化模型,并指定超参数、损失函数和优化器。
# 指定超参数
input_size = 1
output_size = 1
hidden_dim = 32
n_layers = 1 # 实例化模型
rnn = myRNN(input_size, output_size, hidden_dim, n_layers) # 指定损失函数和优化器,学习率设定为0.01
loss = nn.MSELoss()
optimizer = torch.optim.Adam(rnn.parameters(), lr=0.01)
定义训练并打印输出的函数。
def train(rnn, n_steps, print_every):
# 记忆初始化
hidden = None
loss_list = []
for batch_i,step in enumerate(range(n_steps)):
optimizer.zero_grad() # 梯度清零
# 生成训练数据
time_steps = np.linspace(step*np.pi, (step+1)*np.pi, num+1)
data = np.sin(time_steps)
data = data.reshape((num+1, 1))
x = data[0:num, :] # 除了最后一个数据外的所有其他数据
y = data[1:num+1, :] # 除了第一个数据外的所有其他数据
x_tensor = torch.from_numpy(x).unsqueeze(0).type('torch.FloatTensor')
y_tensor = torch.from_numpy(y).type('torch.FloatTensor')
prediction, hidden = rnn(x_tensor, hidden) # 生成预测值和隐状态
hidden = hidden.data
loss_rate = loss(prediction, y_tensor) # 计算损失
loss_rate.backward() # 误差反向传播
optimizer.step() # 梯度更新
loss_list.append(loss_rate)
if batch_i%print_every == 0:
plt.plot(time_steps[1:num+1], x, 'r.', label='input')
plt.plot(time_steps[1:num+1], prediction.data.numpy().flatten(), 'b.', label='predicte')
plt.show()
x = np.linspace(0, n_steps, n_steps)
plt.plot(x, loss_list, color='blue', linewidth=1.0, linestyle='-', label='loss')
plt.legend(loc='upper right')
plt.show()
return rnn
训练模型并打印输出。
n_steps = 100
print_every = 25
trained_rnn = train(rnn, n_steps, print_every)
从左到右、自上而下,分别是训练25、50、75和100次后的模型效果。





参考资料:
邱锡鹏老师《神经网络与深度学习》: https://nndl.github.io/
优达学城pytorch学习课程: https://github.com/udacity/deep-learning-v2-pytorch
慕课手记——RNN架构详解: http://www.imooc.com/article/details/id/31105
pytorch官方手册: https://pytorch.org/docs/stable/nn.html#recurrent-layers
从网络架构方面简析循环神经网络RNN的更多相关文章
- 通过keras例子理解LSTM 循环神经网络(RNN)
博文的翻译和实践: Understanding Stateful LSTM Recurrent Neural Networks in Python with Keras 正文 一个强大而流行的循环神经 ...
- 循环神经网络 RNN
随着科学技术的发展以及硬件计算能力的大幅提升,人工智能已经从几十年的幕后工作一下子跃入人们眼帘.人工智能的背后源自于大数据.高性能的硬件与优秀的算法的支持.2016年,深度学习已成为Google搜索的 ...
- 循环神经网络(RNN, Recurrent Neural Networks)介绍(转载)
循环神经网络(RNN, Recurrent Neural Networks)介绍 这篇文章很多内容是参考:http://www.wildml.com/2015/09/recurrent-neur ...
- 深度学习之循环神经网络RNN概述,双向LSTM实现字符识别
深度学习之循环神经网络RNN概述,双向LSTM实现字符识别 2. RNN概述 Recurrent Neural Network - 循环神经网络,最早出现在20世纪80年代,主要是用于时序数据的预测和 ...
- 循环神经网络RNN模型和长短时记忆系统LSTM
传统DNN或者CNN无法对时间序列上的变化进行建模,即当前的预测只跟当前的输入样本相关,无法建立在时间或者先后顺序上出现在当前样本之前或者之后的样本之间的联系.实际的很多场景中,样本出现的时间顺序非常 ...
- 循环神经网络(RNN, Recurrent Neural Networks)介绍
原文地址: http://blog.csdn.net/heyongluoyao8/article/details/48636251# 循环神经网络(RNN, Recurrent Neural Netw ...
- 循环神经网络RNN及LSTM
一.循环神经网络RNN RNN综述 https://juejin.im/entry/5b97e36cf265da0aa81be239 RNN中为什么要采用tanh而不是ReLu作为激活函数? htt ...
- 用纯Python实现循环神经网络RNN向前传播过程(吴恩达DeepLearning.ai作业)
Google TensorFlow程序员点赞的文章! 前言 目录: - 向量表示以及它的维度 - rnn cell - rnn 向前传播 重点关注: - 如何把数据向量化的,它们的维度是怎么来的 ...
- 循环神经网络(RNN)模型与前向反向传播算法
在前面我们讲到了DNN,以及DNN的特例CNN的模型和前向反向传播算法,这些算法都是前向反馈的,模型的输出和模型本身没有关联关系.今天我们就讨论另一类输出和模型间有反馈的神经网络:循环神经网络(Rec ...
随机推荐
- 推荐20个非常有帮助的web前端开发教程
1. CSS Vocabulary 一个伟大的指向和点击的小应用程序,让你加高速度掌握 CSS 语法的各个不同部分,学习各个属性的正确的名称. 2. Liquidapsive 一个简单的信息化布局.通 ...
- Mac下php版本不支持imagetfftext函数问题
brew rm freetype jpeg libpng gd zlib brew install freetype jpeg libpng gd zlib brew install php71 ht ...
- 登录令牌 Token 介绍
Token值介绍 token 值: 登录令牌.利用 token 值来判断用户的登录状态.类似于 MD5 加密之后的长字符串. 用户登录成功之后,在后端(服务器端)会根据用户信息生成一个唯一的值.这个 ...
- vue 组件与传值
一.表单输入绑定(v-model 指令) 可以用 v-model 指令在表单 <input>.<textarea> 及 <select> 元素上创建双向数据绑定. ...
- codeforces 112B Petya and Square
B. Petya and Square time limit per test 2 seconds memory limit per test 256 megabytes input standard ...
- 9.2 NOIP提高组试题精解(2)
9-18 fruit.c #include <stdio.h> #define MAXN 10000 int Queue1[MAXN], Queue2[MAXN]; void Insert ...
- 大数据之路- Hadoop环境搭建(Linux)
前期部署 1.JDK 2.上传HADOOP安装包 2.1官网:http://hadoop.apache.org/ 2.2下载hadoop-2.6.1的这个tar.gz文件,官网: https://ar ...
- PHP的Calling Scope(::调用非静态方法)
今天在群里发现有人说,PHP可以用::调用非静态方法,一致没这么试过,发现了鸟哥的blog写了这个问题的具体解释,就搬过来: 这个问题乍看, 确实很容易让人迷惑, 但实际上, 造成这样的误解的根本原因 ...
- 我所理解的RESTful Web API [Web标准篇]【转】
原文:http://www.cnblogs.com/artech/p/restful-web-api-01.html REST不是一个标准,而是一种软件应用架构风格.基于SOAP的Web服务采用RPC ...
- 第三届蓝桥杯决赛c++b组
1.星期几 [结果填空] (满分5分) 1949年的国庆节(10月1日)是星期六. 今年(2012)的国庆节是星期一. 那么,从建国到现在,有几次国庆节正好是星期日呢? 只要 ...