CNN基础


CNN一般结构

卷积层作用:

  1. 提取不同维度的特征,组合不同维度特征,其本质是卷积核,因此,学习一个有效的总卷积核是训练卷积层主要工作
    2)寻找不同位置,不同大小的特征
  2. 根据卷积核参数计算上下层blob之前维度关系
input => conv => output:
out = (W-F+2*P)/S + 1
W:input的尺寸
F:kernel的尺寸
S:步长
P:padding的数量

非线性层

控制对不同特征特征信号所应当作出的反应
如,RELU:

\[f(x)=max(0,x) \]

  1. 对低强度特征信息不做反应,超过阈值后,强度越大,反应相应越大
  2. 阈值一般为0,因此样本数据与特征也尽量零均值,这可能是训练数据归一化及batchnormalization的原因

池化层

  1. 降采样:将高维特征稀疏为低维特征
    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