上期我们一起学习了静态RNN和动态RNN的区别,
深度学习算法(第16期)----静态RNN和动态RNN我们知道之前学过的CNN的输入输出都是固定长度,今天我们一起学习下RNN是怎么处理变化长度的输入输出的?

1. 处理变化长度的输入

到目前为止,我们已经知道在RNN中怎么使用固定长度的输入,准确的说是两个时刻长度的输入,但是如果输入的序列是变化长度的呢?比如一个句子。这种情况下,当我们调用dynamic_rnn()或者static_rnn()的时候,需要设置参数sequence_length。该参数应该是一个表示每个样本输入序列大小的一维tensor。走个例子看看:

seq_length = tf.placeholder(tf.int32, [None])
[...]
outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32,
                sequence_length=seq_length)

就上期的例子,假如说,第二个样本的输入长度为1的话,那么为了匹配输入tensor的大小,必须将第二个样本的第二个输入设为0,如下:

X_batch = np.array([
# step 0 step 1
[[0, 1, 2], [9, 8, 7]], # instance 0
[[3, 4, 5], [0, 0, 0]], # instance 1 (padded with a zero vector)
[[6, 7, 8], [6, 5, 4]], # instance 2
[[9, 0, 1], [3, 2, 1]], # instance 3
])
seq_length_batch = np.array([2, 1, 2, 2])

当然,在我们运行RNN的时候,需要将输入X和样本长度seq_length都传输进去,如下:

with tf.Session() as sess:
    init.run()
    outputs_val, states_val = sess.run(
        [outputs, states], feed_dict={X: X_batch, seq_length: seq_length_batch})

这样的话呢,RNN就会根据输入序列的情况输出相应的结果,比方上面例子中第二个样本的第二个时刻为0,那么第二个样本在第二个时刻的输出情况就是0,如下:

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

还有,输出的states tensor包括了每一个记忆单元的最后一个状态(不包括0输入的),如下:

>>> print(states_val)
[[ 0.51955646 1. 0.99999022 -0.99984968 -0.24616946] # t = 1
[-0.12842922 0.99981797 0.84704727 -0.99570125 0.38665548] # t = 0 !!!
[-0.32477224 0.99996376 0.99933046 -0.99711186 0.10981458] # t = 1
[-0.43738723 0.91517633 0.97817528 -0.91763324 0.11047263]] # t = 1

好了,我们知道了RNN是怎么处理变长度的输入,那么输出序列的长度如果也是变化的,该怎么办呢?

2. 处理变化长度的输出

假如我们已经提前知道每个样本的输出的长度的话,比方说,我们知道每个样本输出的长度和输入的一样长,那么我们就可以像上面一样通过设置sequence_length参数来处理。
但是不幸的是,一般情况下,我们都不知道输出的长度,比方说翻译一个句子,输出的长度往往和输入的长度是不一致的。这种情况下最常用的解决办法是去定义一个特殊的输出,称为end-of-sequence token(EOS token)。这个我们后面再讨论。

学习了这么多的关于RNN的知识,下一期我们将学习如何训练RNN网络?