im2col:将卷积运算转为矩阵相乘
博客: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等,转化成矩阵运算后可以通过这些库进行加速。
缺点呢?这是一种空间换时间的方法,消耗了更多的内存——转化的过程中数据被冗余存储。
参考
- 在 Caffe 中如何计算卷积?
- High Performance Convolutional Neural Networks for Document Processing
- algorithm convolution / filter and cross-correlation and implementation
- Convolution in Caffe: a memo
im2col:将卷积运算转为矩阵相乘的更多相关文章
- Opencv中Mat矩阵相乘——点乘、dot、mul运算详解
Opencv中Mat矩阵相乘——点乘.dot.mul运算详解 2016年09月02日 00:00:36 -牧野- 阅读数:59593 标签: Opencv矩阵相乘点乘dotmul 更多 个人分类: O ...
- MATLAB卷积运算(conv、conv2、convn)解释
1 conv(向量卷积运算) 所谓两个向量卷积,说白了就是多项式乘法.比如:p=[1 2 3],q=[1 1]是两个向量,p和q的卷积如下:把p的元素作为一个多项式的系数,多项式按升幂(或降幂)排列, ...
- MATLAB卷积运算(conv、conv2)解释
来源:https://www.cnblogs.com/hyb221512/p/9276621.html 1.conv(向量卷积运算) 所谓两个向量卷积,说白了就是多项式乘法.比如:p=[1 2 3], ...
- 利用Hadoop实现超大矩阵相乘之我见(二)
前文 在<利用Hadoop实现超大矩阵相乘之我见(一)>中我们所介绍的方法有着“计算过程中文件占用存储空间大”这个缺陷,本文中我们着重解决这个问题. 矩阵相乘计算思想 传统的矩阵相乘方法为 ...
- 利用Hadoop实现超大矩阵相乘之我见(一)
前记 最近,公司一位挺优秀的总务离职,欢送宴上,她对我说“你是一位挺优秀的程序员”,刚说完,立马道歉说“对不起,我说你是程序员是不是侮辱你了?”我挺诧异,程序员现在是很低端,很被人瞧不起的工作吗?或许 ...
- Strassen 矩阵相乘算法(转)
偶尔在算法课本上面看到矩阵相乘的算法,联想到自己曾经在蓝桥杯系统上曾经做过一道矩阵相乘的题目,当时用的是普通的矩阵相乘的方法,效率极低,勉强通过编译.所以决定研究一下Strassen矩阵相乘算法,由于 ...
- dp方法论——由矩阵相乘问题学习dp解题思路
前篇戳:dp入门——由分杆问题认识动态规划 导语 刷过一些算法题,就会十分珍惜“方法论”这种东西.Leetcode上只有题目.讨论和答案,没有方法论.往往答案看起来十分切中要害,但是从看题目到得到思路 ...
- Filter2D卷积运算
图像处理中的卷积运算一般都用来平滑图像.尖锐图像求边缘等等.主要看你选择什么样的核函数了.现在核函数很多,比如高斯平滑核函数,sobel核函数,canny核函数等等.这里举一个sobel核函数的例子来 ...
- 基于INTEL FPGA硬浮点DSP实现卷积运算
概述 卷积是一种线性运算,其本质是滑动平均思想,广泛应用于图像滤波.而随着人工智能及深度学习的发展,卷积也在神经网络中发挥重要的作用,如卷积神经网络.本参考设计主要介绍如何基于INTEL 硬浮点的DS ...
随机推荐
- 技术人应该学习的行话--UML统一建模语言
新生代码农如何在硝烟弥漫的商业丛林中生存和崛起? 洞见,让一部分先遇见未来. 最近公司技术部在组织架构师培训,有幸参与.导师老刘特别推荐了UML语言的学习.回想多年来,自己习惯做一些流程图,框图或者所 ...
- Linux 系统化学习系列文章总目录(持续更新中)
本页内容都是本人系统化学习Linux 时整理出来的.这些文章中,绝大多数命令类内容都是翻译.整理man或info文档总结出来的,所以相对都比较完整. 本人的写作方式.风格也可能会让朋友一看就恶心到直接 ...
- 微信小程序录音实现
最近在写一个小程序的功能,需求是需要把用户的录音记录并上传到服务器,然后调用接口来实现播放功能. 翻阅不少资料都发现小程序对wx.startRecord()接口不在维护, 注意:1.6.0 版本开始, ...
- jqery对于select级联操作
问题:今天在做一个需求的时候,有一个级联操作也就是选中下拉框的一列就显示对对应的数据 处理:我在做级联的时候在option的列里面绑定click的事件发现这个事件行不通:查资料发现select触发的是 ...
- JAVA之单源最短路径(Single Source Shortest Path,SSSP问题)dijkstra算法求解
题目简介:给定一个带权有向图,再给定图中一个顶点(源点),求该点到其他所有点的最短距离,称为单源最短路径问题. 如下图,求点1到其他各点的最短距离 准备工作:以下为该题所需要用到的数据 int N; ...
- udp服务端收发数据流程
1.创建服务端的socket以便开始通讯.2.绑定ip以及端口号,这样客户端才能找到这个程序.3.因为本地网卡不止一个所以尽量不写死,一般用""空来表示所有本地网卡.4.接下来开始 ...
- maven包加载
1) IDEA包加载pom.xml配置 <build> <sourceDirectory>src/main/java</sourceDirectory> ...
- super()方法的使用
如果在子类中也定义了构造器,既_init_()函数,那么基类的构造器该如何调用呢? 方法一.明确指定 使用一个子类的实例去调用基类的构造器,在子类的构造器中明确的指明调用基类的构造器. class C ...
- mac 登录亚马逊云服务器报错:Permission denied (publickey).
申请的亚马逊云服务器EC2,实例为ubuntu系统 一.打开终端,定位到放置密钥的文件夹: 二.确保私有秘钥不是公开可见的: p.p1 { margin: 0.0px 0.0px 0.0px 0.0p ...
- MySQL varchar类型数据转tinyint类型
在mysql数据库中性别字段以前存的是'男'和'女',使用varchar类型存储的,但是在我mongo库中这个字段使用的是'1'和'0'存储的,在两个库之间的数据转换就很不方便,于是想要统一存储类型, ...