当前位置:   article > 正文

Dropout理解_dropout的值是丢弃率还是保留率

dropout的值是丢弃率还是保留率

本文转自http://geek.csdn.net/news/detail/161276


4. Dropout

4.1 Dropout简介

dropout是一种防止模型过拟合的技术,这项技术也很简单,但是很实用。它的基本思想是在训练的时候随机的dropout(丢弃)一些神经元的激活,这样可以让模型更鲁棒,因为它不会太依赖某些局部的特征(因为局部特征有可能被丢弃)。

图片描述

上图a是标准的一个全连接的神经网络,b是对a应用了dropout的结果,它会以一定的概率(dropout probability)随机的丢弃掉一些神经元。

4.2 Dropout的实现

实现Dropout最直观的思路就是按照dropout的定义来计算,比如上面的3层(2个隐藏层)的全连接网络,我们可以这样实现:

  1. """ 最原始的dropout实现,不推荐使用 """
  2. p = 0.5 # 保留一个神经元的概率,这个值越大,丢弃的概率就越小。
  3. def train_step(X):
  4. H1 = np.maximum(0, np.dot(W1, X) + b1)
  5. U1 = np.random.rand(*H1.shape) < p # first dropout mask
  6. H1 *= U1 # drop!
  7. H2 = np.maximum(0, np.dot(W2, H1) + b2)
  8. U2 = np.random.rand(*H2.shape) < p # second dropout mask
  9. H2 *= U2 # drop!
  10. out = np.dot(W3, H2) + b3
  11. # 反向梯度计算,代码从略
  12. def predict(X):
  13. H1 = np.maximum(0, np.dot(W1, X) + b1) * p # NOTE: scale the activations
  14. H2 = np.maximum(0, np.dot(W2, H1) + b2) * p # NOTE: scale the activations
  15. out = np.dot(W3, H2) + b3
'
运行

我们看函数 train_step,正常计算第一层的激活H1之后,我们随机的生成dropout mask数组U1。它生成一个0-1之间均匀分布的随机数组,然后把小于p的变成1,大于p的变成0。极端的情况,p = 0,则所有数都不小于p,因此U1全是0;p=1,所有数都小于1,因此U1全是1。因此越大,U1中1越多,也就keep的越多,反之则dropout的越多。 
然后我们用U1乘以H1,这样U1中等于0的神经元的激活就是0,其余的仍然是H1。 
第二层也是一样的道理。

predict函数我们需要注意一下。因为我们训练的时候会随机的丢弃一些神经元,但是预测的时候就没办法随机丢弃了【我个人觉得也不是不能丢弃,但是这会带来结果会不稳定的问题,也就是给定一个测试数据,有时候输出a有时候输出b,结果不稳定,这是实际系统不能接受的,用户可能认为你的模型有”bug“】。那么一种”补偿“的方案就是每个神经元的输出都乘以一个p,这样在”总体上“使得测试数据和训练数据是大致一样的。比如一个神经元的输出是x,那么在训练的时候它有p的概率keep,(1-0)的概率丢弃,那么它输出的期望是p x+(1-p) 0=px。因此测试的时候把这个神经元乘以p可以得到同样的期望。

但是这样测试的时候就需要多一次乘法,我们对于训练的实时性要求没有测试那么高。所以更为常见的做法是如下面的代码:

  1. p = 0.5 # 保留一个神经元的概率,这个值越大,丢弃的概率就越小。
  2. def train_step(X):
  3. H1 = np.maximum(0, np.dot(W1, X) + b1)
  4. U1 = (np.random.rand(*H1.shape) < p) / p # first dropout mask. Notice /p!
  5. H1 *= U1 # drop!
  6. H2 = np.maximum(0, np.dot(W2, H1) + b2)
  7. U2 = (np.random.rand(*H2.shape) < p) / p # second dropout mask. Notice /p!
  8. H2 *= U2 # drop!
  9. out = np.dot(W3, H2) + b3
  10. def predict(X):
  11. # ensembled forward pass
  12. H1 = np.maximum(0, np.dot(W1, X) + b1) # no scaling necessary
  13. H2 = np.maximum(0, np.dot(W2, H1) + b2)
  14. out = np.dot(W3, H2) + b3
'
运行

上面的代码在训练的时候给U1的每个元素除以p,相当于给H1放大1/p倍,那么预测的时候,那么后面层的参数学到的就相当于没有dropout的情况,因此预测的时候就不需要再乘以p了。比如第1层的输出是100个神经元,假设每一个神经元的输出都是0.5。如果没有dropout,这100个神经元都会连接到第二层的第一个神经元,假设第二层的第一个神经元的参数都是1,那么它的累加和是50。如果使用了0.5的概率保留,则第二层第一个神经元的累加和变成了25。但是上面的算法,我们对每个神经元的输出都先除以0.5。也就是第一层每个输出都是1,一个100个输入给第二层的第一个神经元,然后又以0.5的概率丢弃,那么最终累加的结果还是50。这样就”相当于“补偿”了第二层的“损失”。

4.3 实现

我们打开作业2的Dropout.ipynb。

4.3.1 cell1-2

和之前的一样的代码

4.3.2 cell3

打开layers.py,把dropout_forward里缺失的代码实现如下:

  1. if mode == 'train':
  2. ###########################################################################
  3. # TODO: Implement the training phase forward pass for inverted dropout. #
  4. # Store the dropout mask in the mask variable. #
  5. ###########################################################################
  6. [N,D] = x.shape
  7. mask = (np.random.rand(N,D) < (1-p))/(1-p)
  8. out = x * mask
  9. ###########################################################################
  10. # END OF YOUR CODE #
  11. ###########################################################################
  12. elif mode == 'test':
  13. ###########################################################################
  14. # TODO: Implement the test phase forward pass for inverted dropout. #
  15. ###########################################################################
  16. out = x
  17. ###########################################################################
  18. # END OF YOUR CODE #
  19. ###########################################################################

代码非常简单,不过注意的是这里的p是丢弃的概率,所以保留的概率是(1-p)。

运行这个cell的结果如下图:

图片描述

注意使用不同的dropout的结果是差不多的,因为我们实现的方法是“补偿”,如果您使用第一种实现,那么均值应该随着p而变化

4.3.3 cell4

接下来是实现dropout_backward,这也非常简单,只有一行代码:

if mode == ‘train’:

  1. ###########################################################################
  2. # TODO: Implement the training phase backward pass for inverted dropout. #
  3. ###########################################################################
  4. dx = mask * dout
  5. ###########################################################################
  6. # END OF YOUR CODE #
  7. ###########################################################################

然后我们运行cell4进行gradient check

图片描述

4.3.4 cell5

然后我们需要修改fc_net.py来增加dropout的支持,具体来说,如果dropout参数不是空,那么每一个relu的输出都需要增加一个dropout。

4.3.4.1 loss函数

init里代码已经处理好了,不需要任何修改,我们只需要修改loss的forward和backward部分。

首先是loss函数的forward部分:

  1. dropout_cache = {}
  2. for i in range(1, self.num_layers):
  3. keyW = 'W' + str(i)
  4. keyb = 'b' + str(i)
  5. if not self.use_batchnorm:
  6. current_input, affine_relu_cache[i] = affine_relu_forward(current_input, self.params[keyW], self.params[keyb])
  7. else:
  8. key_gamma = 'gamma' + str(i)
  9. key_beta = 'beta' + str(i)
  10. current_input, affine_bn_relu_cache[i] = affine_bn_relu_forward(current_input, self.params[keyW],
  11. self.params[keyb],
  12. self.params[key_gamma], self.params[key_beta],
  13. self.bn_params[i - 1])
  14. if self.use_dropout:
  15. current_input, dropout_cache[i] = dropout_forward(current_input,self.dropout_param)

和之前的代码比其实就增加了两行,如果self.use_dropout。 
当然还要记得在外面定义dropout_cache这个dict

然后是backward部分:

  1. for i in range(self.num_layers - 1, 0, -1):
  2. if self.use_dropout:
  3. affine_dx = dropout_backward(affine_dx, dropout_cache[i])
  4. if not self.use_batchnorm:
  5. affine_dx, affine_dw, affine_db = affine_relu_backward(affine_dx, affine_relu_cache[i])
  6. else:
  7. affine_dx, affine_dw, affine_db, dgamma, dbeta = affine_bn_relu_backward(affine_dx, affine_bn_relu_cache[i])
  8. grads['beta' + str(i)] = dbeta
  9. grads['gamma' + str(i)] = dgamma

在for循环的最上面增加 if self.use_dropout这两行。 
注意顺序,我们是把dropout放到激活函数之后,因此反向求梯度是要最先计算affine_dx

完成代码后我们执行这个cell坚持梯度是否正确计算:

图片描述

4.3.5 cell6-7

As an experiment, we will train a pair of two-layer networks on 500 training examples: one will use no dropout, and one will use a dropout probability of 0.75. We will then visualize the training and validation accuracies of the two networks over time.

接下来我们做一个实验,我们会用500个数据训练一对2层的网络:其中一个使用dropout(0.75的丢弃概率),一个不用。

训练数据上的准确率

图片描述

验证数据上的准确率

图片描述

从上面两个图可以看出:不使用dropout,训练数据很少时会过拟合,在训练数据上准确率100%,但是验证数据上只有28%。而使用了dropout之后,训练数据90%,但是验证数据上能提高到30%以上。

这说明dropout确实能缓解过拟合的问题。

本篇文章介绍了Dropout技术,在下一篇文章中,我们将深入讲解一个CNN网络的具体实现。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/826353
推荐阅读
相关标签
  

闽ICP备14008679号