deep_learning 03. tf.nn.rnn_cell.MultiRNNCell()

本文深入探讨了TensorFlow中MultiRNNCell的使用方法,通过实例解析了如何堆叠基本的RNN单元来构建复杂的网络结构,并详细解释了网络中的输入、输出以及状态传递机制。

开始的话: 从基础做起,不断学习,坚持不懈,加油。 一位爱生活爱技术来自火星的程序汪

前面两节讲了两个基础的BasicRNNCellBasicLSTMCell,今天就来看下怎么把这些简单的cell给堆叠起来,一起使用。 这就是我在本章要介绍的MultiRNNCell这个接口了。

话不多说,先上图。

简单以三个输入以及两层结构作为简要说明。

X_t 表示时间步的输入 Y_t 表示时间步的输出 state 表示每一层的初始化state new state 表示每一层最后一个cell输出的新状态

去理解一个网络架构的时候,看图还是相对要简单很多的。 第一层是以X作为网络的输入 而第二层之后的层数则是以前面一层的输出作为输入,直到最后一层得到Y

show me the code

一个简单的multiRNNCell的demo

def multi_rnn_demo():
    
    m_cell = tf.nn.rnn_cell.MultiRNNCell([tf.nn.rnn_cell.BasicRNNCell(num_units=3 * i) for i in range(1, 5)])
    wrapper = tf.nn.rnn_cell.DropoutWrapper(m_cell, input_keep_prob=0.5, output_keep_prob=0.8)
    a = tf.random_normal([2, 4])
    outs, states = wrapper(
        inputs=a,
        state=m_cell.zero_state(2, dtype=tf.float32)
    )
复制代码

一个更常见的demo,也是我们前面几节常用到的代码结构。 上面的demo,可以理解为sequence_length=1时候的代码 下面的demo,就和我们平常见到的差不多了,3-D的输入。

def multi_rnn_demo_02():
    m_cell = tf.nn.rnn_cell.MultiRNNCell([tf.nn.rnn_cell.BasicRNNCell(num_units=3 * i) for i in range(1, 5)])
    wrapper = tf.nn.rnn_cell.DropoutWrapper(m_cell, input_keep_prob=0.5, output_keep_prob=0.8)
    a = tf.random_normal([2, 3, 4])
    state = m_cell.zero_state(2, tf.float32)
    output, state = tf.nn.dynamic_rnn(wrapper, a, initial_state=state, time_major=False)
复制代码

核心计算,这个demo里面还是BasicRNNCell,当然BasicLSTMCell也是一样的,唯一差别就是输出LSTM是 ch ,而 BasicRNNCell只有 c

注意:为什么有的blog中说:embedding_size 要和 hidden_size要是同样的维度呢?

这是由于他们在定义每一个cell的时候定义错了。(代码实践的时候也发现了这个问题,哈哈)

下面就进行详细说明:

	   	cell = tf.nn.rnn_cell.BasicRNNCell(num_units=6)
		m_cell = tf.nn.rnn_cell.MultiRNNCell([cell] * 3)
    #   这样定义的话是错误的,相当于是用一个cell,这样在第一层的时候,输入是[2,4](这时候的kernel_=[10,6]),输出是[2,6]
    #   gate_inputs = math_ops.matmul(array_ops.concat([inputs, state], 1), self._kernel)
    #   到第二层的时候输入是:[2,6],如果是用一个cell的话,这时候的kernel_=[10,6]
    #   而inputs和state的shape都为[2,6],这样做matmul就有问题了。
    #   所以在定义多层cells的时候一定要注意了每一个cell都应该是不同的cell。
    #   这也就是为什么一些blog说embedding_size和hidden_size要是一样的原因了。
    #   只要定义对了cell,维度当然是可以不同的了。
复制代码

所以要保证:每个cell都是单独的cell,这样就不会出现这样的问题。

再讲一下outputs和states。

outputs的结果:shape=[2,3,12],12表示的是最后一层cell的hidden_unit_size
tf.Tensor(
[[[ 0.29871088  0.08804269 -0.01215147  0.01254939 -0.14880858
    0.03153174  0.4693875   0.00485815 -0.08916988 -0.02274401
    0.10895786  0.01575602]
  [-0.00102792 -0.         -0.          0.17538904  0.09420253
    0.01793489  0.3424931  -0.18222454 -0.         -0.
    0.52473444 -0.26131606]
  [ 0.         -0.22403269 -0.4520385   0.2888178  -0.1598881
    0.15004131  0.09338585 -0.636156    0.06017611 -0.28230968
    0.6120767  -0.11296887]]

 [[-0.00742886  0.0023214  -0.00750306  0.00525704 -0.00685527
    0.00791772  0.         -0.00799915  0.00368653 -0.00367894
    0.00070026  0.00924461]
  [ 0.2665488   0.05599632 -0.          0.01829319 -0.12248072
    0.03517892  0.409784    0.         -0.08614541 -0.01222569
    0.10290711  0.        ]
  [-0.20192349 -0.01105446  0.04047677  0.          0.23573665
    0.11497773 -0.05647155 -0.0019188   0.01215919 -0.44336665
    0.          0.30004367]]], shape=(2, 3, 12), dtype=float32)
复制代码

new_states:有几层cell,则有几个state输出,和图中是一样的。每一层的shape就是该层的hidden_unit_size

<tf.Tensor: id=324, shape=(2, 3), dtype=float32, numpy=
        array([[ 0.04508227, -0.23633523,  0.30065763],
                [ 0.7612733 , -0.52450776, -0.6661888 ]], dtype=float32)>, 
    <tf.Tensor: id=331, shape=(2, 6), dtype=float32, numpy=
        array([[ 0.54584306, -0.17747684, -0.44227242,  0.2842951 ,  0.43393528,-0.63828385],
               [-0.5828087 , -0.5281809 ,  0.06895413, -0.74510646, -0.53972644,
                 0.15232728]], dtype=float32)>, 
    <tf.Tensor: id=338, shape=(2, 9), dtype=float32, numpy=
        array([[ 0.5603744 , -0.04951737, -0.26185265,  0.08209028,  0.672668  ,
                0.18829748,  0.27329427,  0.4323986 ,  0.09414196],
            [ 0.03460297, -0.20349553,  0.47373882,  0.09012283,  0.02843269,
            -0.6374881 , -0.10102204, -0.16552992,  0.35916832]],dtype=float32)>, 
    <tf.Tensor: id=345, shape=(2, 12), dtype=float32, numpy=
            array([[ 0.0955615 , -0.17922615, -0.3616308 ,  0.23105423, -0.12791048,
                     0.12003306,  0.07470868, -0.50892484,  0.04814089, -0.22584775,
                     0.48966137, -0.0903751 ],
                   [-0.1615388 , -0.00884357,  0.03238142,  0.1927497 ,  0.18858932,
                     0.09198219, -0.04517724, -0.00153504,  0.00972735, -0.35469332,
                     0.25491163,  0.24003494]], dtype=float32)>)
复制代码

认真的看的话,会发现这两个demo中分别都用了dropout。在rnn中的dropoutcnndropout还是有区别的。 RECURRENT NEURAL NETWORK REGULARIZATION rnn中的dropout不会在一层的内部计算中做dropout,而是在每层的输入或者输出的时候做dropout。 如图:

和上图相比,输入 X_{t-1}是一个虚线,表示这个输入 dropout,设置为0, 而第一层的输出 X_{t+1}也是虚线,表示这个输出 dropout,设置为0, 最后一层的输出 Y_{t} 也是虚线,表示这个输出 dropout,设置为0,

rnndropout中,只在层与层之间做dropout。在层内部是不做dropout的。 这也就是DropoutWrapper中的input_keep_prob和output_keep_prob设置的区别,只在层与层之间,也就是输入和输出做keep_prob。

tf.nn.rnn_cell.DropoutWrapper(m_cell, input_keep_prob=0.5, output_keep_prob=0.8)
复制代码

最后再来简单讲下dropout

def dropout_test():
    """
    a
    tf.Tensor(
        [[ 0.563435   -1.9721379   1.5006789  -2.2403116 ]
         [-0.39942354 -0.14369683  2.0294921  -1.0674685 ]], shape=(2, 4), dtype=float32)
         b
    tf.Tensor(
        [[ 0.        -0.         0.        -0.       ]
         [-0.7988471 -0.         4.0589843 -0.       ]], shape=(2, 4), dtype=float32)
    """
    a = tf.random_normal(shape=[2, 4])
    #   ret = math_ops.div(x, keep_prob) * binary_tensor,会看到输出的值是原来的1/0.5 倍,深度学习中正是这样解决,训练和预测过程的dropout
    #   并不是说每一个的输入中有一半的输入会为0
    b = tf.nn.dropout(x=a, keep_prob=0.5)
    print(a)
    print(b)
复制代码

关键的是: ret = div(x, keepprob) * binaryTensor 先给每个输入的值 除以 1 / 0.5 也就是乘以 2。然后再乘以 0 或者 1binarytensor. 从demo中是可以看到这一点操作的。 这样做的原因,其实是由于: 假设keep_prob=0.5,理论上有一半的输出会被设置为0, 结果近似的缩小了2倍。 如果我们rescale 每一个输出到原来的2倍,那么expectedsum的值就很近似了,不会出现太大的差别。

With probability `keep_prob`, outputs the input element scaled up by
  `1 / keep_prob`, otherwise outputs `0`.  The scaling is so that the expected
  sum is unchanged.
复制代码

我们都知道训练的时候会设置dropout,而在预测的时候是不会设置dropout的,所以我们在训练的时候对没有dropout的值进行rescale,这样就能保证在预测的时候结果不会差的很大了。

谢谢。

更多代码请移步我的个人github,会不定期更新各种框架。 本章代码见code 欢迎关注

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值