tensorflow lstm 详解

这里我们解释一下tf.nn.rnn_cell.BasicLSTMCell(), tf.nn.dynamic_rnn()的用法。

1 tf.nn.rnn_cell.BasicLSTMCell()

__init__(
    num_units,
    forget_bias=1.0,
    state_is_tuple=True,
    activation=None,
    reuse=None,
    name=None,
    dtype=None,
    **kwargs
)

  • num_units 是lstm中隐藏状态h的维度大小
  • forget_bias从CudnnLSTM训练的检查点(checkpoin)恢复时,必须手动设置为0.0。这个太明白,以后再说。
  • state_is_tuple如果为True,则接受和返回的状态是c_state和m_state的2-tuple;如果为False,则他们沿着列轴连接。后一种即将被弃用。
  • activation:内部状态的激活函数。默认为tanh
  • reuse:布尔类型,描述是否在现有范围中重用变量。如果不为True,并且现有范围已经具有给定变量,则会引发错误。
  • name:String类型,层的名称。具有相同名称的层将共享权重,但为了避免错误,在这种情况下需要reuse=True.
  • dtype:该层默认的数据类型。默认值为None表示使用第一个输入的类型。在call之前build被调用则需要该参数。
    当声明完lstm后,要调用l函数,参数有两个
  • inputs 是一个2-Dtensor shape=[batch_size, input_size],即每一时刻的输入,例如输入的batch为1,‘word’这个单词的编码为[1,0,0,0]那么输入到大小为[1, 4]
  • state 上一时刻的隐藏状态,默认是一个tuple,每个tuple的大小为[batch, state_size]
 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])
    

2 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
)
  • 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计算参数个数。
    tensorflow lstm 详解_第1张图片
    上图是个经典的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,ht1决定上一时刻的单元状态 c t − 1 c_t-1 ct1有多少保留到 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[ht1,xt]+bf)
    输入门, 根据 x t , h t − 1 x_t, h_{t-1} xt,ht1决定当前输入 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[ht1,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[ht1,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=ftct1+itctm
    输出门,根据上一次的输出和本次输入来计算的:
    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[ht1,xt]+bo)
    h t = o t ∗ t a n h ( C t ) h_t=o_t * tanh(C_t) ht=ottanh(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 4h(h+m)+4h,最后加上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)

总结一下我遇到的问题

  1. tf.nn.rnn_cell.BasicRNNCell中 num_units 是lstm中隐藏状态h的维度大小
  2. 当输入到句子长度不为一时,其实所有时刻共用一个时刻的lstm cell
  3. tf.nn.rnn_cell.BasicRNNCell,可以实现 tf.nn.dynamic_rnn的功能,只是输入参数的维度不同
  4. tf中参数量的计算方式为 4 ∗ h ∗ ( h + m ) + 4 ∗ h 4*h*(h+m)+4*h 4h(h+m)+4h

你可能感兴趣的:(深度学习)