前言
最近在学习lstm,看代码中,首先注意力就集中在网络的输入、输出是什么,本篇主要介绍输出是什么,帮忙自己和大家理解。
定义
outputs, states = 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
)
这里主要讲解一下outputs和states,百度得到的结果是:
outputs:包含了所有时刻的输出 H ,python代码里输出shape是[ time , batch , depth]
states :包含了最后一个时刻的输出 H 和 C,python代码里输出shape是[ batch , depth],RNN网络中没有C,LSTM 有H和C
这里time
也叫step
,很多博客在代码里书写时填写的是step
,其实是可以理解为timestep,这个也是根据你神经网络的长度有关,因为一个序列就是一个时间点,所以这里timestep
也就是序列长度,在NLP领域,你可以理解为一个句子分词后,单词的数量,但是这里timestep是固定的,不会因为句子长度变化而变化,所以在很多TensorFlow代码中,设计网络时都是给固定长度的,这个长度会影响你的输入,数值太小,这个就不能完全把一个完整表达含义的句子作为输入放到神经网络中训练,这个话题额外讨论。
那我们要怎么理解这个包含所有时刻的输出H呢?我们来看一个多层的LSTM网图:
这是一个3层网络,每层网络有5个timestep,之前我的理解就是一个timestep就是一个cell,每个cell都有一个H输出,所以这个网络一共有15个cell,也就是15个H,所以这里就是理解outputs中的depth就应该是神经网络的层数,也就是depth=3,但是后面想想不太对,因为output的最后两个维度是[timestep,depth],如果H是一个输出,肯定是一个矩阵,不可能是一个数字,而一个timestep有一个H,我们用二维的眼光来看[timestep,depth]二维矩阵,这里timestep是行数,depth是列数,这样如果depth是网络层数,那么H就是一个数字,因为一个行数和一个列数唯一确定一个数字,实际H也不可能是一个H,所以这里depth不可能是层数,后面和一个朋友聊天,一般只考虑最上一层的H,也就是上述多层LSTM图中最上面的一层神经元的所有输出H,这样一个timestep的大小就表示有多少个H,那么depth就很有可能是输出H的维度,因为一行对应一列向量,一列向量也可以表示为一个[1,n]二维的矩阵,这样就说明H是一个[1,n]二维的矩阵,n就是表示output的depth,那么这个depth具体表示什么含义呢?
从NLP角度思考,我们为了便于分析假设样本数量是1,时间序列也是1,也就是样本中只有一个单词作为输入,就是一个timestep,一般用词向量来表示单词,作为神经网络的输入,词向量一般是[1,m]维度的,t时刻的输入
x
t
∈
R
[
1
,
m
]
x_t \in \mathbb{R}^{[1,m]}
xt∈R[1,m],经过隐藏层RNN的计算公式如下:
h
t
=
f
(
x
t
∗
w
1
+
h
t
−
1
∗
w
2
)
h_t=f(x_t*w_1+h_{t-1}*w_2)
ht=f(xt∗w1+ht−1∗w2)
这里
h
t
−
1
h_{t-1}
ht−1的维度与
h
t
h_t
ht的维度是一样的,权重
w
1
w_1
w1就维度应该是[m,n],这样才可以跟
x
t
x_t
xt矩阵相乘,相乘后得到的结果的shape=[1,n],所以
h
t
h_t
ht应该是[1,n]维的,这里n是权重
w
w
w的维度,综合上述,可以给出一般性的结论,output的最后一个depth维度是一个跟权重有关的维度。
认识state
另外我们看下state的输出shape,官网给的shape是[2,batchsize,depth],这里2表示两个输出,一个是C ,另一个是H,实质state中state[0]表示的是C,staet[1]表示的是H,batchsize表示样本数量,我们分析一个样本的情况,如果一个样本,那么batchsize=1,那么depth也就表示[1,n]维的数据,所以跟output的理解是一样的。
代码验证
import tensorflow as tf
import numpy as np
batch_size = 4
time_step=3
n_inputs=6
n_weight =10
inputs = tf.random_normal(shape=[batch_size,time_step, n_inputs], dtype=tf.float32)
print('inputs.shape=',np.shape(inputs))
print(inputs)
cell = tf.nn.rnn_cell.BasicLSTMCell(n_weight, forget_bias=1.0, state_is_tuple=True) # n_hidden表示神经元的个数
init_state = cell.zero_state(batch_size, dtype=tf.float32)
output, final_state = tf.nn.dynamic_rnn(cell, inputs, initial_state=init_state, time_major=False)
#time_major如果是True,就表示RNN的steps用第一个维度表示,建议用这个,运行速度快一点。
#如果是False,那么输入的第二个维度就是steps。[batch_size, max_time, depth]
#如果是True,output的维度是[steps, batch_size, depth],
#final_state就是整个LSTM输出的最终的状态,包含c和h。c和h的维度都是[batch_size, n_hidden]
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
o1, s1 = sess.run([output, final_state])
print("o1:",np.shape(o1))
print(o1)
print("--------------分割线-------------")
print("s1:",np.shape(s1))
print(s1[1])
#print(sess.run([output,final_state]))
tf.reset_default_graph()
以上代码的输出结果如下:
inputs.shape= (4, 3, 6)
Tensor("random_normal:0", shape=(4, 3, 6), dtype=float32)
o1: (4, 3, 10)
[[[ 0.11675259 0.10834437 -0.11760951 -0.16499795 -0.12800275
-0.04105048 0.06492673 -0.04715323 0.08390025 -0.15421923]
[ 0.1077038 0.15109685 0.07964695 -0.3384151 -0.10303801
-0.09303306 0.00935416 0.00841029 0.11700577 -0.11993322]
[-0.03809141 0.00617996 0.06268601 -0.12101521 -0.03785394
0.01157774 0.09201031 0.11435588 0.06480291 -0.07531944]]
[[-0.07236119 -0.15773843 -0.13357502 0.07174082 -0.03322748
0.09571467 0.14610699 -0.01372721 -0.03793286 0.07912478]
[-0.08712895 -0.07700258 -0.03718312 0.13961595 0.04127593
0.14114884 0.05352723 -0.12297448 -0.1075298 -0.01109935]
[-0.02151851 -0.10921723 -0.18571903 0.0759456 -0.04821194
0.12550941 0.13462071 -0.16062255 -0.03908236 -0.07705115]]
[[ 0.01077853 0.02665833 0.07161952 0.00905736 0.02202087
-0.09277559 -0.03952483 0.06880546 0.03590827 0.10169523]
[-0.07577747 -0.00217751 0.06121065 0.03796327 0.03663403
0.06855082 -0.18908596 -0.06048196 -0.16463414 -0.1495474 ]
[-0.15763797 -0.01071369 0.08756836 0.1574932 0.13014945
0.155852 -0.33482167 -0.19914962 -0.29694003 -0.41591448]]
[[ 0.03347449 0.1368615 0.2151585 -0.05400553 0.18862696
-0.1954773 -0.07700919 0.13760252 0.08745126 0.06443474]
[ 0.19846883 0.17776097 0.03971095 -0.05234902 -0.04460184
-0.25098005 0.02780598 0.05854506 0.17640008 0.02584271]
[ 0.15725945 0.21813194 0.13022186 -0.1683359 0.05236974
-0.31286812 -0.12361103 0.01360542 0.06070696 -0.03747219]]]
--------------分割线-------------
s1: (2, 4, 10)
[[-0.03809141 0.00617996 0.06268601 -0.12101521 -0.03785394 0.01157774
0.09201031 0.11435588 0.06480291 -0.07531944]
[-0.02151851 -0.10921723 -0.18571903 0.0759456 -0.04821194 0.12550941
0.13462071 -0.16062255 -0.03908236 -0.07705115]
[-0.15763797 -0.01071369 0.08756836 0.1574932 0.13014945 0.155852
-0.33482167 -0.19914962 -0.29694003 -0.41591448]
[ 0.15725945 0.21813194 0.13022186 -0.1683359 0.05236974 -0.31286812
-0.12361103 0.01360542 0.06070696 -0.03747219]]
大家可以注意到,最后一维数字是10,正是n_weight的值,并且state的输出H正好和output的最后一个timestep的输出H是一样的。
注意
很多博客里会将n_wright的值与神经元个数设置为一样,造成很多博客里认为输出H的值会是跟神经元个数一样,而且很多博客里都会让n_weight与输入的特征数目一样,这样就会让人以为输出的H跟输入特征数量有关,当然n_weight是可以随意设置的,具体可以根据你的神经网络的设计来考虑,不过大家应该清楚这个值应该具体跟什么有关,以下参考的博客中都有这样的问题,希望大家可以通过查看参考博客来对比认识一下。
总结
稍微分析了一下这个 tf.nn.dynamic_rnn方法的两个输出,这样便于理解用法,同时参考了很多人的博客,同时本文有一些是自己的猜测,这里不能说全队,希望读者有疑问可以提出来,大家一起学习。
参考博客
tensorflow高阶教程:tf.dynamic_rnn
TensorFlow入门(五)多层 LSTM 通俗易懂版
tf.nn.dynamic_rnn :函数使用和输出
tf.nn.dynamic_rnn 详解