近期学习了下img2col的原理,自己写了版用C++实现的img2col。img2col对于熟悉深度学习的卷积计算的人来说应该并不陌生,它是一种将普通卷积计算转换为一次矩阵乘法计算的算法,相较于直接计算卷积来说能提升计算效率。
关于img2col的原理,网上有非常多的讲解,在此不再多作描述,不了解的人可以自行百度或参考以下链接:
我所实现的代码,正是基于上文中卷积加速算法的最后一种,也就是利用分块矩阵乘法的计算特性,将加偏置的步骤合并到一次矩阵乘法中,省去了一步矩阵加法。也就是下图中的算法(来自于以上链接):
关键代码如下,如需要测试代码及pytorch_python版的正确性验证代码,请到以下链接下载:
自己实现的img2col-C++代码+测试代码+pytorch-python验证代码
#include <Eigen/Core>
using namespace std;
using namespace Eigen;
typedef struct convRet
{
int num;
int width;
int height;
int* data;
}convRet;
/***
input:
imgw 原图像素宽
imgh 原图像素高
img 原图像素值
pad padding值
stride 步长
convN 卷积核数目
convW 卷积核宽
convH 卷积核高
conv 卷积核数值
bias 偏置值
output 计算后的输出
**/
void myImg2col(int imgw, int imgh, int* img, int pad, int stride, int convN, int convW, int convH,
int* conv, int* bias, convRet* output) {
int unfoldW = convW * convH + 1;//img展开后的宽,此处加了偏移量扩展
int unfoldH = ((pad * 2 + imgh - convH) / stride + 1)*((pad * 2 + imgw - convW) / stride + 1);//img展开后的高=卷积乘法次数
int brow = 0 - pad;
int bcol = 0 - pad;
//定义存放img展开后的矩阵的eigen矩阵
MatrixXi unfold(unfoldH, unfoldW);
for (int h = 0; h < unfoldH; h++) {
if (bcol + convW > imgw + pad) {//水平方向上超出了pad后的图片,要换行
bcol = 0 - pad;
brow += stride;
}
if (brow + convH > imgh + pad) {//竖直方向上已经超出pad后的图片了,一般外层循环就限制了不会到这步,加一个保险
break;
}
for (int w = 0; w < unfoldW - 1; w++) {
int rowBias = w / convW;
int colBias = w % convW;
if (brow + rowBias < 0 || bcol + colBias < 0 || brow + rowBias >= imgh || bcol + colBias >= imgw) {
//满足此条件则说明在pad的区域里
unfold(h, w) = 0;
}
else {
unfold(h, w) = img[(brow + rowBias)*imgw + bcol + colBias];
}
}
unfold(h, unfoldW - 1) = 1;
bcol += stride;
}
//定义存放展开后的卷积核的eigen矩阵
int unfoldConvLen = convH * convW + 1;
MatrixXi unfoldConv(convN, unfoldConvLen);
for (int n = 0; n < convN; n++) {
for (int m = 0; m < unfoldConvLen - 1; m++) {
unfoldConv(n, m) = conv[n*convW*convH + (m / convW)*convW + m % convW];
}
unfoldConv(n, unfoldConvLen - 1) = bias[n];
}
//两矩阵相乘
MatrixXi retMat = unfold * unfoldConv.transpose();
//取结果
output->num = convN;
output->width = (pad * 2 + imgw - convW) / stride + 1;
output->height = (pad * 2 + imgh - convH) / stride + 1;
output->data = (int*)malloc(sizeof(int)*convN*output->width*output->height);
for (int n = 0; n < output->num; n++) {
for (int h = 0; h < output->height; h++) {
for (int w = 0; w < output->width; w++) {
output->data[n*output->width*output->height + h * output->width + w] = retMat(h*output->width + w, n);
}
}
}
}