深度学习算法(第16期)----静态RNN和动态RNN

上期我们一起学习了基础RNN在tensorflow中的实现
深度学习三人行(第15期)----基本RNN的Tensorflow实现
今天我们一起学习下静态RNN和动态RNN及其区别。

1. 静态RNN

函数static_rnn()函数通过连接记忆单元创建一个展开的RNN网络,下面的代码创建了一个RNN网络,该网络和上期中我们创建的是完全一样的。

X0 = tf.placeholder(tf.float32, [None, n_inputs])
X1 = tf.placeholder(tf.float32, [None, n_inputs])
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
output_seqs, states = tf.contrib.rnn.static_rnn(
                        basic_cell, [X0, X1], dtype=tf.float32)
Y0, Y1 = output_seqs

首先,和之前一样我们创建了两个用来输入数据的placeholder,接下来,我们创建了BasicRNNCell(可以把这个函数想象为一个创建记忆单元的一个工厂),用来构建展开的RNN网络。接下来调用static_rnn(),该函数输入为创建好的cell,输入的tensor以及tensor的类型。对于每一个输入,static_rnn()调用记忆单元的__call__()函数,创建两个记忆单元的两个copy,每一个copy中都包含着有5个循环神经元的一层网络,并且有着共享变量和偏置项,和之前做的一样,连在一起。
static_rnn()函数返回两个对象,其中一个是一个list,该list包含每一个时刻所输出的tensor,另一个对象是一个tensor包含着网络的最终状态。如果我们用最基本的记忆单元的话,那么最后状态和输出是一致的。
如果有50个时刻,那么这样定义50个输入的placeholder和50个输出的tensor就会显得比较麻烦。还有在执行的时候,还得传输50个placeholder和50个输出tensor,你说麻烦不麻烦?既然麻烦,那么就会有简单的办法,如下:

X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
X_seqs = tf.unstack(tf.transpose(X, perm=[1, 0, 2]))
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
output_seqs, states = tf.contrib.rnn.static_rnn(
                        basic_cell, X_seqs, dtype=tf.float32)
outputs = tf.transpose(tf.stack(output_seqs), perm=[1, 0, 2])

如上,这里我们只需要用1个placeholder就可以了,它的shape是[None, n_steps, n_inputs],其中第一维度None代表mini-batch的大小,第二个维度代表时间步长的个数,也就是有多少个时刻。第三个维度为每个时刻的输入大小。X_seqs是一个拥有n_steps个shape为[None, n_inputs]的tensor。为了转换成这种形式,我们得先通过transpose()函数,将前两个维度互换一下,转换之后时间步长就变成了第一个维度。然后我们可以通过unstack()函数根据第一个维度将它转换成一个python列表的形式。接下来的两行跟之前一样,创建静态RNN,最后我们通过stack()函数将所有的输出tensor合并成一个tensor,最后交换输出tensor的前两个维度,转换成[None, n_steps, n_inputs]形式。这样的话,我们就可以通过传输一个包含mini-batch序列的tensor来运行网络了。如下:

X_batch = np.array([
        # t = 0 t = 1
        [[0, 1, 2], [9, 8, 7]], # instance 0
        [[3, 4, 5], [0, 0, 0]], # instance 1
        [[6, 7, 8], [6, 5, 4]], # instance 2
        [[9, 0, 1], [3, 2, 1]], # instance 3
    ])
with tf.Session() as sess:
    init.run()
    outputs_val = outputs.eval(feed_dict={X: X_batch})

运行结束,我们得到一个tensor(outputs_val) 包含着每个样本在每个时刻每个神经元上的输出情况。如下:

>>> print(outputs_val)
[[[-0.2964572 0.82874775 -0.34216955 -0.75720584 0.19011548]
  [ 0.51955646 1. 0.99999022 -0.99984968 -0.24616946]]
[[-0.12842922 0.99981797 0.84704727 -0.99570125 0.38665548]
 [-0.70553327 -0.11918639 0.48885304 0.08917919 -0.26579669]]
[[ 0.04731077 0.99999976 0.99330056 -0.999933 0.55339795]
 [-0.32477224 0.99996376 0.99933046 -0.99711186 0.10981458]]
[[ 0.70323634 0.99309105 0.99909431 -0.85363263 0.7472108 ]
 [-0.43738723 0.91517633 0.97817528 -0.91763324 0.11047263]]]

然而,通过这种方法创建的图会在每个时刻都创建一个单元,有50个时刻的话,看起来比较丑,有点像不用循环写了50次。Y0=f(0, X0); Y1=f(Y0, X1); Y2=f(Y1, X2); ...; Y50=f(Y49,X50)。这么大的一张图,一想就知道肯定会占用很大的内存空间,特别是在反向传播的时候,对于一个内存受限的GPU,简直就是要了老命。因为反向传播的时候需要用所有前向传播的权重值来计算梯度,这就不得不将前向传播的值都存下来。那么对于有限的内存来说,关机,洗洗睡吧。
兵来将挡水来土掩,幸运的是,我们可以用动态RNN来解决这个问题,那么什么是动态RNN呢?

2. 动态RNN

动态RNN的函数为dynamic_rnn(),这个函数内部用了一个while_loop()的操作,它会根据有多少时刻来动态调整参数运行网络。我们也可以通过设置swap_memory=True来避免在反向传输的时候内存耗尽。这个设置允许在反向传输的时候将CPU内存和GPU内存互换。
很方便的,动态RNN对于所有的输入也接收一个tensor,shape为[None, n_steps, n_inputs],并且和前面的一样,输出一个shape为[None, n_steps, n_inputs]的tensor。而且不用像前面一样要通过unstack,stack,transpose等函数转来转去的。下面的代码用dynamic_rnn()创建了和前面一样的RNN。比较漂亮!

X = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)

今天我们主要从静态RNN和动态RNN的角度来理解了一下循环神经网络,希望有些收获,欢迎留言或进社区共同交流。

喜欢的话,就点个在看留下您的足迹吧,也可以置顶公众号,第一时间接收最新内容。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值