CNN基础
CNN一般结构
卷积层作用:
- 提取不同维度的特征,组合不同维度特征,其本质是卷积核,因此,学习一个有效的总卷积核是训练卷积层主要工作
2)寻找不同位置,不同大小的特征
- 根据卷积核参数计算上下层blob之前维度关系
input => conv => output:
out = (W-F+2*P)/S + 1
W:input的尺寸
F:kernel的尺寸
S:步长
P:padding的数量
非线性层
控制对不同特征特征信号所应当作出的反应
如,RELU:
- 对低强度特征信息不做反应,超过阈值后,强度越大,反应相应越大
- 阈值一般为0,因此样本数据与特征也尽量零均值,这可能是训练数据归一化及batchnormalization的原因
池化层
- 降采样:将高维特征稀疏为低维特征
2)可以增强模型对特征畸变的鲁棒性,如手写数字笔记不工整
dropout:
打破网络的对称性,使网络结构不断重构,防止网络过拟合;
具体实现直接看源码dropout_layer.cpp和dropout_layer.hpp
前传过程中,bottom计算出的部分特征不参与前传过程的计算
void DropoutLayer::Forward_cpu(const vector*>& bottom, const vector*>& 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(); //从cpu中取一段可读写的内存,返回指针mask
const int count = bottom[0]->count(); //mutable_cpu_data表示可读写的内存,而cpu_data表示只读内存
if (this->phase_ == TRAIN) {
// Create random numbers
caffe_rng_bernoulli(count, 1. - threshold_, mask); //将mask内容部分以伯努利概率置为0,置0的概率与threshold有关
for (int i = 0; i < count; ++i) {
top_data[i] = bottom_data[i] * mask[i] * scale_; //用mask掩码将bottom_data中部分特征设为死结点,不参与前传中loss的计算
}
} else {
caffe_copy(bottom[0]->count(), bottom_data, top_data);
}
}
在反传过程中,上层梯度回传时至下层时,同样会一部分被mask屏蔽,而且这个maskg前传一致,保证了前传与反传过程看到的网络结构是一致的
template
void DropoutLayer::Backward_cpu(const vector*>& top, const vector& propagate_down, const vector*>& 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(); //将前传时写入mask内容重新赋值mask指针
const int count = bottom[0]->count();
for (int i = 0; i < count; ++i) {
bottom_diff[i] = top_diff[i] * mask[i] * scale_; //同样,反传过程也屏蔽了部分梯度,这样在一次前传与反传过程中所看见的网络结构实际就相同了,
} //得到bottom梯度后会给cpu或gpu进行solver的update
} else {
caffe_copy(top[0]->count(), top_diff, bottom_diff);
}
}
}
其中,mask初始化用到了一个 caffe_rng_bernoulli,在这篇文章中提到,其实它主要调用了boost::bernoulli_distribution,将向量初始化为一定比例的1其余为0
参考
dropout_layer