Dropout
关于Dropout的博文网上很多,今晚同学问了我关于Dropout具体是怎么实现的,一时答不出来。
然后就查看了下Caffe的源码,代码思路很清晰,很简洁。
思路
前向: 训练时,利用伯努利分布,随机出一个只包含0,1的mask矩阵,然后利用这个mask去对应乘上每个输入,得到的就是Dropout后的结果;测试时,Dropout并不起作用。
后向:训练时,根据mask来求对应梯度;测试时,与无Dropout一致。
参数
message DropoutParameter {
// dropouot_ratio 即伯努利分布中p的值,源码中根据1-p的概率选取0,使特征失效
optional float dropout_ratio = 1 [default = 0.5]; // dropout ratio
}
代码
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) {
// Create random numbers
caffe_rng_bernoulli(count, 1. - threshold_, mask); // Important,
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);
}
}
template <typename Dtype>
void DropoutLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down,
const vector<Blob<Dtype>*>& bottom) {
if (propagate_down[0]) {
const Dtype* top_diff = top[0]->cpu_diff();
Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
if (this->phase_ == TRAIN) {
const unsigned int* mask = rand_vec_.cpu_data();
const int count = bottom[0]->count();
for (int i = 0; i < count; ++i) {
bottom_diff[i] = top_diff[i] * mask[i] * scale_;
}
} else {
caffe_copy(top[0]->count(), top_diff, bottom_diff);
}
}
}
反思
Dropout用了这么久,却没有注意到具体是怎么实现的,这是学习上的疏忽。
以后对自己用到的每一种网络层都需要了解原理,明白实现细节。
2017.09.03 记