摘要:RNN可以用于描述时间上连续状态的输出,有记忆功能,能处理时间序列的能力,让我惊叹。

本文分享自华为云社区《用RNN进行图像分类——CNN之后的探索》,作者: Yin-Manny。

一、 写前的思考:

当看完RNN的PPT,我惊叹于RNN可以用于描述时间上连续状态的输出,有记忆功能,能处理时间序列的能力。

当拿到思考题,在CNN框架下加入RNN程序,这是可以实现的吗,如果可以,它的理论依据是什么,它的实现方法是什么,它的效果是怎样的。加入这个有必要吗。

我寻找了CNN combine with RNN的资料,看了CLDNN论文,我知道了:

CNN和RNN直接的不同点:

CNN进行空间扩展,神经元与特征卷积;RNN进行时间扩展,神经元与多个时间输出计算;

RNN可以用于描述时间上连续状态的输出,有记忆功能;CNN则用于静态输出;

CNN高级结构可以达到100+深度;RNN的深度有限。

CNN和RNN组合的意义:

大量信息同时具有时间空间特性:视频,图文结合,真实的场景对话;

带有图像的对话,文本表达更具体;

视频相对图片描述的内容更完整。

但是这对思考题没有什么帮助。

于是我又从RNN分类图像下手,试图弄明白RNN能用于图像分类的原理,首先需要将图片数据转化为一个序列数据,例如MINST手写数字图片的大小是28x28,那么可以将每张图片看作是长为28的序列,序列中的每个元素的特征维度是28,这样就将图片变成了一个序列。同时考虑循环神经网络的记忆性,所以图片从左往右输入网络的时候,网络可以记忆住前面观察东西,然后与后面部分结合得到最后预测数字的输出结果,理论上是行得通的。但是对于图像分类,CNN才是主流,RNN图像分类的理论,对于CNN能有什么帮助呢?

甚至我们知道,循环神经网络还是不适合处理图片类型的数据:

第一个原因是图片并没有很强的序列关系,图片中的信息可以从左往右看,也可以从右往左看,甚至可以跳着随机看,不管是什么样的方式都能够完整地理解图片信息;

第二个原因是循环神经网络传递的时候,必须前面一个数据计算结束才能进行后面一个数据的计算,这对于大图片而言是很慢的,但是卷积神经网络并不需要这样,因为它能够并行,在每一层卷积中,并不需要等待第一个卷积做完才能做第二个卷积,整体是可以同时进行的。

那么我要怎么在CNN中加入RNN程序呢?

初步设想:

把CNN比较深层次的网络提取到的特征序列化,再喂给RNN进行分类,因为我认为这时候CNN提取到的特征比原始图像有更强的序列关系(如下图,越深层得到的特征序列关系越强,比如跳着看可能就难以进行分类了)

二、 如何将图像数据改成序列数据?如何加入RNN系列程序,以改进图片分类的性能?

设image.shape为(h,w)

则令time_steps=h, input_size=w即可将图像数据改成序列数据

令X=[batch_size,h,w]

outputs, states = tf.nn.dynamic_rnn(rnn_cell, X, dtype=tf.float32)即可将X应用于RNN程序

如果是三通道图片,如RGB,则利用图像处理知识将三通道图像转为单通道灰度图像。

例如:

import matplotlib.pyplot as plt # plt 用于显示图片
from PIL import Image
import numpy as np
image1 = Image.open('./1.jpg')
img=np.array(image1)
# 通道转换
def change_image_channels(image):
# 3通道转单通道
if image.mode == 'RGB':
r, g, b = image.split()
return r,g,b
ima = change_image_channels(image1)
for i in range(3):
plt.imshow(ima[i])
plt.show()
r=img[:,:,0]
g=img[:,:,1]
b=img[:,:,2]
GRAY = b * 0.114 + g * 0.387 + r * 0.29
im=Image.fromarray(GRAY) # numpy 转 image类
im.show()

效果如下:

正如初步设想所说,把CNN比较深层次的网络提取到的特征序列化,再喂给RNN进行分类,因为我认为这时候CNN提取到的特征比原始图像有更强的序列关系,也许能够改进图片分类的性能?

例如我们将第一层的特征图喂到RNN中

import sys
sys.path.append('..')
import torch
from torch.autograd import Variable
from torch import nn
from torch.utils.data import DataLoader
from torchvision import transforms as tfs
from torchvision.datasets import MNIST
# 定义数据
data_tf = tfs.Compose([
tfs.ToTensor(),
tfs.Normalize([0.5], [0.5]) # 标准化
])
train_set = MNIST('./data', train=True, transform=data_tf, download=True)
test_set = MNIST('./data', train=False, transform=data_tf, download=True)
train_data = DataLoader(train_set, 64, True, num_workers=2)
test_data = DataLoader(test_set, 128, False, num_workers=2)
# 定义模型
class rnn_classify(nn.Module):
def __init__(self, in_feature=28, hidden_feature=100, num_class=10, num_layers=2):
super(rnn_classify, self).__init__()
self.rnn = nn.LSTM(in_feature, hidden_feature, num_layers) # 使用两层 lstm
self.classifier = nn.Linear(hidden_feature, num_class) # 将最后一个 rnn 的输出使用全连接得到最后的分类结果
def forward(self, x):
'''
x 大小为 (batch, 1, 28, 28),所以我们需要将其转换成 RNN 的输入形式,即 (28, batch, 28)
'''
x = x.squeeze() # 去掉 (batch, 1, 28, 28) 中的 1,变成 (batch, 28, 28)
x = x.permute(2, 0, 1) # 将最后一维放到第一维,变成 (28, batch, 28)
out, _ = self.rnn(x) # 使用默认的隐藏状态,得到的 out 是 (28, batch, hidden_feature)
out = out[-1, :, :] # 取序列中的最后一个,大小是 (batch, hidden_feature)
out = self.classifier(out) # 得到分类结果
return out
net = rnn_classify()
criterion = nn.CrossEntropyLoss()
optimzier = torch.optim.Adadelta(net.parameters(), 1e-1)
# 开始训练
from utils import train
train(net, train_data, test_data, 10, optimzier, criterion)

迭代10次准确率高达98%,因此分类效果还是不错的。

三、 不同的RNN细胞结构、不同的RNN整体结构,对分类性能有什么影响?

不同的细胞结构具有不同的门结构,对长短期记忆有不同的权重

不同的RNN整体结构有不同的层数与架构,对长短期记忆有不同的遗忘属性

常见RNN细胞总结:

BasicRNNCell--一个普通的RNN单元。

GRUCell--一个门控递归单元细胞。

BasicLSTMCell--一个基于递归神经网络正则化的LSTM单元,没有窥视孔连接或单元剪裁。

LSTMCell--一个更复杂的LSTM单元,允许可选的窥视孔连接和单元剪切。

MultiRNNCell--一个包装器,将多个单元组合成一个多层单元。

DropoutWrapper - -一个为单元的输入和/或输出连接添加dropout的包装器。

常见RNN整体结构:

LSTM和GRU,其它的还有向GridLSTM、AttentionCell等

这些在tf.keras.layers.***中都可以直接调用API

因此只需修改下面一行代码的API即可实现不同的RNN细胞结构、不同的RNN整体结构对分类性能的影响的实验。

self.rnn = nn.LSTM(in_feature, hidden_feature, num_layers) # API

由于时间问题我没有运行完代码,直接附上相关资料的实验结果:

一般来说,多层结构的复杂度更高,分类性能会更好,但是产生的时间成本也会更多。

四、 nn.dynamic_rnn输出的final_state.h和output[:,-1,:]是否是相同的?

RNN 是这样一个单元:y_t, s_t = f(x_t, s_{t-1}) ,画成图的话,就是这样:

考虑 Vanilla RNN/GRU Cell(vanilla RNN 就是最普通的 RNN,对应于 TensorFlow 里的 BasicRNNCell),工作过程如下:

这时,s_t = y_t = h_t

对于 LSTM,它的循环部件其实有两部分,一个是内部 cell 的值,另一个是根据 cell 和 output gate 计算出的 hidden state,输出层只利用 hidden state 的信息,而不直接利用 cell。这样一来,LSTM 的工作过程就是:

其中真正用于循环的状态 s_t 其实是 (c_t, h_t) 组成的 tuple(就是 TensorFlow 里的 LSTMStateTuple),而输出 y_t 仅仅是 h_t(例如网络后面再接一个全连接层然后用 softmax 做分类,这个全连接层的输入仅仅是 h_t,而没有 c_t),这时就可以看到区分 RNN 的输出和状态的意义了。

如果是一个多层的 Vanilla RNN/GRU Cell,那么一种简单的抽象办法就是,把多层 Cell 当成一个整体,当成一层大的 Cell,然后原先各层之间的关系都当成这个大的 Cell 的内部计算过程/数据流动过程,这样对外而言,多层的 RNN 和单层的 RNN 接口就是一模一样的:在外部看来,多层 RNN 只是一个内部计算更复杂的单层 RNN。图示如下:

大方框表示把多层 RNN 整体视为一层大的 Cell,而里面的小方框则对应于原先的每一层 RNN。这时,如果把大方框视为一个整体,那么这个整体进行循环所需要的状态就是各层的状态组成的集合,或者说把各层的状态放在一起组成一个 tuple:st=(st(l),st(2),…, st(l))而这个大的 RNN 单元的输出则只有原先的最上层 RNN 的输出,即整体的yt= yt(l)=ht(l)。

最后对于多层 LSTM:

和之前的例子类似,把多层 LSTM 看成一个整体,这个整体的输出就是最上层 LSTM 的输出:yt=ht(l);而这个整体进行循环所依赖的状态则是每一层状态组合成的 tuple,而每一层状态本身又是一个 (c, h) tuple,所以最后结果就是一个 tuple 的 tuple

这样一来,便可以回答问题:final_state.h和output[:,-1,:]是否是相同的

output是RNN Cell的output组成的列表,假设一共有T个时间步,那么 outputs = [y_1, y_2, ..., y_T],因此 outputs[:,-1,:] = y_T;而 final_state.h 则是最后一步的隐层状态的输出,即 h_T。

那么,到底 output[:,-1,:]等不等于final_state.h 呢?或者说 y_T 等不等于 h_T 呢?看一下上面四个图就可以知道,当且仅当使用单层 Vanilla RNN/GRU 的时候,他们才相等。

代码运行结果具体见附件代码第三问。

点击关注,第一时间了解华为云新鲜技术~

后CNN探索,如何用RNN进行图像分类的更多相关文章

  1. 越狱后的ios如何用apt-get 安装各种命令

    越狱后的ios如何用apt-get 安装各种命令   iphone越狱后想玩linux. 1. ssh 客户端:ssh Term Pro. 2. 只装客户端是连不上的,还得一个 ssh connect ...

  2. CNN(卷积神经网络)、RNN(循环神经网络)、DNN(深度神经网络)的内部网络结构有什么区别?

    https://www.zhihu.com/question/34681168 CNN(卷积神经网络).RNN(循环神经网络).DNN(深度神经网络)的内部网络结构有什么区别?修改 CNN(卷积神经网 ...

  3. CNN(卷积神经网络)、RNN(循环神经网络)、DNN,LSTM

    http://cs231n.github.io/neural-networks-1 https://arxiv.org/pdf/1603.07285.pdf https://adeshpande3.g ...

  4. Kaggle比赛冠军经验分享:如何用 RNN 预测维基百科网络流量

    Kaggle比赛冠军经验分享:如何用 RNN 预测维基百科网络流量 from:https://www.leiphone.com/news/201712/zbX22Ye5wD6CiwCJ.html 导语 ...

  5. CNN(卷积神经网络)、RNN(循环神经网络)和DNN(深度神经网络)

    本文转载修改自:知乎-科言君 感知机(perceptron) 神经网络技术起源于上世纪五.六十年代,当时叫感知机(perceptron),拥有输入层.输出层和一个隐含层.输入的特征向量通过隐含层变换达 ...

  6. cnn,rnn,dnn

    CNN(卷积神经网络).RNN(循环神经网络).DNN(深度神经网络)的内部网络结构有什么区别? https://www.zhihu.com/question/34681168 CNN(卷积神经网络) ...

  7. 人工机器:NDC-谷歌机器翻译破世界纪录,仅用Attention模型,无需CNN和RNN

    终于找到ML日报的微信链接,抄之...................................... 请拜访原文链接:[谷歌机器翻译破世界纪录]仅用Attention模型,无需CNN和RNN. ...

  8. 深度学习实战篇-基于RNN的中文分词探索

    深度学习实战篇-基于RNN的中文分词探索 近年来,深度学习在人工智能的多个领域取得了显著成绩.微软使用的152层深度神经网络在ImageNet的比赛上斩获多项第一,同时在图像识别中超过了人类的识别水平 ...

  9. [转帖]CNN、RNN、DNN的一般解释

    CNN.RNN.DNN的一般解释 https://www.jianshu.com/p/bab3bbddb06b?utm_campaign=maleskine&utm_content=note& ...

  10. 【深度学习篇】---CNN和RNN结合与对比,实例讲解

    一.前述 CNN和RNN几乎占据着深度学习的半壁江山,所以本文将着重讲解CNN+RNN的各种组合方式,以及CNN和RNN的对比. 二.CNN与RNN对比 1.CNN卷积神经网络与RNN递归神经网络直观 ...

随机推荐

  1. 安装k8s

    完成安装k8s集群以后,推荐使用https://github.com/512team/dhorse发布应用. 准备环境 准备两台服务器节点,如果需要安装虚拟机,可以参考<wmware和cento ...

  2. redis主从同步及redis哨兵机制

    1.主从和哨兵的作用: 角色 作用 主从 1.(提供)数据副本:多一份数据副本,保证redis高可用 2.  扩展(读)性能:如容量.QPS等 哨兵 1.监控: 监控redis主库及从库运行状态: 2 ...

  3. tomcat环境

    tomcat环境部署时需要先部署JDK工具: JDK环境: #将上传的jdk包进行解压/并移至Java目录下: tar xf jdk1.8.0_131.tar.gz mkdir -p /usr/jav ...

  4. 中兴GoldenDB(MYSQL)营运商SQL优化案例(超复杂SQL)

    陆老师好久没找我,今天他在营运商遇到2条性能慢的SQL,说找了好多专家老手看了都优化不了,然后就找到我打算给我玩玩. 第一次接触营运商行业的SQL,妈呦还真的复杂  SQL1: SELECT WORK ...

  5. JavaScript高级程序设计笔记12 BOM

    BOM BOM的核心--window对象 窗口和弹窗 location对象--页面信息 navigator对象--浏览器信息 history对象--浏览器历史记录 BOM是使用JavaScript开发 ...

  6. 2022.7.13 tongyf 讲课纪要

    前言 这个笔记记晚了,主要是都在跟 \(LCT\) 进行殊死搏斗,所以博客这方面就挂了很久. tongyf 学长当年是拿到省一之后省选炸了,之后暴切高考.ORZ%%% 这节课讲的是线性dp和背包dp, ...

  7. CF510B Fox And Two Dots

    题目大意 矩阵中各个方格都有颜色,判断是否有相同颜色的方块可以组成环.(原题链接:CF510B Fox And Two Dots) 输入: 第一行:\(n\), \(m\),表示矩阵的行和列 接下来\ ...

  8. 对 .NET程序2G虚拟地址紧张崩溃 的最后一次反思

    一:背景 1. 讲故事 最近接连遇到了几起 2G 虚拟地址紧张 导致的程序崩溃,基本上 90% 都集中在医疗行业,真的很无语,他们用的都是一些上古的 XP,Windows7 x86,我也知道技术人很难 ...

  9. Springboot+shiro,完整教程,带你学会shiro

    您的第一个 Apache Shiro 应用程序 引入依赖: <?xml version="1.0" encoding="UTF-8"?> <p ...

  10. MongoDB的聚合笔记

    1,聚合 聚合(aggregate)主要用于计算数据,类似sql中的sum().avg(). 常用的表达式如上图. 1.1,aggregate  语法 语法:     db.集合名称.aggregat ...