tensorflow中RNN的输出以及使用

在使用tensorflow进行rnn运算的时候,总是搞不清楚rnn的输入,输出到底是什么,所以乘此机会集中精力把这部分知识进行加深巩固。
  首先这里推荐一下李沐老师的新书:http://zh.diveintodeeplearning.org/ 我觉得这对于入门深度学习是相当好的一本书。本博客里面的很多图也都借鉴了李沐老师书中的内容。
  要知道RNN善于处理前后有联系的数据,一个最典型的例子就是文本类型的数据。因为文本存在很强的前后依赖。所以在RNN中就存在一个时间步的概念,比如我们在处理文本数据 “我爱北京天安门” 这句话的时候,我们可以将每一个字作为一个时间步的输入,每输入一个字进行一次运算得出结果并学得 “隐含知识”。这里的隐含知识又会伴随下一个时间步的输入一共输入下一步的计算中。上图:
在这里插入图片描述
  上图就是一个最简单的RNN神经网络,这里我们需要注意的是图中每一个白色的方框都代表一个时间步运算,比如Xt-1可以代表“我”, Xt可以代表“爱”,Xt+1代表“北”以此类推。所以在进行完第一次的运算以后,会产生一个结果也就是输出层的内容,并且会得到一个隐含的向量Ht-1,Ht-1会和Xt共同输入到下一步中。这里需要注意的是在tensorflow的实现中,其实output(图中的输出)和H是一样的。ouptut就是最后一步H的输出。
  下来我们需要看看在tensorflow中基本最基本RNN的实现:BasicRNNCell
  这是最基本的RNN单元,输入是样本在某个时间步的数据以及上一个时间步的隐层输出。
  某个时间步骤步数据:(batch_size, data_szie),例:[[at1, at2, at3], [bt1, bt2, bt3]],这表示在时间步t下,a样本和b样本的数据,注意这里是一个时间步,当时间向后推移又会有一批新的数据进入。
上一个隐层输出:(batch_size, num_units),这里如果运行第一个时间步,需要输入一个初始化向量,其中num_units为网络中神经元的个数。
  有了网络的输入以及上一时的输入作为输入,调用方法call(),就可以将运算向后推进一步,生成结果以及这一步的输出:output,state。这里一直有疑惑困扰,ouput和state到底是什么,理论上可以解释为output为rnn每运算一步的输出,我们需要使用它来进行分类或是回归,state为每一运算中产生的需要传入下一步的tensor,是包含前面所有步骤信息的向量。但具体是什么了?其实tensorflow为了使用的方便在这里输出是一样的,都是经过num_units个神经元计算后的结果。即output = state(只在BasicRNNCell里)

# BasicRNNCell
input_data = tf.get_variable(shape=(batch_size, 30), dtype=tf.float32, name="seq_input")
cell = tf.nn.rnn_cell.BasicRNNCell(num_units=num_units)
init_state = tf.zeros(shape=(batch_size, int(cell.state_size)))
outputs, states = cell.__call__(input_data, init_state)
print(outputs.shape) # 10 128
print(states.shape) # 10 128

但是随着输入时间步的增长,RNN在提取信息还有在反向传播着一块会遇到问题,比如:“我是个中国人,我住在北京。。。。,我说中文”。其中“中文”这个信息和“中国人”这个信息存在很强的关联,但是由于这个句子是在是太长了,RNN一个字一个字的运算过来融合了太多的信息,导致已经提取不出两个词的关联。所以我们需要一个更加智能的RNN,她需要在非常长的句子中“保存下重要的信息”和“删去无关紧要的信息”。这就是LSTM。
在这里插入图片描述
  这个图片也参考李沐老师新书。还是和RNN一样,图中的白色大方框就代表一个时间步的运算。首先我们需要看看LSTM的输出和基础RNN有什么不同,很明显LSTM每一个时间步运算会产生三个变量:output,C,H。其中和基础RNN一样ouput=H。那C是什么呢?其实C的在LSTM中的功能就是控制“遗忘”和“输入”的。更加细致来说的话,它是由两个门单元“遗忘门”,“输入门”并结合“候选记忆细胞”共同作用产生的。其中“遗忘门”利用Ht-1和Xt并使用sigmod激活函数产生Ft,“输入门”同样利用Ht-1和Xt使用sigmod激活函数产生It。“记忆细胞”利用Ht-1和Xt使用激活函数tanh产生C~t(姑且这么写),最后几个变量共同运算产生这一事件步的Ct来控制变量的选取。
              Ct = Ft × Ct-1 + It × C~t
 
  还是来看看,tensorflow中的LSTM实现:BasicLSTMCell
  BasicLSTMCell相比较BasicRNNCell多了一个c输出,和BasicRNNCell一样,output和h是一样的。所以BasicLSTMCell的输出为:outputs, (c, h)。

# BasicLSTMCell
input_data = tf.get_variable(shape=(batch_size, 30), dtype=tf.float32, name="seq_input")
cell = tf.nn.rnn_cell.BasicLSTMCell(num_units=num_units)
init_state = cell.zero_state(batch_size, dtype=tf.float32)
outputs, (c, h) = cell.__call__(input_data, init_state)

这里还有一个和LSTM类似的RNN:GRU。它也是为了解决RNN长依赖问题而提出的。也是通过两个门单元来实现“记忆”与“遗忘”。但是与LSTM不同的是没有“记忆细胞”的存在,神经元更加简单明了,其实与LSTM差球不多。

在这里插入图片描述

dynamic_rnn
  动态调用RNN传递,上面我们所说的BasicRNNCell和BasicLSTMCell在调用call()方法的时候,都在时间步骤上向后计算一个步骤,然后传入下一个时间的数据再次调用call()方法。Tensorflow提供了一个更加便利的接口供我们使用:dynamic_rnn,自动向后传递的方法。该方法需要将所有时间步的数据一次性传入,然后该方法会自动向后进行运算。所以相对BasicRNNCell和BasicLSTMCell的输入来说,dynamic_rnn的输入格式为:(batch_size, time_steps, data_size)。如果迭代的基本单元是BasicLSTMCell,对应的输出为output, (c, h),其中output为所有时间步骤运算的输出其格式为(batch_size, time_steps, num_units)。其中c和h为最后一步的主线向量和隐层输出。所以可以理解为 output[:, -1, :] = h。因为在上述我们也讨论过其实在基础的cell中output和h是相等的。

# dynamic_rnn
input_data = tf.get_variable(shape=(batch_size, 20, 30), dtype=tf.float32, name="seq_input")
cell = tf.nn.rnn_cell.BasicLSTMCell(num_units=num_units)
init_state = cell.zero_state(batch_size, dtype=tf.float32)
outputs, (c, h) = tf.nn.dynamic_rnn(cell, input_data, initial_state=init_state)

print(outputs.shape)
print(c.shape)
print(h.shape)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    get_o, get_c, get_h = sess.run([outputs, c, h])
    print(get_o[0][-1][:10])
    print(get_c[0][:10])
    print(get_h[0][:10])

MultiRNNCell
  上面所说的所有rnn实现中,在计算时间步的时候只计算了单层的RNN,也就是说神经网络只有一层隐层, 但是我们为了增加网络的拟合效果,所以可以使用多层隐层的RNN网络来进行时间步计算。在tensorflow中可以使用MultiRNNCell实现多层网络的堆叠。然后使用上面提到的dynmic_rnn进行数据的计算。所以MultiRNNCell对应的输出为:
  Outputs, [(c, h), (c, h), (c, h)],其中outputs与上述的一致,只是states由原来的一个(c, h)变为一个(c, h)的list,list的个数就是堆叠RNN隐层的个数。


# MultiRNNCell
input_data = tf.get_variable(shape=(batch_size, 20, 30), dtype=tf.float32, name="seq_input")
def get_cell():
    return tf.nn.rnn_cell.BasicLSTMCell(num_units=num_units)
mul_cell = tf.nn.rnn_cell.MultiRNNCell([get_cell() for _ in range(2)])
init_state = mul_cell.zero_state(batch_size, dtype=tf.float32)
outputs, states = tf.nn.dynamic_rnn(mul_cell, input_data, initial_state=init_state)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    get_o, get_s = sess.run([outputs, states])
    print(get_o[0][-1][:10])
    print(get_s[-1][1][0][:10])

最后加入一个自己的理解图:
图中,一次前向传递,就相当于运算一个时间步,计算一次dynamic_rnn。并且因为有三层堆叠,所以会产生三个(c, h), 将最后一层隐层的h添加到output中,并且保留最后一次dynamic_rnn迭代的向量:3个(c, h)
  图中,一次前向传递,就相当于运算一个时间步,计算一次dynamic_rnn。并且因为有三层堆叠,所以会产生三个(c, h), 将最后一层隐层的h添加到output中,并且保留最后一次dynamic_rnn迭代的向量:3个(c, h)

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值