热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[置顶]caffe卷积层代码阅读笔记

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

卷积的实现思想:

  • 通过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函数是一个道理,两套坐标反着来就行)。把索引的代码整合出来,对着源代码看,很容易懂:

    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的行


推荐阅读
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • RouterOS 5.16软路由安装图解教程
    本文介绍了如何安装RouterOS 5.16软路由系统,包括系统要求、安装步骤和登录方式。同时提供了详细的图解教程,方便读者进行操作。 ... [详细]
  • C语言自带的快排和二分查找
    Author🚹:CofCaiEmail✉️:cai.dongjunnexuslink.cnQQ😙:1664866311personalPage&#x ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • 本文讨论了如何使用GStreamer来删除H264格式视频文件中的中间部分,而不需要进行重编码。作者提出了使用gst_element_seek(...)函数来实现这个目标的思路,并提到遇到了一个解决不了的BUG。文章还列举了8个解决方案,希望能够得到更好的思路。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
author-avatar
惯性hold不住
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有