【个人整理】tensorflow关于循环神经网络(RNN)的输出与状态的“维度”分析

前言:使用现在主流的深度学习框架实现循环神经网络是很简单方便的,所以我们很多时候可能忘了怎么去分析神经网络的输入,输出到底是什么,循环层各个节点之间维护的所谓的状态到底是什么?本文需要循环神经网络的基本知识和一些基本的理论,可以参见我前面的两篇文章:

https://blog.csdn.net/qq_27825451/article/details/88870027

写这篇文章的初衷是因为我看过很多的博文,很多都是转载一样的,有一部分原创的说明了一些基本的实现,但是感觉模棱两可,没有涉及到非常细节的地方(也很有可能是别人写的我看不懂),所以我决定自己写一篇,这篇文章只说一个点,那就是循环神经网络的每一个cell的输出和它们之间的状态到底是什么样子的,为什么是这个样子,鉴于水平有限,有不正确的地方,还希望有大佬指正,本文基于tensorflow来说明。

一、RNN的基本结构

二、一个例子彻底弄懂输出与状态的“维度”问题

(1)第一步:创建训练数据样本X

(2)第二步:创建一个运算图结构

(3)第三步:构建graph,进行测试

三、程序的运行结果分析

四、RNN输出值和状态值总结

五:补充,

一、RNN的基本结构

在很多的书本上或者博客里可以看见这样的结构图,如下:

这当然是没有错误的,但是缺点就是这个图是大神经过提炼的,太抽象化了,不太方便理解,那该怎么办呢,我这里在草稿本上化了一个草图,个人感觉更好理解:

画的不好看,但是下面的说明部分做了比较详细的解释。

二、一个例子彻底弄懂输出与状态的“维度”问题

本文使用的是tensorflow框架,版本为tensorflow1.9.里面的dynamic_rnn()函数,为什么要用这个函数,前面的那篇文章已经做了很详细的说明。

(1)第一步:创建训练数据样本X

import tensorflow as tf
import numpy as np
import pprint

train_X = np.array([
[[0, 1, 2], [9, 8, 7],[3,6,8]], 
[[3, 4, 5], [1, 3, 5],[6,2,9]], 
[[6, 7, 8], [6, 5, 4],[1,7,4]], 
[[9, 0, 1], [3, 7, 4],[5,8,2]], 
])
'''样本数据为(samples,timesteps,features)的形式,
其中samples=4,timesteps=3,features=3
'''

(2)第二步:创建一个运算图结构

#创建一个容纳训练数据的placeholder
X=tf.placeholder(tf.float32,shape=[None,3,3])  

# tensorflow处理变长时间序列的处理方式,首先每一个循环的cell里面有5个神经元
basic_cell=tf.nn.rnn_cell.BasicRNNCell(num_units=5)

#使用dynamic_rnn创建一个动态计算的RNN
outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)

(3)第三步:构建graph,进行测试

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    outputs_val, states_val = sess.run(
        [outputs, states], feed_dict={X:train_X})

    pprint.pprint(outputs_val)   #打印输出值和状态值
    pprint.pprint(states_val)
    print('==============================================')
    print(np.shape(outputs_val)) 查看输出值和状态的维度
    print(np.shape(states_val))
    print('++++++++++++++++++++++++++++++++++++++++++++++++')
    print(basic_cell.state_size)
    print(basic_cell.output_size)

三、程序的运行结果分析

程序的运行结果如下:

array([
       [[-0.6108298 ,  0.0596378 , -0.37820065,  0.3211917 ,0.56089014],
        [-0.99999994,  0.9993362 ,  0.9778955 ,  0.5386584 ,-0.9203638],
        [-0.9997577 ,  0.9915552 , -0.9343918 , -0.24285667,0.63978183]],

       [[-0.9990114 ,  0.9177295 , -0.06776464,  0.6655134 ,0.4589014 ],
        [-0.98075587,  0.53816617, -0.05612217, -0.24713938,0.48741972],
        [-0.99997896, -0.47020257,  0.9985639 ,  0.99964374,0.99789286]],

       [[-0.999998  ,  0.9958609 ,  0.2563717 ,  0.85442424,0.34319228],
        [-0.9999506 ,  0.9972327 ,  0.8965514 , -0.5725894 ,-0.9418285],
        [-0.98404294,  0.99636745, -0.99936426, -0.98879707,-0.83194304]],

       [[-0.99995047,  0.92161566,  0.9999575 ,  0.9958721 ,-0.23263188],
        [-0.99924177,  0.9996709 , -0.97150767, -0.9945894 ,-0.991192  ],
        [-0.99982065,  0.99869967, -0.8663484 , -0.98502225,-0.98442185]]], dtype=float32)


array([[-0.9997577 ,  0.9915552 , -0.9343918 , -0.24285667,  0.63978183],
       [-0.99997896, -0.47020257,  0.9985639 ,  0.99964374,  0.99789286],
       [-0.98404294,  0.99636745, -0.99936426, -0.98879707, -0.83194304],
       [-0.99982065,  0.99869967, -0.8663484 , -0.98502225, -0.98442185]],
      dtype=float32)
==============================================
(4, 3, 5)
(4, 5)
++++++++++++++++++++++++++++++++++++++++++++++++
5
5

通过上面的测试,我们发现:

输出值Y的维度为(4,3,5)至于是为什么?上面的手写图中已经说明了;

状态值S的维度为(4,5),上面也已经说明,

理论上Y和S其实应该是一样的,但是tensorflow中对这个state的定义是最后一个时间步上面的那个输出才是S,我们从上面的结果也可以看出,每一个每一个输出y的最后一个向量都与S是相等的,如下标注:

array([
       [[-0.6108298 ,  0.0596378 , -0.37820065,  0.3211917 ,0.56089014],
        [-0.99999994,  0.9993362 ,  0.9778955 ,  0.5386584 ,-0.9203638],
        [-0.9997577 ,  0.9915552 , -0.9343918 , -0.24285667,0.63978183]],

       [[-0.9990114 ,  0.9177295 , -0.06776464,  0.6655134 ,0.4589014 ],
        [-0.98075587,  0.53816617, -0.05612217, -0.24713938,0.48741972],
        [-0.99997896, -0.47020257,  0.9985639 ,  0.99964374,0.99789286]],

       [[-0.999998  ,  0.9958609 ,  0.2563717 ,  0.85442424,0.34319228],
        [-0.9999506 ,  0.9972327 ,  0.8965514 , -0.5725894 ,-0.9418285],
        [-0.98404294,  0.99636745, -0.99936426, -0.98879707,-0.83194304]],

       [[-0.99995047,  0.92161566,  0.9999575 ,  0.9958721 ,-0.23263188],
        [-0.99924177,  0.9996709 , -0.97150767, -0.9945894 ,-0.991192  ],
        [-0.99982065,  0.99869967, -0.8663484 , -0.98502225,-0.98442185]]], dtype=float32)


array([[-0.9997577 ,  0.9915552 , -0.9343918 , -0.24285667,  0.63978183],
       [-0.99997896, -0.47020257,  0.9985639 ,  0.99964374,  0.99789286],
       [-0.98404294,  0.99636745, -0.99936426, -0.98879707, -0.83194304],
       [-0.99982065,  0.99869967, -0.8663484 , -0.98502225, -0.98442185]],
      dtype=float32)

四、RNN总结

本文的总结是在tensorflow的dynamic_rnn基础之上的,但是这是RNN运算过程的本质反映,都是通用的。

结论总结:

(1)RNN的输出维度,output_Y的维度为(samples,timesteps,num_units)

(2)RNN的状态维度,output_S的维度为(samples,num_units)

注意,output_S和output_Y本质上是一样的,只不过这里人为RNN中间的state是在RNN的cell之间传递的,并没有输出,所以不算输出值,只是把最后一个时间不上面的state作为了输出的state。

补充:看一下dynamic_rnn的定义

tf.nn.dynamic_rnn(
    cell,
    inputs,
    sequence_length=None,
    initial_state=None,
    dtype=None,
    parallel_iterations=None,
    swap_memory=False,
    time_major=False,
    scope=None
)
'''
Defined in tensorflow/python/ops/rnn.py.
See the guide: Neural Network > Recurrent Neural Networks
Creates a recurrent neural network specified by RNNCell cell.
Performs fully dynamic unrolling of inputs.
'''
#Example:
#=====================================================================
# create a BasicRNNCell
rnn_cell = tf.nn.rnn_cell.BasicRNNCell(hidden_size)

# 'outputs' is a tensor of shape [batch_size, max_time, cell_state_size]

# defining initial state
initial_state = rnn_cell.zero_state(batch_size, dtype=tf.float32)

# 'state' is a tensor of shape [batch_size, cell_state_size]
outputs, state = tf.nn.dynamic_rnn(rnn_cell, input_data,
                                   initial_state=initial_state,
                                   dtype=tf.float32)
#======================================================================
# create 2 LSTMCells
rnn_layers = [tf.nn.rnn_cell.LSTMCell(size) for size in [128, 256]]

# create a RNN cell composed sequentially of a number of RNNCells
multi_rnn_cell = tf.nn.rnn_cell.MultiRNNCell(rnn_layers)

# 'outputs' is a tensor of shape [batch_size, max_time, 256]
# 'state' is a N-tuple where N is the number of LSTMCells containing a
# tf.contrib.rnn.LSTMStateTuple for each cell
outputs, state = tf.nn.dynamic_rnn(cell=multi_rnn_cell,
                                   inputs=data,
                                   dtype=tf.float32)

 参数解释:

  • cell: 通过RNNCell类创建的实例对象
  • inputs: 这个是RNN网络的输出. 如果time_major == False (这是默认值), 则inputs的shape必须是: [batch_size, max_time, features], 但是如果time_major == True,则inputs的shape必须是:[max_time, batch_size,features], 注意:这里的max_time就相当于前面的timesteps,但是这里为什么要用max_time呢?因为我们的序列有时候是长度不固定的,这里是指的最大序列长,后面还会讲到对于不定长序列得处理该怎么做,所以为了一般性,这里使用了max_time。而这里的features有时候也用depth来表示,总之是特征的意思。
  • sequence_length: 这是一个可选参数,专门用来处理变长序列的,后面再详细说明,这里先不说明。
  • time_major: 这个参数决定了 inputs and outputs 的形状. 如果是True, 则输入inputs和输出outputs是 [max_time, batch_size, depth]. 如果是False, 则输入inputs和输出outputs是[batch_size, max_time, depth]. 需要注意的是默认的False可能更加符合我们的习惯,但是使用True的话,运算效率更高一些,至于是什么原因,我在上面的一篇文章里面已经向说明了的,因为timesteps在前面对应着static_rnn,这个是按照RNN的运算过程来实现的,不需要转置等操作,如果是timesteps在中间的话,与dynamic_rnn是对应着的,这个是需要通过一些转置运算的,所以效率相对较低

返回值:

outputs: The RNN output Tensor.

If time_major == False (default), this will be a Tensor shaped: [batch_size, max_time, cell.output_size].这是默认的情况

If time_major == True, this will be a Tensor shaped: [max_time, batch_size, cell.output_size].

state:同上所述。

五:补充,

这里收集到几张关于RNN比较好理解的动态图,分享给大家:

 

  • 15
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值