19

(图解)一步一步使用CPP实现深度学习中的卷积

 4 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzA4MjY4NTk0NQ%3D%3D&%3Bmid=2247492333&%3Bidx=1&%3Bsn=0645569380ca1c1376dd45767b2343b9
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【GiantPandaCV导语】 卷积操作在深度学习中的重要性,想必大家都很清楚了。接下来将通过图解的方式,使用cpp一步一步从简单到复杂来实现卷积操作。

  • 符号约定 F 为输入; width 为输入的宽; height 为输入的高; channel 为输入的通道; K 为kernel; kSizeX 为kernel的宽; kSizeY 为kernel的高; filters 为kernel的个数; O 为输出; outWidth 为输出的宽; outHeight 为输出的高; outChannel 为输出的通道;

  • 卷积输出尺寸计算公式

  • 1. 最简单的3x3卷积首先, 我们不考虑任何padding, stride, 多维度等情况,来一个最简单的3x3卷积操作.计算思路很简单, 对应元素相乘最后相加即可.此处:

    • width=3

    • height=3

    • channel=1

    • paddingX=0

    • paddingY=0

    • strideX=1

    • strideY=1

    • dilationX=1

    • dilationY=1

    • kSizeX=3

    • kSizeY=3

    • filters=1

      可根据卷积输出尺寸计算公式,得到:

    • outWidth=1

    • outHeight=1

    • outChannel=1

322AN3m.png!mobile图1 最简单的3x3卷积

cpp代码:

void demo0()
{
float F[] = {1,2,3,4,5,6,7,8,9};
float K[] = {1,2,3,4,5,6,7,8,9};
float O = 0;

int width = 3;
int height = 3;
int kSizeX = 3;
int kSizeY = 3;

for(int m=0;m<kSizeY;m++)
{
for(int n=0;n<kSizeX;n++)
{
O+=K[m*kSizeX+n]*F[m*width+n];
}
}

std::cout<<O<<" ";
}
  • 2. 最简单卷积(1)接下来考虑能适用于任何尺寸的简单卷积, 如输入为4x4x1, kernel为3x3x1. 这里考虑卷积步长为1, 则此处的参数为:

    cpp代码:

    • width=4

    • height=4

    • channel=1

    • paddingX=0

    • paddingY=0

    • strideX=1

    • strideY=1

    • dilationX=1

    • dilationY=1

    • kSizeX=3

    • kSizeY=3

    • filters=1

      可根据卷积输出尺寸计算公式,得到:

    • outWidth=2

    • outHeight=2

    • outChannel=1 zUJvmaQ.png!mobile

      图2 最简单卷积(1)
void demo1()
{
float F[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
float K[] = {1,2,3,4,5,6,7,8,9};
float O[] = {0,0,0,0};

int padX = 0;
int padY = 0;

int dilationX = 1;
int dilationY = 1;

int strideX = 1;
int strideY = 1;

int width = 4;
int height = 4;

int kSizeX = 3;
int kSizeY = 3;

int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;

for(int i=0;i<outH;i++)
{
for(int j=0;j<outW;j++)
{
for(int m=0;m<kSizeY;m++)
{
for(int n=0;n<kSizeX;n++)
{
O[i*outW+j]+=K[m*kSizeX+n]*F[(m+i)*width+(n+j)];
}
}
}
}

for (int i = 0; i < outH; ++i)
{
for (int j = 0; j < outW; ++j)
{
std::cout<<O[i*outW+j]<<" ";
}
std::cout<<std::endl;
}
}
  • 3. 最简单卷积(2)接下来考虑在步长上为任意步长的卷积,  如输入为4x4x1, kernel为2x2x1. 这里考虑卷积步长为2, 则此处的参数为:

    • width=4

    • height=4

    • channel=1

    • paddingX=0

    • paddingY=0

    • strideX=2

    • strideY=2

    • dilationX=1

    • dilationY=1

    • kSizeX=2

    • kSizeY=2

    • filters=1

      可根据卷积输出尺寸计算公式,得到:

    • outWidth=2

    • outHeight=2

    • outChannel=1

2mia6zR.png!mobile图3 最简单卷积(2)

cpp代码:

void demo2()
{
float F[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
float K[] = {1,2,3,4};
float O[] = {0,0,0,0};

int padX = 0;
int padY = 0;

int dilationX = 1;
int dilationY = 1;

int strideX = 2;
int strideY = 2;

int width = 4;
int height = 4;

int kSizeX = 2;
int kSizeY = 2;

int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;

for(int i=0;i<outH;i++)
{
for(int j=0;j<outW;j++)
{
for(int m=0;m<kSizeY;m++)
{
for(int n=0;n<kSizeX;n++)
{
O[i*outW+j]+=K[m*kSizeX+n]*F[(m+i*strideY)*width+(n+j*strideX)];
}
}
}
}

for (int i = 0; i < outH; ++i)
{
for (int j = 0; j < outW; ++j)
{
std::cout<<O[i*outW+j]<<" ";
}
std::cout<<std::endl;
}
}
  • 4. 带padding的卷积接下来考虑带padding的卷积,  如输入为4x4x1, kernel为3x3x1. 卷积步长为1, padding为1 则此处的参数为:

    cpp代码:

    • width=4

    • height=4

    • channel=1

    • paddingX=1

    • paddingY=1

    • strideX=1

    • strideY=1

    • dilationX=1

    • dilationY=1

    • kSizeX=3

    • kSizeY=3

    • filters=1

      可根据卷积输出尺寸计算公式,得到:

    • outWidth=2

    • outHeight=2

    • outChannel=1 QF3mmyv.png!mobile

      图4 考虑padding卷积
void demo3()
{
float F[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
float K[] = {1,2,3,4,5,6,7,8,9};
float O[] = {0,0,0,0};

int padX = 1;
int padY = 1;

int dilationX = 1;
int dilationY = 1;

int strideX = 2;
int strideY = 2;

int width = 4;
int height = 4;

int kSizeX = 3;
int kSizeY = 3;

int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;

for(int i=0;i<outH;i++)
{
for(int j=0;j<outW;j++)
{
for(int m=0;m<kSizeY;m++)
{
for(int n=0;n<kSizeX;n++)
{
float fVal = 0;
//考虑边界强情况
if((n+j*strideX-padX)>-1&&(m+i*strideY-padY>-1)&&(n+j*strideX-padX)<=width&&(m+i*strideY-padY>-1)<=height)
{
fVal = F[(m+i*strideY-padX)*width+(n+j*strideX-padY)];
}
O[i*outW+j]+=K[m*kSizeX+n]*fVal;
}
}
}
}

for (int i = 0; i < outH; ++i)
{
for (int j = 0; j < outW; ++j)
{
std::cout<<O[i*outW+j]<<" ";
}
std::cout<<std::endl;
}
}
  • 5. 多通道卷积接下来考虑多通道卷积,  如输入为4x4x1, kernel为3x3x1. 卷积步长为1, padding为1, 输入通道为2, 则此处的参数为:

    cpp代码:

    • width=4

    • height=4

    • channel=2

    • paddingX=1

    • paddingY=1

    • strideX=1

    • strideY=1

    • dilationX=1

    • dilationY=1

    • kSizeX=3

    • kSizeY=3

    • filters=1

      可根据卷积输出尺寸计算公式,得到:

    • outWidth=2

    • outHeight=2

    • outChannel=1 YJ3iueQ.png!mobile

      图5 多通道卷积
void demo4()
{
float F[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
float K[] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9};
float O[] = {0,0,0,0};

int padX = 1;
int padY = 1;

int dilationX = 1;
int dilationY = 1;

int strideX = 2;
int strideY = 2;

int width = 4;
int height = 4;

int kSizeX = 3;
int kSizeY = 3;

int channel = 2;

int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;

for (int c = 0; c < channel; ++c)
{
for(int i=0;i<outH;i++)
{
for(int j=0;j<outW;j++)
{
for(int m=0;m<kSizeY;m++)
{
for(int n=0;n<kSizeX;n++)
{
float fVal = 0;
if((n+j*strideX-padX)>-1&&(m+i*strideY-padY>-1)&&(n+j*strideX-padX)<=width&&(m+i*strideY-padY>-1)<=height)
{
fVal = F[c*width*height + (m+i*strideY-padX)*width+(n+j*strideX-padY)];
}
O[i*outW+j]+=K[c*kSizeX*kSizeY+m*kSizeX+n]*fVal;
}
}
}
}
}

for (int i = 0; i < outH; ++i)
{
for (int j = 0; j < outW; ++j)
{
std::cout<<O[i*outW+j]<<" ";
}
std::cout<<std::endl;
}
}
  • 6. 多kernel卷积接下来考虑多kernel卷积,  如输入为4x4x1, kernel为3x3x1. 卷积步长为1, padding为1, 输入通道为2, filters为2, 则此处的参数为:

    • width=4

    • height=4

    • channel=2

    • paddingX=1

    • paddingY=1

    • strideX=1

    • strideY=1

    • dilationX=1

    • dilationY=1

    • kSizeX=3

    • kSizeY=3

    • filters=2

      可根据卷积输出尺寸计算公式,得到:

    • outWidth=2

    • outHeight=2

    • outChannel=2

qqeE7n.png!mobile图6 多kernel卷积

cpp代码:

void demo5()
{
float F[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
float K[] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,
1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9
};
float O[] = {0,0,0,0,0,0,0,0};

int padX = 1;
int padY = 1;

int dilationX = 1;
int dilationY = 1;

int strideX = 2;
int strideY = 2;

int width = 4;
int height = 4;

int kSizeX = 3;
int kSizeY = 3;

int channel = 2;

int filters = 2;

int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;

int outC = filters;

for (int oc = 0; oc < outC; ++oc)
{
for (int c = 0; c < channel; ++c)
{
for(int i=0;i<outH;i++)
{
for(int j=0;j<outW;j++)
{
for(int m=0;m<kSizeY;m++)
{
for(int n=0;n<kSizeX;n++)
{
float fVal = 0;
if((n+j*strideX-padX)>-1&&(m+i*strideY-padY>-1)&&(n+j*strideX-padX)<=width&&(m+i*strideY-padY>-1)<=height)
{
fVal = F[c*width*height + (m+i*strideY-padX)*width+(n+j*strideX-padY)];
}
O[oc*outH*outW+i*outW+j]+=K[oc*outC*kSizeX*kSizeY+c*kSizeX*kSizeY+m*kSizeX+n]*fVal;
}
}
}
}
}
}

for (int oc = 0; oc < outC; ++oc)
{
for (int i = 0; i < outH; ++i)
{
for (int j = 0; j < outW; ++j)
{
std::cout<<O[oc*outH*outW+i*outW+j]<<" ";
}
std::cout<<std::endl;
}
std::cout<<std::endl<<std::endl;
}
}
  • 7. 膨胀卷积接下来考虑多膨胀卷积,  如输入为4x4x1, kernel为3x3x1. 卷积步长为1, padding为1, 输入通道为2, filters为2, dilate为2则此处的参数为:

    • width=4

    • height=4

    • channel=2

    • paddingX=1

    • paddingY=1

    • strideX=1

    • strideY=1

    • dilationX=2

    • dilationY=2

    • kSizeX=3

    • kSizeY=3

    • filters=2

      可根据卷积输出尺寸计算公式,得到:

    • outWidth=2

    • outHeight=2

    • outChannel=2

3INRjeB.png!mobile图7 膨胀卷积

cpp代码:

void demo6()
{
float F[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
float K[] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,
1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9
};
float O[] = {0,0,0,0,0,0,0,0};

int padX = 1;
int padY = 1;

int dilationX = 2;
int dilationY = 2;

int strideX = 1;
int strideY = 1;

int width = 4;
int height = 4;

int kSizeX = 3;
int kSizeY = 3;

int channel = 2;

int filters = 2;

int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;

int outC = filters;

for (int oc = 0; oc < outC; ++oc)
{
for (int c = 0; c < channel; ++c)
{
for(int i=0;i<outH;i++)
{
for(int j=0;j<outW;j++)
{
for(int m=0;m<kSizeY;m++)
{
for(int n=0;n<kSizeX;n++)
{
float fVal = 0;
if( ((n+j*strideX)*dilationX-padX)>-1 && ((m+i*strideY)*dilationY-padY)>-1&&
((n+j*strideX)*dilationX-padX)<=width && ((m+i*strideY)*dilationY-padY>-1)<=height)
{
fVal = F[c*width*height + ((m+i*strideY)*dilationX-padX)*width+((n+j*strideX)*dilationY-padY)];
}
O[oc*outH*outW+i*outW+j]+=K[oc*outC*kSizeX*kSizeY+c*kSizeX*kSizeY+m*kSizeX+n]*fVal;
}
}
}
}
}
}

for (int oc = 0; oc < outC; ++oc)
{
for (int i = 0; i < outH; ++i)
{
for (int j = 0; j < outW; ++j)
{
std::cout<<O[oc*outH*outW+i*outW+j]<<" ";
}
std::cout<<std::endl;
}
std::cout<<std::endl;
}
}
  • git源码https://github.com/msnh2012/ConvTest

  • 最后欢迎关注我和BBuf及公众号的小伙伴们一块维护的一个深度学习框架Msnhnet:  https://github.com/msnh2012/Msnhnet

欢迎关注GiantPandaCV, 在这里你将看到独家的深度学习分享,坚持原创,每天分享我们学习到的新鲜知识。( • ̀ω•́ )✧

有对文章相关的问题,或者想要加入交流群,欢迎添加BBuf微信:

uEjqIr7.png!mobile二维码

为了方便读者获取资料以及我们公众号的作者发布一些Github工程的更新,我们成立了一个QQ群,二维码如下,感兴趣可以加入。

j6NJfya.png!mobile公众号QQ交流群

Recommend

  • 66
    • 微信 mp.weixin.qq.com 6 years ago
    • Cache

    什么是深度学习的卷积?

  • 47
    • 微信 mp.weixin.qq.com 6 years ago
    • Cache

    [译] 什么是深度学习的卷积?

    来源:Medium 编译:Bot 近年来,随着一些强大、通用的深度学习框架相继出现,把卷积层添加进深度学习模型也成了可能。这个过程很简单,只需一行代码就能实现。但是,你真的理解“卷积”是什么吗?当初学...

  • 27
    • www.cnblogs.com 4 years ago
    • Cache

    深度学习三:卷积神经网络

    卷积神经网络 卷积神经网络(Convolutional Neural Network,CNN)又叫 卷积网络(Convolutional Network) ,是一种专门用来处理具有类似网格结构的数据的神经网络。卷积神经网络一词中的卷积是一种特殊的...

  • 13

    原文来源于魏秀参博士的《解析深度学习:卷积神经网络原理与视觉实践》,本书由周志华老师作序推荐:“市面上深度学习书籍已不少,但专门针对卷积神经网络展开,侧重实践有不失论释者尚不多见。本书基本覆盖了卷积神经网络实践所有涉及环节,作者交...

  • 10

    深度学习-卷积神经网络-目标检测之YOLOV3模型-代码运行图片检测实践2_firemonkeycs的专栏-CSDN博客 深度学习-卷积神经网...

  • 14

    ↑ 点击 蓝字  关注极市平台 作者丨Redflashing@...

  • 11

    一、前期工作 本文将实现灵笼中人物角色的识别。较上一篇文章,这次我采用了VGG-19结构,并增加了预测与保存and加载模型两个部分。 我的环境: 语言环境:Python3.6.5编译器:jupyt...

  • 12
    • seanwangjs.github.io 2 years ago
    • Cache

    图解卷积运算的 im2col 算法

    图解卷积运算的 im2col 算法 卷积运算是一种提取图像特征的重要算法,它通过把给定的卷积核应用到图像上,从而缩减图像尺寸,达到提取特征的目的。卷积核在图像上每移动一次就对其覆盖的区域进行一次运算,具体的算法其实就是对应元素相乘然后求...

  • 7
    • antkillerfarm.github.io 2 years ago
    • Cache

    深度学习(十一)——花式卷积(2)

    分组卷积(续) 上图是ShuffleNet的Unit结构图,DWConv表示depthwise convolution,GConv表示pointwise group convolution。a是普通的Deep Residual Unit,b的进化用以提高精度,c的进一步进化用以减少计算量。 https://blog.yani.io/filte...

  • 5
    • antkillerfarm.github.io 2 years ago
    • Cache

    深度学习(十)——花式卷积(1)

    CNN进化史 总结(续) 前深度学习时代的人脸识别的标准流程: 第一步是人脸检测,其结果就是在图片中的脸部区域打一个矩形框。 第二步是寻找眼睛、鼻子、嘴等特征点,目的是把脸对齐,也就是把眼鼻嘴放在近乎相同的位置,...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK