转载自:http://blog.csdn.net/tangwei2014/article/details/47730797

卷积的实现思想:

  • 通过im2col将image转为一个matrix,将卷积操作转为矩阵乘法运算
  • 通过调用GEMM完成运算操作
  • 下面两个图是我在知乎中发现的,“盗”用一下,确实很好,能帮助理解。 
     

参数剖析

  • 配置参数:(从配置文件得来) 
    kernel_h_ pad_h_ hole_h_ stride_h_ 
    kernel_w_ pad_w_ hole_w_ stride_w_ 
    is_1x1_:上面8个参数都为1时,该参数为true

  • 和输入有关的参数:(从bottom得来) 
    num_ 
    channels_ 
    height_ 
    width_

  • 和卷积核有关的参数:(前两个参数从配置文件得来) 
    num_output_ 
    group_ 
    this->blobs_[0].reset(new Blob(num_output_, channels_ / group_, kernel_h_, kernel_w_)); 
    this->blobs_[1].reset(new Blob(1, 1, 1, num_output_)); 
    this->param_propagate_down_

  • 和输出有关的参数:(计算得来) 
    const int kernel_h_eff = kernel_h_ + (kernel_h_ - 1) * (hole_h_ - 1); 
    const int kernel_w_eff = kernel_w_ + (kernel_w_ - 1) * (hole_w_ - 1); 
    height_out_ = (height_ + 2 * pad_h_ - kernel_h_eff) / stride_h_ + 1; 
    width_out_ = (width_ + 2 * pad_w_ - kernel_w_eff) / stride_w_ + 1;

  • 和矩阵运算有关的参数:(计算得来) 
    M_ = num_output_ / group_; 
    K_ = channels_ * kernel_h_ * kernel_w_ / group_; 
    N_ = height_out_ * width_out_; 
    col_buffer_.Reshape(1, channels_*kernel_h_*kernel_w_, height_out_, width_out_);// is_1x1_为false的时候用 
    bias_multiplier_.Reshape(1, 1, 1, N_); //全部为1

输入大小:(num_, channels_, height_, width_) 
输出大小:(num_, num_output_, height_out_, width_out_)

重点函数剖析

  • 函数一: 
    im2col_cpu(bottom_data + bottom[i]->offset(n), 
    1, channels_, height_, width_, 
    kernel_h_, kernel_w_, pad_h_, pad_w_, 
    stride_h_, stride_w_, hole_h_, hole_w_, 
    col_buff);

    该函数的目的是:根据配置参数,将一幅(1, channels_, height_, width_)的输入feature map expand成 (1, channels_*kernel_h_*kernel_w_, height_out_, width_out_)大小的矩阵。

    具体的实现方法是: 
    内部主要有两套索引 
    一套是在输入图像上的索引,分别是:c_im(channels), h_im(height), w_im(width) 
    另一套是在输出的col_buff上的,分别是:c(channels_col), h(height_col), w(width_col)

    循环变量来自输出的col_buff的维数,根据输出的位置计算对应在输入图像上的位置(col2imh函数和im2col函数是一个道理,两套坐标反着来就行)。把索引的代码整合出来,对着源代码看,很容易懂:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> kernel_h_eff = kernel_h + (kernel_h - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>) * (hole_h - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>);<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> kernel_w_eff = kernel_w + (kernel_w - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>) * (hole_w - <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>);<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> height_col = (height + <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span> * pad_h - kernel_h_eff) / stride_h + <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> width_col = (width + <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span> * pad_w - kernel_w_eff) / stride_w + <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> channels_col = channels * kernel_h * kernel_w;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> w_offset = (c % kernel_w)  * hole_w;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> h_offset = ((c / kernel_w) % kernel_h) * hole_h;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> c_im = c / kernel_w / kernel_h;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> h_im = h * stride_h + h_offset - pad_h;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> w_im = w * stride_w + w_offset - pad_w;</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>
  • 函数二:

    caffe_cpu_gemm(CblasNoTrans, CblasNoTrans, M_, N_, K_, 
    (Dtype)1., weight + weight_offset * g, col_buff + col_offset * g, 
    (Dtype)0., top_data + top[i]->offset(n) + top_offset * g);

    该函数的目的是: 
    将(num_output_/group_, channels_ /group_, kernel_h_, kernel_w_)卷积核看成一个(num_output_/group_, channels_*kernel_h_*kernel_w_/group_)的矩阵A,即大小为M_x K_。

    将(1, channels_*kernel_h_*kernel_w_, height_out_, width_out_)的col_buff看成group_个(channels_*kernel_h_*kernel_w_/group_, height_out_*width_out_)的矩阵B,即大小为K_x N_。

    两者相乘再加上偏置项,就能得到卷积的结果。

    解释caffe_cpu_gemm函数: 
    其实其内部包了一个cblas_sgemm函数。 
    void cblas_sgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, 
    const enum 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

    const enum CBLAS_ORDER Order,这是指的数据的存储形式,在CBLAS的函数中无论一维还是二维数据都是用一维数组存储,这就要涉及是行主序还是列主序,在C语言中数组是用 行主序,fortran中是列主序。如果是习惯于是用行主序,所以这个参数是用CblasRowMajor,如果是列主序的话就是 CblasColMajor。 
    const int M,矩阵A的行,矩阵C的行 
    const int N,矩阵B的列,矩阵C的列 
    const int K,矩阵A的列,矩阵B的行

caffe卷积层代码阅读笔记相关推荐

  1. BNN Pytorch代码阅读笔记

    BNN Pytorch代码阅读笔记 这篇博客来写一下我对BNN(二值化神经网络)pytorch代码的理解,我是第一次阅读项目代码,所以想仔细的自己写一遍,把细节理解透彻,希望也能帮到大家! 论文链接: ...

  2. P2PNet(代码阅读笔记)

    P2PNet 代码阅读笔记 一.主干网络 主干网络采用的是VGG16 class BackboneBase_VGG(nn.Module):def __init__(self, backbone: nn ...

  3. VITAL Tracker Pytorch 代码阅读笔记

    VITAL Tracker Pytorch 代码阅读笔记 论文链接:https://arxiv.org/pdf/1804.04273.pdf 代码链接:https://github.com/abner ...

  4. StyleGAN2代码阅读笔记

    源代码地址:https://github.com/NVlabs/stylegan2-ada-pytorch 这是一篇代码阅读笔记,顾名思义是对代码进行阅读,讲解的笔记.对象是styleGAN2的pyt ...

  5. leveldb代码阅读笔记(一)

    leveldb代码阅读笔记 above all leveldb是一个单机的键值存储的内存数据库,其内部使用了 LSM tree 作为底层存储结构,支持多版本数据控制,代码设计巧妙且简洁高效,十分值得作 ...

  6. C++ Primer Plus 6th代码阅读笔记

    C++ Primer Plus 6th代码阅读笔记 第一章没什么代码 第二章代码 carrots.cpp : cout 可以拼接输出,cin.get()接受输入 convert.cpp 函数原型放在主 ...

  7. CNN去马赛克代码阅读笔记

    有的博客链接是之前几周写好的草稿,最近整理的时候才发布的 CNN去马赛克论文及代码下载地址 有torch,minimal torch和caffe三种版本 关于minimal torch版所做的努力,以 ...

  8. ORB-SLAM2代码阅读笔记(五):Tracking线程3——Track函数中单目相机初始化

    Table of Contents 1.特征点匹配相关理论简介 2.ORB-SLAM2中特征匹配代码分析 (1)Tracking线程中的状态机 (2)单目相机初始化函数MonocularInitial ...

  9. YOLO 卷积层代码学习

    YOLO 卷积层代码学习 卷积层的初始化 void im2col_cpu(float* data_im,int channels, int height, int width,int ksize, i ...

最新文章

  1. 常见的函数式编程模型
  2. Leetcode1701. 平均等待时间[C++题解]:模拟题
  3. 理解javascript中的回调函数(callback)【转】
  4. 总结一下qsort()函数的使用与注意
  5. 关于服务器的入门知识整理
  6. SAP Spartacus org unit list当前行is-current的判定逻辑
  7. Spring Boot功能实战
  8. python 月报_python实践--月报分析之获取jira缺陷数据
  9. 爱奇艺如何开启两指双击触发奇观功能
  10. 数据库只有mdf文件而没有ldf文件,如何恢复数据库
  11. Python批量整理文件名小案例(附公众号第一批赠书活动中奖名单)
  12. 开源操作系统年度技术会议图文直播
  13. 图片水印 之 二
  14. html 限制文本框的长度,JavaScript 限制文本框输入长度(支持字符限制)
  15. Win10环境下 Cad插件使用失败 解决方法
  16. OP-TEE中的线程管理(四)
  17. 转载:h5标签中的embed标签
  18. Chrome for Android AutoComplete地址栏地址自动补全功能调研和更新
  19. OneNote 2016登录时显示“很抱歉,遇到一些临时服务器问题“,重试很多遍也不管用~~~~解决方法
  20. Android 将签名布局旋转90度,Android 自定义View手写签名

热门文章

  1. [WARNING] Using platform encoding (GBK actually) to copy filtered resources, i.e. build is platform
  2. 最小二乘拟合,L1、L2正则化约束--转
  3. AOP 的利器:ASM 3.0 介绍
  4. Linux 线程实现机制分析--转
  5. 【采用】【风险管理】(第一篇)风险管理核心指标
  6. 【勉强采用】反欺诈之血缘关系分析和犯罪传导监测
  7. 智能合约开发环境搭建及 Hello World 合约
  8. 灰度图像--图像增强 平滑之均值滤波、高斯滤波
  9. mysql 有always on么,mysql 关于时间类型的刨坑之路
  10. Visual Studio 2013开发 mini-filter driver step by step 内核中使用线程(7)