Caffe中卷积的实现细节(涉及到BaseConvolutionLayer、ConvolutionLayer、im2col等)
来源:互联网 发布:淘宝网西服 编辑:程序博客网 时间:2024/06/10 15:52
文章地址:http://blog.csdn.net/xizero00/article/details/51049858
一、 卷积层的作用简介
卷积层是深度神经网络中的一个重要的层,该层实现了局部感受野,通过这种局部感受野,可以有效地降低参数的数目。
我们将结合caffe来讲解具体是如何实现卷积层的前传和反传的。至于是如何前传和反传的原理可以参考Notes on Convolutional Neural Networks,具体请百度或者谷歌,即可下载到。
Caffe中的master分支已经将vision_layers.hpp中的各个层分散到layers中去了,因此如果你是主分支的代码,请在include/layers中找BaseConvolutionLayer和ConvolutionLayer的头文件的定义。
二、卷积层的详细介绍
1)构造函数
2)成员变量
为了更为准确地解释,我把代码中的各个变量的含义也贴进来:
输入的大小 bottom_dim_ = conv_in_channel X in_height X in_width
输出的大小 top_dim_ = conv_out_channel X out_height X out_width
卷积核输入的图像大小num_kernels_im2col_ = conv_in_channel X out_height X out_width
卷积核输出的图像大小num_kernels_col2im_ = conv_out_channel X out_height X out_width
将偏置扩展成矩阵的bias_multiplier_
weight_shape = [conv_out_channels_, conv_in_channels_/group, weight_h, weight_w]
conv_input_shape_data = [inchannel, in_height, in width]
kernel_shape_=[kernel_height, kernel_width]
3)成员函数
在给出上述的变量以后,不得不介绍一下caffe里面究竟是如何实现卷积的,在介绍成员函数这一小节,我们从调用的顺序讲起是如何实现卷积以及反传的。
ConvolutionLayer是继承于BaseConvolutionLayer的,而BaseConvolutionLayer才是真正实现卷积及其反传的,而在BaseConvolutionLayer中的卷积的实现中有一个重要的函数就是im2col以及col2im,im2colnd以及col2imnd。前面的两个函数是二维卷积的正向和逆向过程,而后面的两个函数是n维卷积的正向和逆向过程。
首先放出conv_layer.cpp的前传代码
上述的代码中调用了基类的前传函数,那么我们就把基类的前传函数拿出来看看
而积累中又使用了conv_im2col_cpu将卷积核在图像上的滑动转换为了矩阵。
这就是真身:
调用im2col_cpu的时候输入的参数为
im2col_cpu(一幅图像,输入图像的channel, 输入图像的height, 输入图像的width, kernel的height, kernel的width, pad的height, pad的width, stride的height, stride的width)
其函数原型如下
看起来这段代码很难理解,其实你不妨把计算卷积之后的图像与卷积之前的图像的位置看成是一个函数find_src_location
该函数输入的是h_offset,w_offset
该函数主要就是将每一个子矩形(与kernel一样大小的图像)的(h_offset,w_offset )的位置的像素取出来放到第c行去
这个函数具体是做了啥事了呢,我来画个图
如下图所示,左边的是输入的原始图像,该图像需要进行卷积,右图是经过im2col处理的得到的图像
结合图来说这个函数find_src_location内部的两个for循环干的事情就是将每个不同颜色的小矩形框内部(h_offset,w_offset )位置的像素值复制到
第c行的位置去。比如下图中的右图,第一行的1,就是左图中的每一个不同颜色的小矩形框内的(0,0)位置的1全部复制过来的。
图1 解释im2col的工作过程
上图中输入图像的channel=1,长im_w是9,宽im_h是9,
kernel的h和w都是3,因为kernel是在图像上面滑动,所以kernel_channel=1
假设padding的h和w都是0,也就是不填充
stride_h为3,stide_w=3
那么通过该函数,就可以获得新的矩形
长是:channel*kernel_h*kernel_w = 1*3*3=9
宽是:[(im_h+2*pad_h-kernel_h)/stride_h+1] * [(im_w+2*pad_w-kernel_w)/stride_w+1]=[(9+2*0-3)/3+1]*[(9+2*0-3)/3+1]=9
注意:这个宽呢,就是经过卷积之后的图像的长和宽相乘
含义是什么:
将一个kernel大小的图像块变成图中新的矩形的一个列,一列的长是channel*kernel_h*kernel_w = 1*3*3=9
而宽(列数)则是在图像上的滑动的次数,这里是9,之所以是9并不是巧合,而是取了stride_h和stride_w都为3了。
可是这么干是为了什么呢?
这可以将滑动的卷积变成两个矩形相乘。
怎么讲,假设一个kernel输出的channel=output channel
那么对应的卷积权重W矩阵的形状即为outputchannle x [channel*kernel_h*kernel_w]
而图像经过im2col处理之后的原始图像srcimg变成了[channel*kernel_h*kernel_w] x [卷积之后的图像的长和宽相乘 ]
W x srcimg = outputchannle x 卷积之后的图像的长和宽相乘
怎么样,这下理解了吧
很机智地将卷积变成了两个矩阵相乘了。。。
而对应的col2im的代码就很类似了与im2col 的代码几乎没有啥差别就是这个下面的赋值语句的位置颠倒了一下
上面介绍了二维卷积,那么我们就乘热打铁,再看看n维通用卷积是如何实现的
接下来介绍n维通用的卷积的具体实现
n维卷积的实现与二维卷积的实现很类似,只不过对应的变量进行了变化,你只需要找到对应就可以很快理解
d_offset 对应于im2col中的h_offset和w_offset是一个输入图像的channel 乘以kernel_size大小的图像块的偏移量(kernel_size下面的代码有定义)
d_iter对应于im2col中内层for循环的h和w,是经过im2colnd处理过的col_buff中的偏移
d_pad对应于im2col中内层for循环的h_pad和w_pad,是输入的原始图像中的偏移
作者还将im2colnd和col2imnd合并到一起实现了,通过const bool im2col来判断是im2col还是col2im
给出包裹im2col_nd_core_cpu 的im2col_nd_cpu 函数
kIm2Col=true,输入是data_im,输出是data_col
给出包裹im2col_nd_core_cpu的col2im_nd_cpu函数
一个德行,只不过kIm2Col = false了,此外输入的书data_col而输出的是data_im
还有几个显式的声明,定义float和double类型的函数,下面一并贴出
// 显式声明float和double类型的im2col_nd_cpu
// 显式声明float和double类型的col2im_cpu
// 显式声明float和double类型的col2im_nd_cpu
三、总结
一开始看的时候,看懵了,没看懂im2col,后来查了下matlab的im2col,发现原来做了这么一件事情,就是给定一个大小的窗口之后,im2col会将窗口内的元素flat成一个列向量,而列向量的个数则是这个大小的窗口在图像上的滑动的次数(这个滑动的次数实际上也是经过卷积之后的图像中的像素个数,不知道你理解没:))。
窥一斑难见全豹,详细的注释请在http://download.csdn.net/detail/xizero00/9480181下载
参考:
[1] 别人写的一个博客介绍im2col这个函数具体是怎么实现的,但是我觉得没写明白
http://blog.csdn.net/ayst123/article/details/43924151
[2] matlab中im2col的解释,这给了我启发
http://blog.163.com/liwei_ie/blog/static/20931113220143954730342/
阅读全文
0 0
- Caffe中卷积的实现细节(涉及到BaseConvolutionLayer、ConvolutionLayer、im2col等)
- Caffe中卷积的实现细节(涉及到BaseConvolutionLayer、ConvolutionLayer、im2col等)
- caffe代码阅读10:Caffe中卷积的实现细节(涉及到BaseConvolutionLayer、ConvolutionLayer、im2col等)-2016.4.3
- caffe中卷积转换为矩阵时im2col的详细过程
- Caffe卷积层的实现细节
- caffe源码深入学习6:超级详细的im2col绘图解析,分析caffe卷积操作的底层实现
- Caffe中卷积的实现
- (Caffe)卷积的实现
- Caffe框架源码剖析(4)—卷积层基类BaseConvolutionLayer
- Caffe框架源码剖析(4)—卷积层基类BaseConvolutionLayer
- Caffe中卷积层的实现
- caffe中卷积层的实现
- Caffe中实现卷积的计算
- Caffe框架源码剖析(5)—卷积层ConvolutionLayer
- Caffe框架源码剖析(5)—卷积层ConvolutionLayer
- caffe代码阅读7:Caffe中卷积的实现
- 卷积层(ConvolutionLayer)
- caffe源码解析-BaseConvolutionLayer
- dp day2/3-区间dp
- c++ c# opencv dll 调用的方法
- angular4小星星评分功能代码
- 面向对象的第一个特征:封装II
- POJ
- Caffe中卷积的实现细节(涉及到BaseConvolutionLayer、ConvolutionLayer、im2col等)
- unique去重函数
- 根据数组中某个两个值作为键、并把数组中同时存在该两个字段值的数组值累加、组合成三维数组
- Out of Hay
- 河南省多校赛(4)GJJ的日常之再游戏
- 98. Validate Binary Search Tree
- 嵌入式Linux 使用libmad
- 组合数取模
- 设置select下拉框禁用