一、前言

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的更多相关文章

  1. 通过keras例子理解LSTM 循环神经网络(RNN)

    博文的翻译和实践: Understanding Stateful LSTM Recurrent Neural Networks in Python with Keras 正文 一个强大而流行的循环神经 ...

  2. 循环神经网络 RNN

    随着科学技术的发展以及硬件计算能力的大幅提升,人工智能已经从几十年的幕后工作一下子跃入人们眼帘.人工智能的背后源自于大数据.高性能的硬件与优秀的算法的支持.2016年,深度学习已成为Google搜索的 ...

  3. 循环神经网络(RNN, Recurrent Neural Networks)介绍(转载)

    循环神经网络(RNN, Recurrent Neural Networks)介绍    这篇文章很多内容是参考:http://www.wildml.com/2015/09/recurrent-neur ...

  4. 深度学习之循环神经网络RNN概述,双向LSTM实现字符识别

    深度学习之循环神经网络RNN概述,双向LSTM实现字符识别 2. RNN概述 Recurrent Neural Network - 循环神经网络,最早出现在20世纪80年代,主要是用于时序数据的预测和 ...

  5. 循环神经网络RNN模型和长短时记忆系统LSTM

    传统DNN或者CNN无法对时间序列上的变化进行建模,即当前的预测只跟当前的输入样本相关,无法建立在时间或者先后顺序上出现在当前样本之前或者之后的样本之间的联系.实际的很多场景中,样本出现的时间顺序非常 ...

  6. 循环神经网络(RNN, Recurrent Neural Networks)介绍

    原文地址: http://blog.csdn.net/heyongluoyao8/article/details/48636251# 循环神经网络(RNN, Recurrent Neural Netw ...

  7. 循环神经网络RNN及LSTM

    一.循环神经网络RNN RNN综述 https://juejin.im/entry/5b97e36cf265da0aa81be239 RNN中为什么要采用tanh而不是ReLu作为激活函数?  htt ...

  8. 用纯Python实现循环神经网络RNN向前传播过程(吴恩达DeepLearning.ai作业)

    Google TensorFlow程序员点赞的文章!   前言 目录: - 向量表示以及它的维度 - rnn cell - rnn 向前传播 重点关注: - 如何把数据向量化的,它们的维度是怎么来的 ...

  9. 循环神经网络(RNN)模型与前向反向传播算法

    在前面我们讲到了DNN,以及DNN的特例CNN的模型和前向反向传播算法,这些算法都是前向反馈的,模型的输出和模型本身没有关联关系.今天我们就讨论另一类输出和模型间有反馈的神经网络:循环神经网络(Rec ...

随机推荐

  1. java读取TXT文件中的数据

    将文件放在一个指定的磁盘目录下: File file = new File("指定的文件路径"); try{ BufferedReader br = new BufferedRea ...

  2. API的理解和使用——哈希类型的命令

    哈希常用的命令复习 命令 功能 hset key field value 设置哈希值 hsetnx 设置哈希值,field或键必须不存在 hget 获取某个file对应的值 hdel 删除一个或多个f ...

  3. POJ - 1321 棋盘问题 【DFS】

    题目链接 http://poj.org/problem?id=1321 思路 和N皇后问题类似 但是有一点不同的是 这个是只需要摆放K个棋子就可以了 所以 我们要做好 两个出口 并且要持续往下一层找 ...

  4. Macaca,app-inspector安装

    1.安装brew 软件包管理工具:/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/inst ...

  5. 高通MSM8255 GPS 调试分析&&Android系统之Broadcom GPS 移植【转】

    本文转载自:http://blog.csdn.net/gabbzang/article/details/12063031 http://blog.csdn.NET/dwyane_zhang/artic ...

  6. POJ3450 Corporate Identity —— 后缀数组 最长公共子序列

    题目链接:https://vjudge.net/problem/POJ-3450 Corporate Identity Time Limit: 3000MS   Memory Limit: 65536 ...

  7. 架构设计:系统间通信(34)——被神化的ESB(上)

    1.概述 从本篇文章开始,我们将花一到两篇的篇幅介绍ESB(企业服务总线)技术的基本概念,为读者们理清多个和ESB技术有关名词.我们还将在其中为读者阐述什么情况下应该使用ESB技术.接下来,为了加深读 ...

  8. 关于Dubbo

    什么是Dubbo 一款分布式服务框架 高性能和透明化的RPC远程服务调用方案 SOA服务治理方案 每天为2千多个服务提供大于30亿次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点以及别的公司的业务 ...

  9. java入门了解05

    1.模板模式 (一)需求:解决默写事情有固定模式,但有时内部会发生变化,此时就需要应用模板模式编写此过程 从而解决事情的流程依然可以别使用,这就是模板模式的好处(类似我们的个人简历模板) (二)步骤: ...

  10. ASP.NET 4.0 页面 ValidateRequest="false" 失效不起作用

    当ASP.NET 2.0升级到 ASP.NET 4.0后,页面的 ValidateRequest="false" 不起作用. 因为 ASP.NET 4.0 请求验证被提前到IHtt ...