深度学习中Dropout优化的原理分析

来源:互联网 发布:世达15件网络寻线器 编辑:程序博客网 时间:2024/06/05 16:26

本文主要参考博文:

1. http://www.aiuxian.com/article/p-1870737.html

2. http://shuokay.com/2016/06/14/dropout/

引言

在机器学习的一些模型中,如果模型的参数太多,而训练样本又太少的话,这样训练出来的模型很容易产生过拟合现象。在训练bp网络时经常遇到的这个问题,过拟合指的是模型在训练数据上损失函数比较小,预测准确率较高,但是在测试数据上损失函数比较大,预测准确率较低。

常用的防止过拟合的方法是正则化,也就是在模型的损失函数中,对模型的参数进行“惩罚”,这样的话这些参数就不会太大,而越小的参数说明模型越简单,越简单的模型则越不容易产生过拟合现象。因此在添加权值惩罚项后,应用梯度下降算法迭代优化计算时,如果参数theta比较大,则此时的正则项数值也比较大,那么在下一次更新参数时,参数削减的也比较大,从而可以使拟合结果看起来更平滑,不至于过拟合。

Dropout是hintion最近几年提出的,为了防止模型过拟合,Dropout可以作为一种trik供选择。在hinton的论文摘要中指出,在每个训练批次中,通过忽略一半的特征检测器(让一半的隐层节点值为0),可以明显地减少过拟合现象。这种方式可以减少特征检测器间的相互作用,检测器相互作用是指某些检测器依赖其他检测器才能发挥作用。

Dropout方法

训练阶段:

1.Dropout是在标准的bp网络的的结构上,使bp网的隐层激活值以一定的比例v变为0,即:按照一定比例v,随机地让一部分隐层节点失效

2.去掉权值惩罚项,取而代之的是限制权值的范围,给每个权值设置一个上限范围。如果在训练更新的过程中,权值超过了这个上限,则把权值设置为这个上限的值。这样处理,不论权值更新量有多大,权值都不会过大。此外,还可以使算法采用一个比较大的学习率来加快学习速度,从而使算法在一个更广阔的权值空间中搜索更好的权值,而不用担心权值过大。

测试阶段:

在前向传播到输出层前,隐含层节点的输出值都要缩减到(1-v)倍,例如:正常的隐层输出为a,此时需要缩减为a(1-v)。

这里我的解释是:假设比例v=0.5,也就是在训练阶段以0.5的比例忽略隐层节点,假设隐层有80个节点,每个节点输出值为1,那么此时只有40个节点正常工作,也就是说总的输出为40个1和40个0,输出总和为40;而在测试阶段,由于我们的权值已经训练完成,此时就不再按照0.5的比例忽略隐层输出,假设此时每个隐层的输出还是1,那么此时总的输出为80个1,明显比dropout训练时输出大一倍(由于dropout比例为0.5),所以为了得到和训练时一样的输出结果,就缩减隐层输出为a(1-v),即此时输出80个0.5,总和也为40。这样就使得测试阶段和训练阶段的输出“一致”了。


如上图, 左边是我们常见的 full connect layer, 右边是使用了 dropout 之后的效果。 其操作方法是, 首先设定一个 dropout ratio σ
σ是超参数, 范围设置为 (0,1), 表示在 Forward 阶段需要随机断开连接的比例。每次 Forward 的时候都要随机地断开该比例的连接, 只更新剩下的 weight。 最后, 在 test/predict 的时候使用全部的连接, 不过, 这些 weights 全部都需要乘上 1σ。在具体的实现中略有不同。

Dropout原理分析

Dropout可以看做是一种模型平均,所谓模型平均,顾名思义,就是把来自不同模型的估计或者预测通过一定的权重平均起来,在一些文献中也称为模型组合,它一般包括组合估计和组合预测。

Dropout中哪里体现了“不同模型”;这个奥秘就是我们随机选择忽略隐层节点,在每个批次的训练过程中,由于每次随机忽略的隐层节点都不同,这样就使每次训练的网络都是不一样的,每次训练都可以单做一个“新”的模型;此外,隐含节点都是以一定概率随机出现,因此不能保证每2个隐含节点每次都同时出现,这样权值的更新不再依赖于有固定关系隐含节点的共同作用,阻止了某些特征仅仅在其它特定特征下才有效果的情况。

这样dropout过程就是一个非常有效的神经网络模型平均方法,通过训练大量的不同的网络,来平均预测概率。不同的模型在不同的训练集上训练(每个批次的训练数据都是随机选择),最后在每个模型用相同的权重来“融合”,有点类似boosting算法。

代码详解

caffe 源码:

void DropoutLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
NeuronLayer<Dtype>::LayerSetUp(bottom, top);
threshold_ = this->layer_param_.dropout_param().dropout_ratio();
DCHECK(threshold_ > 0.);
DCHECK(threshold_ < 1.);
scale_ = 1. / (1. - threshold_);
uint_thres_ = static_cast<unsigned int>(UINT_MAX * threshold_);
}
template <typename Dtype>
void DropoutLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
const Dtype* bottom_data = bottom[0]->cpu_data();
Dtype* top_data = top[0]->mutable_cpu_data();
unsigned int* mask = rand_vec_.mutable_cpu_data();
const int count = bottom[0]->count();
if (this->phase_ == TRAIN) {
// 均值采样,采样的概率是留下的概率,即:1-threshold_,根据这个概率来断开连接,但是保留下的数据除以(1-threshold_),这样在test/predict的时候
就不需要像上面描述的那样乘以(1-threshold_)
caffe_rng_bernoulli(count, 1. - threshold_, mask);
for (int i = 0; i < count; ++i) {
top_data[i] = bottom_data[i] * mask[i] * scale_;
}
} else {
caffe_copy(bottom[0]->count(), bottom_data, top_data);
}
}


在caffe中的使用方法:

layer {
name: "fc6"
type: "InnerProduct"
bottom: "pool5"
top: "fc6"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 0.1
}
}
}
layer {
name: "relu6"
type: "ReLU"
bottom: "fc6"
top: "fc6"
}
# 接在full connect后面, 需要指定 dropout ratio
layer {
name: "drop6"
type: "Dropout"
bottom: "fc6"
top: "fc6"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "fc7"
type: "InnerProduct"
bottom: "fc6"
top: "fc7"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 0.1
}
}
}
layer {
name: "relu7"
type: "ReLU"
bottom: "fc7"
top: "fc7"
}
layer {
name: "drop7"
type: "Dropout"
bottom: "fc7"
top: "fc7"
dropout_param {
dropout_ratio: 0.5
}
}

0 0