博客:blog.shinelee.me | 博客园 | CSDN

im2col实现

如何将卷积运算转为矩阵相乘?直接看下面这张图,以下图片来自论文High Performance Convolutional Neural Networks for Document Processing



上图为3D卷积的传统计算方式与矩阵乘法计算方式的对比,传统卷积运算是将卷积核以滑动窗口的方式在输入图上滑动,当前窗口内对应元素相乘然后求和得到结果,一个窗口一个结果。相乘然后求和恰好也是向量内积的计算方式,所以可以将每个窗口内的元素拉成向量,通过向量内积进行运算,多个窗口的向量放在一起就成了矩阵,每个卷积核也拉成向量,多个卷积核的向量排在一起也成了矩阵,于是,卷积运算转化成了矩阵运算。

下图为转化后的矩阵尺寸,padding为0:



代码上怎么实现呢?这里参看一下SeetaFaceEngine/FaceIdentification/src/conv_net.cpp 中的代码,与上面的图片对照着看比较直观。

int dst_h = (src_h - kernel_h) / stride_h_ + 1; // int src_h = input->height(); int kernel_h = weight->height();
int dst_w = (src_w - kernel_w) / stride_w_ + 1; // int src_w = input->width(); int kernel_w = weight->width();
int end_h = src_h - kernel_h + 1;
int end_w = src_w - kernel_w + 1;
int dst_size = dst_h * dst_w;
int kernel_size = src_channels * kernel_h * kernel_w; const int src_num_offset = src_channels * src_h * src_w; // int src_channels = input->channels();
float* const dst_head = new float[src_num * dst_size * dst_channels];
float* const mat_head = new float[dst_size * kernel_size]; const float* src_data = input->data().get();
float* dst_data = dst_head;
int didx = 0; for (int sn = 0; sn < src_num; ++sn) {
float* mat_data = mat_head;
for (int sh = 0; sh < end_h; sh += stride_h_) {
for (int sw = 0; sw < end_w; sw += stride_w_) {
for (int sc = 0; sc < src_channels; ++sc) {
int src_off = (sc * src_h + sh) * src_w + sw;
for (int hidx = 0; hidx < kernel_h; ++hidx) {
memcpy(mat_data, src_data + src_off,
sizeof(float) * kernel_w);
mat_data += kernel_w;
src_off += src_w;
}
} // for sc
} // for sw
} // for sh
src_data += src_num_offset; const float* weight_head = weight->data().get();
// int dst_channels = weight->num();
matrix_procuct(mat_head, weight_head, dst_data, dst_size, dst_channels,
kernel_size, true, false); dst_data += dst_channels * dst_size;
} // for sn

src_num 个输入,每个尺寸为 src_channels * src_h * src_w,卷积核尺寸为kernel_size = src_channels * kernel_h * kernel_w,将每个输入转化为二维矩阵,尺寸为(dst_h * dst_w) * (kernel_size),可以看到最内层循环在逐行拷贝当前窗口内的元素,窗口大小与卷积核大小相同,一次拷贝kernel_w个元素,一个窗口内要拷贝src_channels*kernel_h次,因此一个窗口共拷贝了kernel_size个元素,共拷贝dst_h * dst_w个窗口,因此输入对应的二维矩阵尺寸为(dst_h * dst_w) * (kernel_size)。对于卷积核,有dst_channels= weight->num();个卷积核,因为是行有先存储,卷积核对应的二维矩阵尺寸为dst_channels*(kernel_size)逻辑上虽然为矩阵乘法,实现时两个矩阵逐行内积即可

优缺点分析

将卷积运算转化为矩阵乘法,从乘法和加法的运算次数上看,两者没什么差别,但是转化成矩阵后,运算时需要的数据被存在连续的内存上,这样访问速度大大提升(cache),同时,矩阵乘法有很多库提供了高效的实现方法,像BLAS、MKL等,转化成矩阵运算后可以通过这些库进行加速。

缺点呢?这是一种空间换时间的方法,消耗了更多的内存——转化的过程中数据被冗余存储。

参考

im2col:将卷积运算转为矩阵相乘的更多相关文章

  1. Opencv中Mat矩阵相乘——点乘、dot、mul运算详解

    Opencv中Mat矩阵相乘——点乘.dot.mul运算详解 2016年09月02日 00:00:36 -牧野- 阅读数:59593 标签: Opencv矩阵相乘点乘dotmul 更多 个人分类: O ...

  2. MATLAB卷积运算(conv、conv2、convn)解释

    1 conv(向量卷积运算) 所谓两个向量卷积,说白了就是多项式乘法.比如:p=[1 2 3],q=[1 1]是两个向量,p和q的卷积如下:把p的元素作为一个多项式的系数,多项式按升幂(或降幂)排列, ...

  3. MATLAB卷积运算(conv、conv2)解释

    来源:https://www.cnblogs.com/hyb221512/p/9276621.html 1.conv(向量卷积运算) 所谓两个向量卷积,说白了就是多项式乘法.比如:p=[1 2 3], ...

  4. 利用Hadoop实现超大矩阵相乘之我见(二)

    前文 在<利用Hadoop实现超大矩阵相乘之我见(一)>中我们所介绍的方法有着“计算过程中文件占用存储空间大”这个缺陷,本文中我们着重解决这个问题. 矩阵相乘计算思想 传统的矩阵相乘方法为 ...

  5. 利用Hadoop实现超大矩阵相乘之我见(一)

    前记 最近,公司一位挺优秀的总务离职,欢送宴上,她对我说“你是一位挺优秀的程序员”,刚说完,立马道歉说“对不起,我说你是程序员是不是侮辱你了?”我挺诧异,程序员现在是很低端,很被人瞧不起的工作吗?或许 ...

  6. Strassen 矩阵相乘算法(转)

    偶尔在算法课本上面看到矩阵相乘的算法,联想到自己曾经在蓝桥杯系统上曾经做过一道矩阵相乘的题目,当时用的是普通的矩阵相乘的方法,效率极低,勉强通过编译.所以决定研究一下Strassen矩阵相乘算法,由于 ...

  7. dp方法论——由矩阵相乘问题学习dp解题思路

    前篇戳:dp入门——由分杆问题认识动态规划 导语 刷过一些算法题,就会十分珍惜“方法论”这种东西.Leetcode上只有题目.讨论和答案,没有方法论.往往答案看起来十分切中要害,但是从看题目到得到思路 ...

  8. Filter2D卷积运算

    图像处理中的卷积运算一般都用来平滑图像.尖锐图像求边缘等等.主要看你选择什么样的核函数了.现在核函数很多,比如高斯平滑核函数,sobel核函数,canny核函数等等.这里举一个sobel核函数的例子来 ...

  9. 基于INTEL FPGA硬浮点DSP实现卷积运算

    概述 卷积是一种线性运算,其本质是滑动平均思想,广泛应用于图像滤波.而随着人工智能及深度学习的发展,卷积也在神经网络中发挥重要的作用,如卷积神经网络.本参考设计主要介绍如何基于INTEL 硬浮点的DS ...

随机推荐

  1. Visual Studio 2017 15.7 下的.NET Core

    Visual Studio 2017 15.7版本发布,对.NET Core项目的主要相关改变如下, 同时对Xamarin.Android和iOS项目的支持上也做了较大改进. 一. .NET Core ...

  2. nginx配置 location及rewrite规则详解

    1. location正则写法 语法规则: location [=|~|~*|^~] /uri/ { … } =    开头表示精确匹配 ^~  开头表示uri以某个常规字符串开头,理解为匹配 url ...

  3. ThinkPHP5从零基础搭建CMS系统(二)

    接上节,开启wamp集成环境,在浏览器地址栏输入http://localhost/cms/public,即可运行项目,但是这边域名太长,做一下处理. 注:需要查看tp5全部教程,请点击右侧thinkp ...

  4. GitHub学习笔记:远程端的操控

    对于远端,当你新建一个项目的时候,需要在网页处新建,在新建项目的页面,会有一段提示你上传本地项目到此远端方法的代码,直接拷贝粘贴到git shell就可以解决问题,不再详述. 当你把代码上传到一个已经 ...

  5. This 在构造函数中的固定用法

    class Person{ String name; int age; char gender; public Person (String name){ this.name = name; } pu ...

  6. .NET开发微信小程序-上传图片到服务器

    1.上传图片分为几种: a:上传图片到本地(永久保存) b:上传图片到本地(临时保存) c:上传图片到服务器 a和b在小程序的api文档里面有.直接说C:上传图片到服务器 前端代码: /* 上传图片到 ...

  7. Python_字符串格式化

    #冒泡排序 array = [1,2,3,6,5,4] for i in range(len(array)): for j in range(i): if array[j] > array[j ...

  8. Unity3D学习(四):小游戏Konster的整体代码重构

    前言 翻了下之前写的代码,画了个图看了下代码结构,感觉太烂了,有很多地方的代码重复啰嗦,耦合也紧,开个随笔记录下重构的过程. 过程 _____2017.10.13_____ 结构图: 目前发现的待改进 ...

  9. oracle批量插入测试数据

    做数据库开发或管理的人经常要创建大量的测试数据,动不动就需要上万条,如果一条一条的录入,那会浪费大量的时间,本文介绍了Oracle中如何通过一条 SQL快速生成大量的测试数据的方法.产生测试数据的SQ ...

  10. 并发库应用之十 & 多线程数据交换Exchanger应用

    申明:用大白话来说就是用于实现两个人之间的数据交换,每个人在完成一定的事务后想与对方交换数据,第一个先拿出数据的人会一直等待第二个人,直到第二个人拿着数据到来时,才能彼此交换数据. java.util ...