这里我们解释一下tf.nn.rnn_cell.BasicLSTMCell(), tf.nn.dynamic_rnn()的用法。
__init__(
num_units,
forget_bias=1.0,
state_is_tuple=True,
activation=None,
reuse=None,
name=None,
dtype=None,
**kwargs
)
def call(self, inputs, state):
"""Long short-term memory cell (LSTM).
Args:
inputs: `2-D` tensor with shape `[batch_size, input_size]`.
state: An `LSTMStateTuple` of state tensors, each shaped
`[batch_size, self.state_size]`, if `state_is_tuple` has been set to
`True`. Otherwise, a `Tensor` shaped
`[batch_size, 2 * self.state_size]`.
Returns:
A pair containing the new hidden state, and the new state (either a
`LSTMStateTuple` or a concatenated state, depending on
`state_is_tuple`).
"""
当我们输入的是一个句子时, 一下面代码为例,假设一个batch,每个batch两个单词,每个单词用5维向量表示。仔细观察会发现,虽然我们定义了句子长为2, 但是依然只用一个lstm做,当下面我们介绍tf.nn.dynamic_rnn()会给予解释。
import tensorflow as tf
print(tf.__version__)
input_data = tf.random.uniform([1, 2, 5], dtype=tf.float32)
rnn_cell = tf.nn.rnn_cell.BasicRNNCell(10)
# defining initial state
initial_state = rnn_cell.zero_state(1, dtype=tf.float32)
inputs=input_data, dtype=tf.float32)
out2, state2 = input_data[:, 0, :], initial_state
for i in range(2):
out2,state2 = rnn_cell(input_data[:,i,:], state2)
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.333)
print(np.sum([np.prod(v.get_shape().as_list()) for v in tf.trainable_variables()]))
with tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) as sess:
sess.run(tf.global_variables_initializer())
o2 = sess.run([out2])
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
)
cell:LSTM、GRU等的记忆单元。cell参数代表一个LSTM或GRU的记忆单元,也就是一个cell。例如,cell = tf.nn.rnn_cell.LSTMCell((num_units),其中,num_units表示rnn cell中神经元个数,也就是下文的cell.output_size。返回一个LSTM或GRU cell,作为参数传入。
inputs:输入的训练或测试数据,一般格式为[batch_size, max_time, embed_size],其中batch_size是输入的这批数据的数量,max_time就是这批数据中序列的最长长度,embed_size表示嵌入的词向量的维度。
sequence_length:是一个list,假设你输入了三句话,且三句话的长度分别是5,10,25,那么sequence_length=[5,10,25]。
time_major:决定了输出tensor的格式,如果为True, 张量的形状必须为 [max_time, batch_size,cell.output_size]。如果为False, tensor的形状必须为[batch_size, max_time, cell.output_size],cell.output_size表示rnn cell中神经元个数。
返回值:元组(outputs, states)
outputs:outputs很容易理解,就是每个cell会有一个输出
states:states表示最终的状态,也就是序列中最后一个cell输出的状态。一般情况下states的形状为 [batch_size, cell.output_size ],但当输入的cell为BasicLSTMCell时,state的形状为[2,batch_size, cell.output_size ],其中2也对应着LSTM中的cell state和hidden state。
tf.nn.dynamic_rnn的作用你可以这样理解,学习lstm理论的时候,我们都知道,要将每一时刻lstm的输入有两个,一个上一时刻的隐藏 状态,一个是inputs,这个函数就结合了时刻,所以输入的是一个三维向量[batch, time_step, embed_size],也许你会理解有多少个时刻就有多少个lstm,一下面的例子为例,时刻长为2,就应该有两个lstm,但事实上只有一个lstm,你可以理解所有的时刻都共享一个lstm,我们通过输出参数的个数可以得知,这里介绍一下如何在tensorflow计算参数个数。
上图是个经典的lstm结构图,假隐藏向量维度是h,输入维度是m。从图中看出,一共有四个参数矩阵,所谓的门,其实就是一个全连接层,用来过滤信息。四个矩阵分别为 W f , W i , W c , W o , σ W_f, W_i, W_c, W_o, \sigma Wf,Wi,Wc,Wo,σ是sigmoid函数,tanh是tanh激活函数。
想一下为什没激活函数有两种?还有为什么要用sigmoid和tanh?
所谓的门,就是控制有多少信息被保留,有多少信息被过滤掉。考虑极端情况,如果门=0那么所有信息都被过滤掉,门=1,所有信息都保留。那么sigmoid函数非常符合这个特性,所以选择sigmoid为门的激活函数。
tanh 用在了状态和输出上,是对数据的处理,这个用其他激活函数或许也可以。
遗忘门:根据 x t , h t − 1 x_t, h_{t-1} xt,ht−1决定上一时刻的单元状态 c t − 1 c_t-1 ct−1有多少保留到 c t c_t ct
f t = σ ( W f ∗ [ h t − 1 , x t ] + b f ) f_t = \sigma(W_f*[h_{t-1}, x_t]+b_f) ft=σ(Wf∗[ht−1,xt]+bf)
输入门, 根据 x t , h t − 1 x_t, h_{t-1} xt,ht−1决定当前输入 x t x_t xt有多少保留到 c t c_t ct
i t = σ ( W i ∗ [ h t − 1 , x t ] + b i ) i_t = \sigma(W_i*[h_{t-1}, x_t]+b_i) it=σ(Wi∗[ht−1,xt]+bi)
当前单元状态 ,根据上一次的输出和本次输入来计算的:
c t m = t a n h ( W c ∗ [ h t − 1 , x t ] + b c ) c^m_t =tanh(W_c*[h_{t-1}, x_t]+b_c) ctm=tanh(Wc∗[ht−1,xt]+bc)
利用输入门,遗忘门,更新单元状态 c t c_t ct
c t = f t ∘ c t − 1 + i t ∘ c t m c_t = f_t\circ c_{t-1} + i_t\circ c^m_t ct=ft∘ct−1+it∘ctm
输出门,根据上一次的输出和本次输入来计算的:
o t = σ ( W o ∗ [ h t − 1 , x t ] + b o ) o_t = \sigma(W_o*[h_t-1,x_t]+b_o) ot=σ(Wo∗[ht−1,xt]+bo)
h t = o t ∗ t a n h ( C t ) h_t=o_t * tanh(C_t) ht=ot∗tanh(Ct)
最后再利用输出门 o t o_t ot和单元状态 c t c_t ct来决定当前隐藏状态 h t h_t ht
仔细观察上面的式子,一共有四个矩阵,而且矩阵的shape相同,都是[h+m,h],在tf中为了节省空间,将四个门结合成一个,大W的shape为[h+m, 4*h],所以每个lstm单元参数个数为 4 ∗ h ∗ ( h + m ) + 4 ∗ h 4*h*(h+m)+4*h 4∗h∗(h+m)+4∗h,最后加上bias,在本例子中每个cell参数个数为160, 当你输出下面的模型的参数时,参数个数为160,所以每一时刻共享同一lstm,而且自动添加时刻的方法tf.nn.dynamic_rnn和手动添加时刻的方法的输出结果相同,但是要注意,tf.nn.dynamic_rnn和tf.nn.rnn_cell.BasicRNNCell输入的维度不同一个是3-D的一个是2-D。
import tensorflow as tf
print(tf.__version__)
# def basic_rnn_demo():
input_data = tf.random.uniform([1, 2, 5], dtype=tf.float32)
# create a BasicRNNCell
rnn_cell = tf.nn.rnn_cell.BasicRNNCell(10)
# defining initial state
initial_state = rnn_cell.zero_state(1, dtype=tf.float32)
out, state = tf.nn.dynamic_rnn(cell=rnn_cell,initial_state=initial_state, inputs=input_data, dtype=tf.float32)
out2, state2 = input_data[:, 0, :], initial_state
for i in range(2):
out2,state2 = rnn_cell(input_data[:,i,:], state2)
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.333)
print(np.sum([np.prod(v.get_shape().as_list()) for v in tf.trainable_variables()]))
with tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) as sess:
sess.run(tf.global_variables_initializer())
o, o2 = sess.run([out, out2])
print(o.shape)
print(o2.shape)
print(o[:, -1, :])
print(o2)
总结一下我遇到的问题