深度学习笔记4:卷积层的实现

来源:互联网 发布:c语言打印布尔值 编辑:程序博客网 时间:2024/06/03 23:54

卷积层的推导

卷积层的前向计算

如下图,卷积层的输入来源于输入层或者pooling层。每一层的多个卷积核大小相同,在这个网络中,我使用的卷积核均为5*5。


如图输入为28*28的图像,经过5*5的卷积之后,得到一个(28-5+1)*(28-5+1) = 24*24、的map。卷积层2的每个map是不同卷积核在前一层每个map上进行卷积,并将每个对应位置上的值相加然后再加上一个偏置项。


每次用卷积核与map中对应元素相乘,然后移动卷积核进行下一个神经元的计算。如图中矩阵C的第一行第一列的元素2,就是卷积核在输入map左上角时的计算结果。在图中也很容易看到,输入为一个4*4的map,经过2*2的卷积核卷积之后,结果为一个(4-2+1) *(4-2+1) = 3*3的map。

卷积层的后向计算

之前的笔记中我有写到:在反向传播过程中,若第x层的a节点通过权值W对x+1层的b节点有贡献,则在反向传播过程中,梯度通过权值W从b节点传播回a节点。不管下面的公式推导,还是后面的卷积神经网络,在反向传播的过程中,都是遵循这样的一个规律。

卷积层的反向传播过程也是如此,我们只需要找出卷积层L中的每个单元和L+1层中的哪些单元相关联即可。我们还用上边的图片举例子。

在上图中,我们的矩阵A11通过权重B11与C11关联。而A12与2个矩阵C中2个元素相关联,分别是通过权重B12和C11关联,和通过权重B11和C12相关联。矩阵A中其他元素也类似。

那么,我们有没有简单的方法来实现这样的关联呢。答案是有的。可以通过将卷积核旋转180度,再与扩充后的梯度矩阵进行卷积。扩充的过程如下:如果卷积核为k*k,待卷积矩阵为n*n,需要以n*n原矩阵为中心扩展到(n+2(k-1))*(n+2(k-1))。具体过程如下:

假设D为反向传播到卷积层的梯度矩阵,则D应该与矩阵C的大小相等,在这里为3*3。我们首先需要将它扩充到(3+2*(2-1))* (3+2*(2-1)) = 5*5大小的矩阵,


同时将卷积核B旋转180度:


将旋转后的卷积核与扩充后的梯度矩阵进行卷积:



Caffe中卷积层的实现

在caffe的配置文件中,我们的网络定义了2个卷积层,下面是第二个卷积层的配置信息:

layer {  name: "conv2"  type: "Convolution"  bottom: "pool1"  top: "conv2"  param {    lr_mult: 1  }  param {    lr_mult: 2  }  convolution_param {    num_output: 50    kernel_size: 5    stride: 1    weight_filler {      type: "xavier"    }    bias_filler {      type: "constant"    }  }}

我们可以看到,该层的类型为Convolution,即卷积层,bottom表示上一层为pool1,是一个池化层,top表示该层的输出为conv2,即本层卷积层的输出。

lr_mult是2个学习速率,这个在权值更新部分在说。

接下来可以看到num_output表示该层有50个输出map,kernel_size卷积核大小为5*5,stride表示卷积步长为1,weight_filler表示权值初始化方式, 默认为“constant",值全为0,很多时候我们也可以用"xavier"或者”gaussian"来进行初始化。bias_filler表示偏置值的初始化方式,该参数的值和weight_filler类似,一般设置为"constant",值全为0。



前向过程

在看代码前,我们先看一下caffe中卷积的实现。

Caffe中卷积的实现十分巧妙,详细可以参考一下这篇论文: https://hal.archives-ouvertes.fr/file/index/docid/112631/filename/p1038112283956.pdf

下面是一张论文中的图片,看这张图片可以很清楚理解。从图中可以看出,卷积之前将输入的多个矩阵和多个卷积核先展开再组合成2个大的矩阵,用展开后的矩阵相乘。


假设我们一次训练16张图片(即batch_size为16)。通过之前的推导,我们知道该层的输入为20个12*12的特征图,所以bottom的维度16*20*12*12,则该层的输出top的维度为16*50*8*8。

 

下面我们来看一下caffe中对于卷积层的实现代码。在caffe中,GPU上的卷积层对应的文件为\src\caffe\layers\conv_layer.cu

我们先看一下前向过程的代码:

void ConvolutionLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom,      const vector<Blob<Dtype>*>& top) {  const Dtype* weight = this->blobs_[0]->gpu_data();  for (int i = 0; i < bottom.size(); ++i) {    const Dtype* bottom_data = bottom[i]->gpu_data();    Dtype* top_data = top[i]->mutable_gpu_data();    for (int n = 0; n < this->num_; ++n) {//bottom_data为上一层传入的数据,与weight作卷积,结果保存到top_data中      this->forward_gpu_gemm(bottom_data + n * this->bottom_dim_, weight,          top_data + n * this->top_dim_);      if (this->bias_term_) {  //加上偏置值        const Dtype* bias = this->blobs_[1]->gpu_data();        this->forward_gpu_bias(top_data + n * this->top_dim_, bias);      }    }  }}

其中,卷积的运算用到了这个函数forward_gpu_gemm(),我们展开看一下这个函数的代码:

void BaseConvolutionLayer<Dtype>::forward_gpu_gemm(const Dtype* input,       const Dtype* weights, Dtype* output, bool skip_im2col) {    const Dtype* col_buff = input;    //若为1x1,不进行卷积操作    if (!is_1x1_) {if (!skip_im2col) {                //将输入矩阵展开                conv_im2col_gpu(input, col_buffer_.mutable_gpu_data());              }        col_buff = col_buffer_.gpu_data();    }    //对weights与col_buffer作卷积,卷积的结果放入output    caffe_gpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, conv_out_channels_ /            group_, conv_out_spatial_dim_, kernel_dim_,            (Dtype)1., weights + weight_offset_ * g, col_buff + col_offset_ * g,            (Dtype)0., output + output_offset_ * g);        }}

反向传播

Caffe中反向传播的代码如下:

void ConvolutionLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,      const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {  const Dtype* weight = this->blobs_[0]->gpu_data();  Dtype* weight_diff = this->blobs_[0]->mutable_gpu_diff();  for (int i = 0; i < top.size(); ++i) {    const Dtype* top_diff = top[i]->gpu_diff();    // Bias gradient, if necessary.    if (this->bias_term_ && this->param_propagate_down_[1]) {      Dtype* bias_diff = this->blobs_[1]->mutable_gpu_diff();      for (int n = 0; n < this->num_; ++n) {  //对一个batch中每一个map,计算其偏置的偏导        this->backward_gpu_bias(bias_diff, top_diff + n * this->top_dim_);      }    }    if (this->param_propagate_down_[0] || propagate_down[i]) {      const Dtype* bottom_data = bottom[i]->gpu_data();      Dtype* bottom_diff = bottom[i]->mutable_gpu_diff();      for (int n = 0; n < this->num_; ++n) {        // gradient w.r.t. weight. Note that we will accumulate diffs.        if (this->param_propagate_down_[0]) {          this->weight_gpu_gemm(bottom_data + n * this->bottom_dim_,              top_diff + n * this->top_dim_, weight_diff);        }        // gradient w.r.t. bottom data, if necessary.        if (propagate_down[i]) {          this->backward_gpu_gemm(top_diff + n * this->top_dim_, weight,              bottom_diff + n * this->bottom_dim_);        }      }    }  }}



4 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 军靴穿着磨脚怎么办 军靴磨大脚趾怎么办 想开服装店没经验怎么办 我鼻子大怎么办啊 副局长不听局长的话怎么办 边防改制部局怎么办 毛中老师打人怎么办 老婆是个泼妇怎么办 一年级孩子学习不好怎么办 生活作息不规律怎么办 作息时间不规律怎么办 能醒不想起床怎么办 不想起床怎么办 神回复 孩子不起床上学怎么办 孕晚期起床困难怎么办 腰间盘疼痛起床困难怎么办 小学生做作业拖拉怎么办 被手机贷起诉怎么办 大人睡颠倒了怎么办 熬夜长斑了怎么办 ps遇到文件尾怎么办 pdf用ps打不开怎么办 ps安装不上怎么办 工作原因经常熬夜怎么办 熬夜皮肤暗黄怎么办 经常熬夜睡眠不好怎么办 经常熬夜皮肤不好怎么办 皮肤熬夜变暗黄怎么办 晚上上夜班白天睡不着怎么办 熬夜肾虚怎么办才能好 经常熬夜口气重怎么办 晚上睡不着白天起不来怎么办 晚上熬夜白天睡不着怎么办 在外打工孩子上学怎么办 婴儿脸干燥发红怎么办 宝宝脸上红点点怎么办 药流出血量少怎么办 药流喝药第一天发烧了怎么办 药流第一天量很少怎么办 小孩不想吃饭口臭怎么办 晚上上班白天睡不着怎么办