tf.nn.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
)
tf.nn.dynamic_rnn的返回值有两个:outputs和state
为了描述输出的形状,先介绍几个变量,batch_size是输入的这批数据的数量,max_time就是这批数据中序列的最长长度,如果输入的三个句子,那max_time对应的就是最长句子的单词数量,cell.output_size其实就是rnn cell中神经元的个数。
那为什么state输出形状会有变化呢?state和output又有什么关系呢?
对于第一问题“state”形状为什么会发生变化呢?
我们以LSTM和GRU分别为tf.nn.dynamic_rnn的输入cell类型为例,当cell为LSTM,state形状为[2,batch_size, cell.output_size ];当cell为GRU时,state形状为[batch_size, cell.output_size ]。其原因是因为LSTM和GRU的结构本身不同,如下面两个图所示,这是LSTM的cell结构,每个cell会有两个输出: 和 ,上面这个图是输出,代表哪些信息应该被记住哪些应该被遗忘; 下面这个图是输出,代表这个cell的最终输出,LSTM的state是由 和 组成的。
当cell为GRU时,state就只有一个了,原因是GRU将 和 进行了简化,将其合并成了,如下图所示,GRU将遗忘门和输入门合并成了更新门,另外cell不在有细胞状态cell state,只有hidden state。
对于第二个问题outputs和state有什么关系?
结论上来说,如果cell为LSTM,那 state是个tuple,分别代表 和 ,其中 与outputs中的对应的最后一个时刻的输出相等,假设state形状为[ 2,batch_size, cell.output_size ],outputs形状为 [ batch_size, max_time, cell.output_size ],那么state[ 1, batch_size, : ] == outputs[ batch_size, -1, : ];如果cell为GRU,那么同理,state其实就是 ,state ==outputs[ -1 ]
我们写点代码来具体感觉下outputs和state是什么,代码如下
import tensorflow as tf
import numpy as np
def dynamic_rnn(rnn_type='lstm'):
# 创建输入数据,3代表batch size,6代表输入序列的最大步长(max time),8代表每个序列的维度
X = np.random.randn(3, 6, 4)
# 第二个输入的实际长度为4
X[1, 4:] = 0
#记录三个输入的实际步长
X_lengths = [6, 4, 6]
rnn_hidden_size = 5
if rnn_type == 'lstm':
cell = tf.contrib.rnn.BasicLSTMCell(num_units=rnn_hidden_size, state_is_tuple=True)
else:
cell = tf.contrib.rnn.GRUCell(num_units=rnn_hidden_size)
outputs, last_states = tf.nn.dynamic_rnn(
cell=cell,
dtype=tf.float64,
sequence_length=X_lengths,
inputs=X)
with tf.Session() as session:
session.run(tf.global_variables_initializer())
o1, s1 = session.run([outputs, last_states])
print(np.shape(o1))
print(o1)
print(np.shape(s1))
print(s1)
if __name__ == '__main__':
dynamic_rnn(rnn_type='lstm')
实验一:cell类型为LSTM,我们看看输出是什么样子,如下图所示,输入的形状为 [ 3, 6, 4 ],经过tf.nn.dynamic_rnn后outputs的形状为 [ 3, 6, 5 ],state形状为 [ 2, 3, 5 ],其中state第一部分为c,代表cell state;第二部分为h,代表hidden state。可以看到hidden state 与 对应的outputs的最后一行是相等的。另外需要注意的是输入一共有三个序列,但第二个序列的长度只有4,可以看到outputs中对应的两行值都为0,所以hidden state对应的是最后一个不为0的部分。tf.nn.dynamic_rnn通过设置sequence_length来实现这一逻辑。
(3, 6, 5)
[[[ 0.0146346 -0.04717453 -0.06930042 -0.06065602 0.02456717]
[-0.05580321 0.08770171 -0.04574306 -0.01652854 -0.04319528]
[ 0.09087799 0.03535907 -0.06974291 -0.03757408 -0.15553619]
[ 0.10003044 0.10654698 0.21004055 0.13792148 -0.05587583]
[ 0.13547596 -0.014292 -0.0211154 -0.10857875 0.04461256]
[ 0.00417564 -0.01985144 0.00050634 -0.13238986 0.14323784]]
[[ 0.04893576 0.14289175 0.17957205 0.09093887 -0.0507192 ]
[ 0.17696126 0.09929577 0.21185635 0.20386451 0.11664373]
[ 0.15658667 0.03952745 -0.03425637 0.00773833 -0.03546742]
[-0.14002582 -0.18578786 -0.08373584 -0.25964601 0.04090167]
[ 0. 0. 0. 0. 0. ]
[ 0. 0. 0. 0. 0. ]]
[[ 0.18564152 0.01531695 0.13752453 0.17188506 0.19555427]
[ 0.13703949 0.14272294 0.21313036 0.07417354 0.0477547 ]
[ 0.23021792 0.04455495 0.10204565 0.17159792 0.34148467]
[ 0.0386402 0.0387848 0.02134559 0.00110381 0.08414687]
[ 0.01386241 -0.02629686 -0.0733538 -0.03194245 0.13606553]
[ 0.01859433 -0.00585316 -0.04007138 0.03811594 0.21708331]]]
(2, 3, 5)
LSTMStateTuple(c=array([[ 0.00909146, -0.03747076, 0.0008946 , -0.23459786, 0.29565899],
[-0.18409266, -0.30463044, -0.28033809, -0.49032542, 0.12597639],
[ 0.04494702, -0.01359631, -0.06706629, 0.06766361, 0.40794032]]), h=array([[ 0.00417564, -0.01985144, 0.00050634, -0.13238986, 0.14323784],
[-0.14002582, -0.18578786, -0.08373584, -0.25964601, 0.04090167],
[ 0.01859433, -0.00585316, -0.04007138, 0.03811594, 0.21708331]]))
实验二:cell类型为GRU,我们看看输出是什么样子,如下图所示,输入的形状为 [ 3, 6, 4 ],经过tf.nn.dynamic_rnn后outputs的形状为 [ 3, 6, 5 ],state形状为 [ 3, 5 ]。可以看到 state 与 对应的outputs的最后一行是相等的。
(3, 6, 5)
[[[-0.05190962 -0.13519617 0.02045928 -0.0821183 0.28337528]
[ 0.0201574 0.03779418 -0.05092804 0.02958051 0.12232347]
[ 0.14884441 -0.26075898 0.1821795 -0.03454954 0.18424161]
[-0.13854156 -0.26565378 0.09567164 -0.03960079 0.14000589]
[-0.2605973 -0.39901657 0.12495693 -0.19295695 0.52423598]
[-0.21596414 -0.63051687 0.20837501 -0.31775378 0.77519457]]
[[-0.1979659 -0.30253523 0.0248779 -0.17981144 0.41815343]
[ 0.34481129 -0.05256187 0.1643036 0.00739746 0.27384158]
[ 0.49703664 0.22241165 0.27344766 0.00093435 0.09854949]
[ 0.23312444 0.156997 0.25482553 0.0138156 -0.02302272]
[ 0. 0. 0. 0. 0. ]
[ 0. 0. 0. 0. 0. ]]
[[-0.06401732 0.08605342 -0.03936866 -0.02287695 0.16947652]
[-0.1775206 -0.2801672 -0.0387468 -0.20264583 0.58125297]
[ 0.39408762 -0.44066425 0.25826641 -0.18851604 0.36172166]
[ 0.0536013 -0.29902928 0.08891931 -0.03930039 0.0743423 ]
[ 0.02304702 -0.0612499 0.09113458 -0.05169013 0.29876455]
[-0.06711324 0.014125 -0.05856332 -0.05632359 -0.00390189]]]
(3, 5)
[[-0.21596414 -0.63051687 0.20837501 -0.31775378 0.77519457]
[ 0.23312444 0.156997 0.25482553 0.0138156 -0.02302272]
[-0.06711324 0.014125 -0.05856332 -0.05632359 -0.00390189]]
Reference
http://colah.github.io/posts/2015-08-Understanding-LSTMs/
https://www.tensorflow.org/api_docs/python/tf/nn/dynamic_rnn