卷积的实现思想:

  • 通过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函数是一个道理。两套坐标反着来即可)。把索引的代码整合出来。对着源码看。非常easy懂:

    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);
int height_col = (height + 2 * pad_h - kernel_h_eff) / stride_h + 1;
int width_col = (width + 2 * pad_w - kernel_w_eff) / stride_w + 1;
int channels_col = channels * kernel_h * kernel_w;
int w_offset = (c % kernel_w) * hole_w;
int h_offset = ((c / kernel_w) % kernel_h) * hole_h;
int c_im = c / kernel_w / kernel_h;
const int h_im = h * stride_h + h_offset - pad_h;
const int w_im = w * stride_w + w_offset - pad_w;
  • 函数二:

    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. [置顶] Linux协议栈代码阅读笔记(二)网络接口的配置

    Linux协议栈代码阅读笔记(二)网络接口的配置 (基于linux-2.6.11) (一)用户态通过C库函数ioctl进行网络接口的配置 例如,知名的ifconfig程序,就是通过C库函数sys_io ...

  2. Linux协议栈代码阅读笔记(二)网络接口的配置

    Linux协议栈代码阅读笔记(二)网络接口的配置 (基于linux-2.6.11) (一)用户态通过C库函数ioctl进行网络接口的配置 例如,知名的ifconfig程序,就是通过C库函数sys_io ...

  3. [置顶] Linux协议栈代码阅读笔记(一)

    Linux协议栈代码阅读笔记(一) (基于linux-2.6.21.7) (一)用户态通过诸如下面的C库函数访问协议栈服务 int socket(int domain, int type, int p ...

  4. 【caffe】卷积层代码解析

    1.Forward_cpu conv_layer.cpp template <typename Dtype> void ConvolutionLayer<Dtype>::For ...

  5. Linux-3.0.8 input subsystem代码阅读笔记

    先乱序记录一下阅读Linux input subsystem代码的笔记. 在input device driver的入口代码部分,需要分配并初始化input device结构,内核提供的API是inp ...

  6. 〖Android〗OK6410a的Android HAL层代码编写笔记

    一.编写LED灯的Linux驱动程序代码 之所以使用存在HAL层,是为了保护对硬件驱动过程的逻辑与原理: 所以,残留在Linux驱动层的代码,只保留了基本的读写操作,而不含有关键的逻辑思维: 1. l ...

  7. caffe 代码阅读笔记1

    首先查看caffe.cpp里的train函数: // Train / Finetune a model. //训练,微调一个网络模型 int train() { // google的glog库,检查- ...

  8. caffe卷积层实现

    下图是jiayangqing在知乎上的回答,其实过程就是把image转换成矩阵,然后进行矩阵运算 卷积的实现在conv_layer层,conv_layer层继承了base_conv_layer层,ba ...

  9. Typecho 代码阅读笔记(二) - 数据库访问

    转载请注明出处:http://blog.csdn.net/jh_zzz 这一块比较复杂,我还没有完全理解为什么要把 SQL 语句的组装搞这么复杂. 从一个普通皮肤页面开始 themes/default ...

随机推荐

  1. 2016"百度之星" - 初赛(Astar Round2A)HDU 5695 拓扑排序+优先队列

    Gym Class Time Limit: 6000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total S ...

  2. C++vector使用

    标准库Vector类型使用需要的头文件:#include <vector>Vector:Vector 是一个类模板.不是一种数据类型. Vector<int>是一种数据类型. ...

  3. 百度之星复赛T5&&hdu6148

    Problem Description 众所周知,度度熊非常喜欢数字. 它最近发明了一种新的数字:Valley Number,像山谷一样的数字. 当一个数字,从左到右依次看过去数字没有出现先递增接着递 ...

  4. eclipse easy shell plugin

    svn checkout svn://svn.code.sf.net/p/pluginbox/code/trunk pluginbox-code   Eclipse中Easy Shell插件配置Pow ...

  5. C++嵌套类及对外围类成员变量的访问

    C++嵌套类及对外围类成员变量的访问 在一个类中定义的类称为嵌套类,定义嵌套类的类称为外围类. 定义嵌套类的目的在于隐藏类名,减少全局的标识符,从而限制用户能否使用该类建立对象.这样可以提高类的抽象能 ...

  6. 明远智睿IMX6Q Android4.4.2移植USBWIFI(RTL8188EUS)

    移植过程中得到网友的不少帮助,很感谢!为了让更多的网友不像我这样折腾,特写此文以做参照.过程中主要参考< Realtek_Wi-Fi_SDK_for_Android_KK_4_4.pdf > ...

  7. 使用keytool生成ssl证书

    使用keytool生成ssl证书 在项目中由于要使用https访问项目,然后了解到jdk有一个自带的工具keytool可以用来生成ssl证书,从而可以通过https进行访问. 使用keytool生成s ...

  8. POJ 3264.Balanced Lineup-RMQ(ST)详解

    先写一道水题的博客,为后面要写的博客做一个铺垫. ヾ(◍°∇°◍)ノ゙ RMQ(Range Minimum/Maximum Query),即区间最值查询,对于长度为n的数列A,回答若干询问RMQ(A, ...

  9. (6)java基础知识-基本数据类型、数据类型转换

    一.基本数据类型 基本的数据类型一共有四类八种 1.整型 byte:  1字节 取值范围 -128~127 short: 2字节    取值范围 -32768~32767 int:     4字节 取 ...

  10. 差分【p3948】 数据结构

    顾z 你没有发现两个字里的blog都不一样嘛 qwq 题目描述-->p3948 数据结构 分析 其实这题完全没有分析的 qwq. 只是因为写了差分数组相关知识,所以顺便写一下题解 qwq. 对于 ...