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

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

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

话不多说,先上图。
在这里插入图片描述
简单以三个输入以及两层结构作为简要说明。

X t X_t Xt 表示时间步的输入
Y t Y_t Yt 表示时间步的输出
s t a t e state state 表示每一层的初始化state
n e w s t a t e new state newstate 表示每一层最后一个cell输出的新状态

去理解一个网络架构的时候,看图还是相对要简单很多的。
第一层是以 X X X作为网络的输入
而第二层之后的层数则是以前面一层的输出作为输入,直到最后一层得到 Y Y 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是 c c c h h h ,而 BasicRNNCell只有 c c 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,维度当然是可以不同的了。

所以要保证:每个 c e l l cell cell都是单独的 c e l l 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中分别都用了 d r o p o u t dropout dropout。在 r n n rnn rnn中的 d r o p o u t dropout dropout c n n cnn cnn d r o p o u t dropout dropout还是有区别的。
RECURRENT NEURAL NETWORK REGULARIZATION
r n n rnn rnn中的 d r o p o u t dropout dropout不会在一层的内部计算中做 d r o p o u t dropout dropout,而是在每层的输入或者输出的时候做 d r o p o u t dropout dropout
如图:
在这里插入图片描述
和上图相比,输入 X t − 1 X_{t-1} Xt1是一个虚线,表示这个输入 d r o p o u t dropout dropout,设置为0,
而第一层的输出 X t + 1 X_{t+1} Xt+1也是虚线,表示这个输出 d r o p o u t dropout dropout,设置为0,
最后一层的输出 Y t Y_{t} Yt 也是虚线,表示这个输出 d r o p o u t dropout dropout,设置为0,

r n n rnn rnn d r o p o u t dropout dropout中,只在层与层之间做 d r o p o u t dropout dropout。在层内部是不做 d r o p o u t 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)

最后再来简单讲下 d r o p o u t dropout 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)

关键的是:
r e t = d i v ( x , k e e p p r o b ) ∗ b i n a r y T e n s o r ret = div(x, keepprob) * binaryTensor ret=div(x,keepprob)binaryTensor
先给每个输入的值 除以 1 / 0.5 1 / 0.5 1/0.5 也就是乘以 2 2 2。然后再乘以 0 0 0 或者 1 1 1 b i n a r y binary binary t e n s o r tensor tensor.
从demo中是可以看到这一点操作的。
这样做的原因,其实是由于:
假设 k e e p keep keep_ p r o b = 0.5 prob=0.5 prob=0.5,理论上有一半的输出会被设置为 0 0 0,
结果近似的缩小了 2 2 2倍。
如果我们 r e s c a l e rescale rescale 每一个输出到原来的 2 2 2倍,那么 e x p e c t e d expected expected s u m sum sum的值就很近似了,不会出现太大的差别。

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.

我们都知道训练的时候会设置 d r o p o u t dropout dropout,而在预测的时候是不会设置 d r o p o u t dropout dropout的,所以我们在训练的时候对没有 d r o p o u t dropout dropout的值进行 r e s c a l e rescale rescale,这样就能保证在预测的时候结果不会差的很大了。

谢谢。

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值