caffe中的图像卷积操作

下面讲一下,caffe中的实现。

Caffe中的卷积计算是将卷积核矩阵和输入图像矩阵变换为两个大的矩阵A与B,然后A与B进行矩阵相乘得到结果C(利用GPU进行矩阵相乘的高效性),三个矩阵的说明如下:

(1)在矩阵A中

        M为卷积核个数,K=k*k,等于卷积核大小,即第一个矩阵每行为一个卷积核向量(是将二维的卷积核转化为一维),总共有M行,表示有M个卷积核。

(2)在矩阵B中

        N=((image_h + 2*pad_h – kernel_h)/stride_h+ 1)*((image_w +2*pad_w – kernel_w)/stride_w + 1)

        image_h:输入图像的高度

        image_w:输入图像的宽度

        pad_h:在输入图像的高度方向两边各增加pad_h个单位长度(因为有两边,所以乘以2)

        pad_w:在输入图像的宽度方向两边各增加pad_w个单位长度(因为有两边,所以乘以2)

        kernel_h:卷积核的高度

        kernel_w:卷积核的宽度

        stride_h:高度方向的滑动步长;

        stride_w:宽度方向的滑动步长。

        因此,N为输出图像大小的长宽乘积,也是卷积核在输入图像上滑动可截取的最大特征数。

        K=k*k,表示利用卷积核大小的框在输入图像上滑动所截取的数据大小,与卷积核大小一样大。

(3)在矩阵C中

        矩阵C为矩阵A和矩阵B相乘的结果,得到一个M*N的矩阵,其中每行表示一个输出图像即feature map,共有M个输出图像(输出图像数目等于卷积核数目)

 (在Caffe中是使用src/caffe/util/im2col.cu中的im2col和col2im来完成矩阵的变形和还原操作)

 

 举个例子(方便理解):

     假设有两个卷积核为与,因此M=2,kernel_h=2,kernel_w=2,K= kernel_h * kernel_w=4

     输入图像矩阵为,因此image_h=3,image_w=3,令边界扩展为0即pad_h=0,pad_w=0,滑动步长为1,即stride_h=1,stride_w=1

     故N=[(3+2*0-2)/1+1]*[ (3+2*0-2)/1+1]=2*2=4

 

    A矩阵(M*K)为(一行为一个卷积核),B矩阵(K*N)为(B矩阵的每一列为一个卷积核要卷积的大小)

    A 矩阵的由来:::    

    B矩阵的由来:(caffe 有 imtocol.cpp代码,专门用于实现) 

    C=A*B=*=

    C中的与分别为两个输出特征图像即feature map。验证了 有几个卷积核就有几个feature map

 

    在Caffe源码中,src/caffe/util/math_functions.cu(如果使用CPU则是src/util/math_functions.cpp)中的caffe_gpu_gemm()函数,其中有两个矩阵A(M*K)

    与矩阵    B(K*N),大家可以通过输出M、K、N的值即相应的矩阵内容来验证上述的原理,代码中的C矩阵与上述的C矩阵不一样,代码中的C矩阵存储的是偏置bias,

    是A  与B相乘后得到M*N大小的矩阵,然后再跟这个存储偏置的矩阵C相加完成卷积过程。如果是跑Mnist训练网络的话,可以看到第一个卷积层卷积过程中,

    M=20,K=25,N=24*24=576。

  (caffe中涉及卷积具体过程的文件主要有:src/caffe/layers/conv_layer.cu、src/caffe/layers/base_conv_layer.cpp、                src/caffe/util/math_functions.cu、src/caffe/util/im2col.cu)

    另外大家也可以参考知乎上贾扬清大神的回答,帮助理解http://www.zhihu.com/question/28385679

  (对于他给出的ppt上的C表示图像通道个数,如果是RGB图像则通道数为3,对应于caffe代码中的变量为src/caffe/layers/base_conv_layer.cpp中

     函数forward_gpu_gemm中的group_)

 

 

darknet里面的im2col用的是caffe的im2col代码。多通道矩阵数据(如彩色图像)经过im2col操作后得到一个矩阵,同时将多个卷积核

也转成一个矩阵的形式。这样一来就能够把卷积操作转换成矩阵乘法。本文主要讨论im2col的实现。

参考:http://blog.csdn.net/mrhiuser/article/details/52672824

caffe的卷积过程是im2col和SGEMM(什么是GEMM?http://www.cnblogs.com/mengmengmiaomiao/p/7587005.html)。

一:im2col

一个图像 input_num=1;


图像通道 input_channel=1;


图像高 input_h=4;


图像宽 input_w=4;


kernel高 kernel_h=3;


kernel宽 kernel_w=3;


stride=1;pad=0;


卷积后,输出图像的计算公式:


output_h=(input_h-kernel_h)/stride+1;


output_w=(input_w-kernel_w)/stride+1;



 


如下图,(注:图像中数据不代表图像的颜色数值)

caffe中的图像卷积操作

原图(图a)按照从左到右、从上到下的过程,将(a)中大小为3*3(因为kernel大小为3*3)的矩阵拉成右图(图b)中的一列。具体过程如下图所示:

caffe中的图像卷积操作

如果stride是2怎么办?
 

答案是填零。

二:多通道的im2col


假设有三个通道(R、G、B)图像通道 input_channel=3;


图像在内存中的存储是:首先是连续存储第一通道的数据,然后再存储第二通道的数据,最后存储第三通道的数据。如下图:

caffe中的图像卷积操作

多通道的im2col的过程,是首先im2col第一通道,然后在im2col第二通道,最后im2col第三通道。各通道im2col的数据在内存中也是连续存储的。如下图:

caffe中的图像卷积操作

三:kernel

图像的每个通道对应一个kernel通道,如下图(注:为计算简单,将kernel的值设置为1,同样此值不代表颜色数值。)

caffe中的图像卷积操作

kernel的通道数据在内存中也是连续存储的。所以上面的kernel图像也可以表示为下图:

caffe中的图像卷积操作

三:矩阵乘 sgemm

在caffe中图像与kernel的矩阵乘,是kernel*img。即:在矩阵乘法中

M=1 ,

N=output_h * output_w

K=input_channels * kernel_h * kernel_w


 

如下图所示:

caffe中的图像卷积操作

图像数据是连续存储,因此输出图像也可以如下图所示【output_h * output_w】=【2*2】:

caffe中的图像卷积操作


 

四:多通道图像输出

在caffe中图像与kernel的矩阵乘中:

M=output_channels ,

N=output_h * output_w

K=input_channels * kernel_h * kernel_w


 

如下图:

caffe中的图像卷积操作

同样,多个输出通道图像的数据是连续存储,因此输出图像也可以如下图所示【output_channels*output_h * output_w】=【3*2*2】,

caffe中的图像卷积操作