caffe卷积的实现源码解析

本文介绍了卷积的概念及其在图像处理中的应用,详细解释了卷积的数学表示,并探讨了卷积核如何通过加权模板作用于图像上的每个像素。此外,还深入分析了Caffe在CPU模式下实现卷积的具体过程。

卷积的概述

卷积是分析数学中一种很重要的运算,其实是一个很简单的概念,但是很多做图像处理的人对这个概念都解释不清,为了简单起见,这里面我们只介绍离散形式的卷积,那么在图像上,对图像用一个卷积核进行卷积运算,实际上是一个滤波的过程。我们先看一下卷积的基本数学表示:

其中I=f(x,y)

是一个图像,f(x,y)是图像I上面x行y列上点的灰度值。而w(x,y)有太多名字叫了,滤波器、卷积核、响应函数等等,而a和b定义了卷积核即w(x,y)的大小。 

从上面的式子中,可以很明显的看到,卷积实际上是提供了一个权重模板,这个模板在图像上滑动,并将中心依次与图像中每一个像素对齐,然后对这个模板覆盖的所有像素进行加权,并将结果作为这个卷积核在图像上该点的响应。所以从整个卷积运算我们可以看到以下几点:

  1. 卷积是一种线性运算
  2. 卷积核的大小,定义了图像中任何一点参与运算的邻域的大小。
  3. 卷积核上的权值大小说明了对应的邻域点对最后结果的贡献能力,权重越大,贡献能力越大。
  4. 卷积核沿着图像所有像素移动并计算响应,会得到一个和原图像等大图像。
  5. 在处理边缘上点时,卷积核会覆盖到图像外层没有定义的点,这时候有几种方法设定这些没有定义的点,可以用内层像素镜像复制,也可以全设置为0。

Caffe的cpu模式下的卷积实现

因为在图像窗口上滑动不利用并行计算,因此,当前卷积的实现基本都遵循如下原则:

  1. 把当前被卷积的矩阵的元素按列转展开成一个大的矩阵;
  2. 展开的矩阵与卷积核相乘;
  3. 完成运算。

Caffe 的cpu 模式下的卷积运算(不包含bias,bias单独运算)同样也是这样实现的;

  1. 调用im2col_cpu函数,将被卷积的矩阵按列展开成一个大的矩阵;
  2. 调用cblas_sgemm函数,使用openblas接口完成两个相乘。

其中:

  1. im2col_cpu函数:
  • 函数原型:void im2col_cpu(const Dtype* data_im, const int channels,const int height, const int width, const int kernel_h, const int kernel_w,const int pad_h, const int pad_w,const int stride_h, const int stride_w,const int dilation_h, const int dilation_w,Dtype* data_col) 
  • 输入:
  1. data_im:输入data,当前layer的input blob data(也称为“bottom blob”);一般是float*类型。
  2.  Channels:当前data的input blob data(也称为“bottom blob”)的channel个数;
  3. Height:当前data的input blob data(也称为“bottom blob”)的rows;
  4. Width:当前data的input blob data(也称为“bottom blob”)的cols;
  5. kernel_h:当前layer的卷积核的rows;
  6.  kernel_w:当前layer的卷积核的cols;
  7. pad_h:当前layer的高度边缘要填充的个数,也就是要填充的rows的个数;
  8. pad_w:当前layer的宽度边缘要填充的个数,也就是要填充的cols的个数;
  9. stride_h:卷积时,窗口滑动的rows方向的步长;
  10. stride_w:卷积时,窗口滑动的cols方向的步长;
  11.  dilation_h:卷积核rows方向的缩放系数,默认为1;一般不做修改或配置,下面的计算不考虑该因子;
  12. dilation_w:卷积核的cols方向的缩放系数,默认为1;一般不做修改或配置,下面的计算不考虑该因子;
  • 输出:
  1. data_col:按列排列的数据;输出的大小如下:

其中,

  1. 标识输出矩阵的大小;
  2. Channels标识输入的数据的channels数,输出的数据channels数保持不变;
  3. 标识卷积核的高度;
  4. 标识卷积核的宽度;
  5. 标识卷积操作后的高度;注意:这里不是当前展开矩阵的高度;

,其中,标识输入data的height;标识卷积层padding参数的高度;标识卷积核的高度;标识卷积的高度的步长;

  1. 标识卷积操作后的宽度;

,其中,标识输入data的width;标识卷积层padding参数的宽度;标识卷积核的宽度标识卷积的宽度的步长;

 

  1. 返回值:无
  2. 处理:

输出的每个channel的数据是相互独立的,因此,以一个channel为例,展开的数据以如下的方式存储:

,其中,H都是*大小的矩阵,卷积核的高度和宽度;

 

 

 

  1. cblas_sgemm函数:
  1. 函数原型:void cblas_sgemm(const CBLAS_LAYOUT layout, const CBLAS_TRANSPOSE TransA,const CBLAS_TRANSPOSE TransB, const int M, const int N,const int K, const float alpha, const float  *A, const int lda, const float  *B, const int ldb,const float beta, float  *C, const int ldc)
  2. 描述:

计算:C := alpha*op(A)*op(B) + beta*C

注意:a、b、c指输入的缓冲区,A、B、C指真正需要计算的缓冲区(如果需要对一小块矩阵进行计算,这两组就不一样了)。

其中,

  1. op(X) 表示是否进行转置或共轭矩阵操作;
  2. A为m*k的矩阵(m行k列)
  3. B为k*n的矩阵
  4. C为m*n的矩阵

 

  1. 输入:
  1. Layout:表示二维矩阵存储是按行优先(CblasRowMajor)还是列优先(CblasColMajor)。C++里面是行优先存储的;fortran是列优先存储数据。(为了让fortran调用方便吧)
  2. Transa:可为CblasNoTrans、CblasTrans、CblasConjTrans;标识是否需要转置,及转置操作类型。
  3. TransB:同Transa。
  4. M:矩阵A和C的行数;对于caffe的卷积层而言,对应于输出的channels的个数;
  5. N:矩阵B和C的列数;对于caffe的卷积层而言,对应于输出的维度,*
  6. K:矩阵a的列数,矩阵b的行数;卷积核的维度,*channels;
  7. Alpha:A*B的缩放因子;见上面的公式;对于caffe的卷积层而言,不做缩放,默认值为1;
  8. A:被乘的矩阵;为M*K的矩阵;对于caffe的卷积层而言,对应于weight矩阵;
  9. lda:行优先 & 不转置时,lda≥max(1,K);
  10. B:A相乘的矩阵;为K*N的矩阵;对于caffe的卷积层而言,对应于input data基于卷积配置参数展开的矩阵;
  11. ldb:行优先 & 不转置时,ldb≥max(1,N);
  12. Beta:偏置矩阵的缩放因子;对于caffe的卷积层而言,有专门的偏置操作,这里设置为0;
  13. C:偏置矩阵,M*N;对于caffe的卷积层而言,有专门的偏置操作,这里不做偏置。
  14. ldc:行优先时,ldc≥max(1,N);
  1. 输出:

C:既是输入的偏置矩阵,又是计算完成后的输出矩阵;

  1. 返回值:无
  2. 处理:最后生成的C矩阵为outchannels**大小的矩阵。

这个博客不能更好的做分级,所以,有点乱,我是从word文档拷贝过来的,大家凑合着看吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值