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

其中I=f(x,y)
是一个图像,f(x,y)是图像I上面x行y列上点的灰度值。而w(x,y)有太多名字叫了,滤波器、卷积核、响应函数等等,而a和b定义了卷积核即w(x,y)的大小。
从上面的式子中,可以很明显的看到,卷积实际上是提供了一个权重模板,这个模板在图像上滑动,并将中心依次与图像中每一个像素对齐,然后对这个模板覆盖的所有像素进行加权,并将结果作为这个卷积核在图像上该点的响应。所以从整个卷积运算我们可以看到以下几点:
- 卷积是一种线性运算
- 卷积核的大小,定义了图像中任何一点参与运算的邻域的大小。
- 卷积核上的权值大小说明了对应的邻域点对最后结果的贡献能力,权重越大,贡献能力越大。
- 卷积核沿着图像所有像素移动并计算响应,会得到一个和原图像等大图像。
- 在处理边缘上点时,卷积核会覆盖到图像外层没有定义的点,这时候有几种方法设定这些没有定义的点,可以用内层像素镜像复制,也可以全设置为0。

Caffe的cpu模式下的卷积实现
因为在图像窗口上滑动不利用并行计算,因此,当前卷积的实现基本都遵循如下原则:
- 把当前被卷积的矩阵的元素按列转展开成一个大的矩阵;
- 展开的矩阵与卷积核相乘;
- 完成运算。
Caffe 的cpu 模式下的卷积运算(不包含bias,bias单独运算)同样也是这样实现的;
- 调用im2col_cpu函数,将被卷积的矩阵按列展开成一个大的矩阵;
- 调用cblas_sgemm函数,使用openblas接口完成两个相乘。
其中:
- 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)
- 输入:
- data_im:输入data,当前layer的input blob data(也称为“bottom blob”);一般是float*类型。
- Channels:当前data的input blob data(也称为“bottom blob”)的channel个数;
- Height:当前data的input blob data(也称为“bottom blob”)的rows;
- Width:当前data的input blob data(也称为“bottom blob”)的cols;
- kernel_h:当前layer的卷积核的rows;
- kernel_w:当前layer的卷积核的cols;
- pad_h:当前layer的高度边缘要填充的个数,也就是要填充的rows的个数;
- pad_w:当前layer的宽度边缘要填充的个数,也就是要填充的cols的个数;
- stride_h:卷积时,窗口滑动的rows方向的步长;
- stride_w:卷积时,窗口滑动的cols方向的步长;
- dilation_h:卷积核rows方向的缩放系数,默认为1;一般不做修改或配置,下面的计算不考虑该因子;
- dilation_w:卷积核的cols方向的缩放系数,默认为1;一般不做修改或配置,下面的计算不考虑该因子;
- 输出:
- data_col:按列排列的数据;输出的大小如下:

其中,
标识输出矩阵的大小;- Channels标识输入的数据的channels数,输出的数据channels数保持不变;
标识卷积核的高度;
标识卷积核的宽度;
标识卷积操作后的高度;注意:这里不是当前展开矩阵的高度;
,其中,
标识输入data的height;
标识卷积层padding参数的高度;
标识卷积核的高度;
标识卷积的高度的步长;
标识卷积操作后的宽度;
,其中,
标识输入data的width;
标识卷积层padding参数的宽度;
标识卷积核的宽度;
标识卷积的宽度的步长;
- 返回值:无
- 处理:
输出的每个channel的数据是相互独立的,因此,以一个channel为例,展开的数据以如下的方式存储:
,其中,H都是
*
大小的矩阵,
和
卷积核的高度和宽度;
- cblas_sgemm函数:
- 函数原型: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)
- 描述:
计算:C := alpha*op(A)*op(B) + beta*C
注意:a、b、c指输入的缓冲区,A、B、C指真正需要计算的缓冲区(如果需要对一小块矩阵进行计算,这两组就不一样了)。
其中,
- op(X) 表示是否进行转置或共轭矩阵操作;
- A为m*k的矩阵(m行k列)
- B为k*n的矩阵
- C为m*n的矩阵
- 输入:
- Layout:表示二维矩阵存储是按行优先(CblasRowMajor)还是列优先(CblasColMajor)。C++里面是行优先存储的;fortran是列优先存储数据。(为了让fortran调用方便吧)
- Transa:可为CblasNoTrans、CblasTrans、CblasConjTrans;标识是否需要转置,及转置操作类型。
- TransB:同Transa。
- M:矩阵A和C的行数;对于caffe的卷积层而言,对应于输出的channels的个数;
- N:矩阵B和C的列数;对于caffe的卷积层而言,对应于输出的维度,
*
- K:矩阵a的列数,矩阵b的行数;卷积核的维度,
*
channels; - Alpha:A*B的缩放因子;见上面的公式;对于caffe的卷积层而言,不做缩放,默认值为1;
- A:被乘的矩阵;为M*K的矩阵;对于caffe的卷积层而言,对应于weight矩阵;
- lda:行优先 & 不转置时,lda≥max(1,K);
- B:A相乘的矩阵;为K*N的矩阵;对于caffe的卷积层而言,对应于input data基于卷积配置参数展开的矩阵;
- ldb:行优先 & 不转置时,ldb≥max(1,N);
- Beta:偏置矩阵的缩放因子;对于caffe的卷积层而言,有专门的偏置操作,这里设置为0;
- C:偏置矩阵,M*N;对于caffe的卷积层而言,有专门的偏置操作,这里不做偏置。
- ldc:行优先时,ldc≥max(1,N);
- 输出:
C:既是输入的偏置矩阵,又是计算完成后的输出矩阵;
- 返回值:无
- 处理:最后生成的C矩阵为outchannels*
*
大小的矩阵。
这个博客不能更好的做分级,所以,有点乱,我是从word文档拷贝过来的,大家凑合着看吧。
本文介绍了卷积的概念及其在图像处理中的应用,详细解释了卷积的数学表示,并探讨了卷积核如何通过加权模板作用于图像上的每个像素。此外,还深入分析了Caffe在CPU模式下实现卷积的具体过程。

825

被折叠的 条评论
为什么被折叠?



