当前位置:   article > 正文

CNN学习(7):用C++实现简单不同参数的卷积模型

CNN学习(7):用C++实现简单不同参数的卷积模型

目录

一、参数说明和计算公式

1、符号约定

2、输出大小计算公式

二、不同类型的卷积

1、输入3*3*1,卷积核3*3*1,输出1*1*1

(1)实现代码

(2)代码说明

2、输入4*4*1,卷积核3*3*1,步长1*1,输出2*2*1

(1)实现代码

(2)代码说明

3、输入4*4*1,卷积核2*2*1,步长2*2,输出2*2*1

(1)实现代码

4、输入4*4*1,卷积核3*3*1,步长1*1,padding为1*1,输出2*2*1

​编辑

(1)实现代码

(2)代码说明

5、输入4*4*2,卷积核3*3*2,步长1*1,padding为1*1,输出2*2*1

(1)实现代码

6、输入4*4*1,卷积核3*3*1,卷积核个数2,步长1*1,padding为1*1,输出2*2*1

(1)实现代码

(2)代码说明

7、输入4*4*2,卷积核3*3*2,步长1*1,padding为1*1,膨胀系数2,输出2*2*2

三、可执行的卷积操作代码


参考资料:

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

其实在上述链接里已经写的非常详细了,为了便于后续理解和学习,所以写个理解的备忘录。

一、参数说明和计算公式

1、符号约定

  1. F[] 为输入;
  2. width 为输入的宽;
  3. height 为输入的高;
  4. channel 为输入的通道;
  5. K[] 为 kernel;
  6. kSizeX 为 kernel 的宽;
  7. kSizeY 为 kernel 的高;
  8. filters 为 kernel 的个数;
  9. padX 为水平方向的填充;
  10. padY 为垂直方向的填充;
  11. strideX 为水平方向的步长;
  12. strideY 为垂直方向的步长;
  13. O[] 为输出;
  14. outW 为输出的宽;
  15. outH 为输出的高;
  16. outChannel 为输出的通道;

2、输出大小计算公式

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

公式说明:

  • height + 2 * padY: 首先,将输入图像的高度加上两倍的填充量,这是因为填充是对称地加在图像的顶部和底部。

  • dilationY * (kSizeY - 1): 这是计算卷积核中除了中心元素外,其他元素之间的总间隔。kSizeY - 1表示卷积核的高度减去中心元素,乘以膨胀系数dilationY,得到这些元素之间的间隔数。

  • dilationY * (kSizeY - 1) + 1: 将上述间隔数加上1,是因为我们还要包括卷积核的中心元素。

  • (height + 2 * padY - (dilationY * (kSizeY - 1) + 1)): 这个表达式计算了在考虑填充和膨胀后,输入图像实际上被卷积核覆盖的区域的高度。

  • / strideY: 将上述计算得到的高度除以步长,得到在垂直方向上卷积核可以覆盖的步数。

  • + 1: 最后,由于输出的高度至少要有一个元素,即使计算结果为0,我们也需要加1,以确保输出尺寸至少为1。

二、不同类型的卷积

1、输入3*3*1,卷积核3*3*1,输出1*1*1

(1)实现代码

封装成demo0(),main()函数可直接调用

  1. void demo0()
  2. {
  3. float F[] = {1,2,3,4,5,6,7,8,9};
  4. float K[] = {1,2,3,4,5,6,7,8,9};
  5. float O = 0;
  6. int width = 3;
  7. int height = 3;
  8. int kSizeX = 3;
  9. int kSizeY = 3;
  10. for(int m=0;m<kSizeY;m++)
  11. {
  12. for(int n=0;n<kSizeX;n++)
  13. {
  14. O+=K[m*kSizeX+n]*F[m*width+n];
  15. }
  16. }
  17. std::cout<<O<<" ";
  18. }

(2)代码说明

  1. 前面都是定义输入矩阵和卷积核,同时定义一个浮点数变量O并初始化为0,用于累加结果。
  2. 两个嵌套循环遍历卷积核的每个元素:
    1. for(int m=0; m<kSizeY; m++):外层循环遍历卷积核的高度。
    2. for(int n=0; n<kSizeX; n++):内层循环遍历卷积核的宽度。
    3. 嵌套循环内部,O += K[m*kSizeX + n] * F[m*width + n];用来执行卷积操作。这行代码将卷积核的当前元素 K[m*kSizeX + n] 与输入矩阵的对应元素 F[m*width + n] 相乘,并将结果累加到 O 中【其中位置是相同的,m只是用来决定以kSize为单位的轮次】。

2、输入4*4*1,卷积核3*3*1,步长1*1,输出2*2*1

(1)实现代码

  1. void demo1()
  2. {
  3. float F[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
  4. float K[] = {1,2,3,4,5,6,7,8,9};
  5. float O[] = {0,0,0,0};
  6. int padX = 0;
  7. int padY = 0;
  8. int dilationX = 1;
  9. int dilationY = 1;
  10. int strideX = 1;
  11. int strideY = 1;
  12. int width = 4;
  13. int height = 4;
  14. int kSizeX = 3;
  15. int kSizeY = 3;
  16. int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
  17. int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;
  18. for(int i=0;i<outH;i++)
  19. {
  20. for(int j=0;j<outW;j++)
  21. {
  22. for(int m=0;m<kSizeY;m++)
  23. {
  24. for(int n=0;n<kSizeX;n++)
  25. {
  26. O[i*outW+j]+=K[m*kSizeX+n]*F[(m+i)*width+(n+j)];
  27. }
  28. }
  29. }
  30. }
  31. for (int i = 0; i < outH; ++i)
  32. {
  33. for (int j = 0; j < outW; ++j)
  34. {
  35. std::cout<<O[i*outW+j]<<" ";
  36. }
  37. std::cout<<std::endl;
  38. }
  39. }

(2)代码说明

  1. 前面都是定义输入矩阵和卷积核,同时定义一个浮点数变量O并初始化【和前面后面一样,都是先计算出来输出结果的大小,然后再执行】,用于累加结果。
  2. 四个嵌套循环开始进行卷积计算:
    1. 外层两个循环遍历输出特征图的每个位置。
    2. 内层两个循环遍历卷积核的每个元素。
  3. 在内层循环中,计算卷积核覆盖的输入特征图区域的加权和,并将结果累加到输出特征图的相应位置。

3、输入4*4*1,卷积核2*2*1,步长2*2,输出2*2*1

(1)实现代码

  1. void demo2()
  2. {
  3. // (height + 2 * paddingY - (dilationY * (kSizeY - 1) + 1)) / strideY + 1;
  4. // (width + 2 * paddingX - (dilationX * (kSizeX - 1) + 1)) / strideX + 1;
  5. float F[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
  6. float K[] = {1,2,3,4};
  7. //float K[] = {1,2,3,4,5,6,7,8,9};
  8. float O[] = {0,0,0,0};
  9. int padX = 0;
  10. int padY = 0;
  11. int dilationX = 1;
  12. int dilationY = 1;
  13. int strideX = 2;
  14. int strideY = 2;
  15. int width = 4;
  16. int height = 4;
  17. int kSizeX = 2;
  18. int kSizeY = 2;
  19. int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
  20. int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;
  21. for(int i=0;i<outH;i++)
  22. {
  23. for(int j=0;j<outW;j++)
  24. {
  25. for(int m=0;m<kSizeY;m++)
  26. {
  27. for(int n=0;n<kSizeX;n++)
  28. {
  29. O[i*outW+j]+=K[m*kSizeX+n]*F[(m+i*strideY)*width+(n+j*strideX)];
  30. }
  31. }
  32. }
  33. }
  34. for (int i = 0; i < outH; ++i)
  35. {
  36. for (int j = 0; j < outW; ++j)
  37. {
  38. std::cout<<O[i*outW+j]<<" ";
  39. }
  40. std::cout<<std::endl;
  41. }
  42. }

这个和上一段代码解释相同,只不过步长和卷积核大小有改变。

4、输入4*4*1,卷积核3*3*1,步长1*1,padding为1*1,输出2*2*1

(1)实现代码

  1. void demo3()
  2. {
  3. float F[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
  4. float K[] = {1,2,3,4,5,6,7,8,9};
  5. float O[] = {0,0,0,0};
  6. int padX = 1;
  7. int padY = 1;
  8. int dilationX = 1;
  9. int dilationY = 1;
  10. int strideX = 2;
  11. int strideY = 2;
  12. int width = 4;
  13. int height = 4;
  14. int kSizeX = 3;
  15. int kSizeY = 3;
  16. int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
  17. int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;
  18. for(int i=0;i<outH;i++)
  19. {
  20. for(int j=0;j<outW;j++)
  21. {
  22. for(int m=0;m<kSizeY;m++)
  23. {
  24. for(int n=0;n<kSizeX;n++)
  25. {
  26. float fVal = 0;
  27. //考虑边界强情况
  28. if((n+j*strideX-padX)>-1&&(m+i*strideY-padY>-1)&&(n+j*strideX-padX)<=width&&(m+i*strideY-padY>-1)<=height)
  29. {
  30. fVal = F[(m+i*strideY-padX)*width+(n+j*strideX-padY)];
  31. }
  32. O[i*outW+j]+=K[m*kSizeX+n]*fVal;
  33. }
  34. }
  35. }
  36. }
  37. for (int i = 0; i < outH; ++i)
  38. {
  39. for (int j = 0; j < outW; ++j)
  40. {
  41. std::cout<<O[i*outW+j]<<" ";
  42. }
  43. std::cout<<std::endl;
  44. }
  45. }

(2)代码说明

按照正常计算输出规模公式计算的话,应该输出是4*4*1。但是考虑到边界墙问题。

if 语句块用来检查当前卷积核的 mn 索引是否在输入特征图的边界内。这里考虑了填充(padding)的情况:

  • (n+j*strideX - padX) 计算了卷积核的左边界在输入特征图上的索引。
  • (m+i*strideY - padY) 计算了卷积核的上边界在输入特征图上的索引。
  • 条件 > -1 确保索引不会是负数。
  • 条件 <= width 和 <= height 确保索引不会超出输入特征图的边界。

5、输入4*4*2,卷积核3*3*2,步长1*1,padding为1*1,输出2*2*1

其中依旧考虑了边界墙问题。

(1)实现代码

  1. void demo4()
  2. {
  3. 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};
  4. float K[] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9};
  5. float O[] = {0,0,0,0};
  6. int padX = 1;
  7. int padY = 1;
  8. int dilationX = 1;
  9. int dilationY = 1;
  10. int strideX = 2;
  11. int strideY = 2;
  12. int width = 4;
  13. int height = 4;
  14. int kSizeX = 3;
  15. int kSizeY = 3;
  16. int channel = 2;
  17. int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
  18. int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;
  19. for (int c = 0; c < channel; ++c)
  20. {
  21. for(int i=0;i<outH;i++)
  22. {
  23. for(int j=0;j<outW;j++)
  24. {
  25. for(int m=0;m<kSizeY;m++)
  26. {
  27. for(int n=0;n<kSizeX;n++)
  28. {
  29. float fVal = 0;
  30. if((n+j*strideX-padX)>-1&&(m+i*strideY-padY>-1)&&(n+j*strideX-padX)<=width&&(m+i*strideY-padY>-1)<=height)
  31. {
  32. fVal = F[c*width*height + (m+i*strideY-padX)*width+(n+j*strideX-padY)];
  33. }
  34. O[i*outW+j]+=K[c*kSizeX*kSizeY+m*kSizeX+n]*fVal;
  35. }
  36. }
  37. }
  38. }
  39. }
  40. for (int i = 0; i < outH; ++i)
  41. {
  42. for (int j = 0; j < outW; ++j)
  43. {
  44. std::cout<<O[i*outW+j]<<" ";
  45. }
  46. std::cout<<std::endl;
  47. }
  48. }

6、输入4*4*1,卷积核3*3*1,卷积核个数2,步长1*1,padding为1*1,输出2*2*1

(1)实现代码

  1. void demo5()
  2. {
  3. 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};
  4. float K[] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,
  5. 1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9
  6. };
  7. float O[] = {0,0,0,0,0,0,0,0};
  8. int padX = 1;
  9. int padY = 1;
  10. int dilationX = 1;
  11. int dilationY = 1;
  12. int strideX = 2;
  13. int strideY = 2;
  14. int width = 4;
  15. int height = 4;
  16. int kSizeX = 3;
  17. int kSizeY = 3;
  18. int channel = 2;
  19. int filters = 2;
  20. int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
  21. int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;
  22. int outC = filters;
  23. for (int oc = 0; oc < outC; ++oc)
  24. {
  25. for (int c = 0; c < channel; ++c)
  26. {
  27. for(int i=0;i<outH;i++)
  28. {
  29. for(int j=0;j<outW;j++)
  30. {
  31. for(int m=0;m<kSizeY;m++)
  32. {
  33. for(int n=0;n<kSizeX;n++)
  34. {
  35. float fVal = 0;
  36. if((n+j*strideX-padX)>-1&&(m+i*strideY-padY>-1)&&(n+j*strideX-padX)<=width&&(m+i*strideY-padY>-1)<=height)
  37. {
  38. fVal = F[c*width*height + (m+i*strideY-padX)*width+(n+j*strideX-padY)];
  39. }
  40. O[oc*outH*outW+i*outW+j]+=K[oc*outC*kSizeX*kSizeY+c*kSizeX*kSizeY+m*kSizeX+n]*fVal;
  41. }
  42. }
  43. }
  44. }
  45. }
  46. }
  47. for (int oc = 0; oc < outC; ++oc)
  48. {
  49. for (int i = 0; i < outH; ++i)
  50. {
  51. for (int j = 0; j < outW; ++j)
  52. {
  53. std::cout<<O[oc*outH*outW+i*outW+j]<<" ";
  54. }
  55. std::cout<<std::endl;
  56. }
  57. std::cout<<std::endl<<std::endl;
  58. }
  59. }

(2)代码说明

  1. 六个嵌套循环实现了卷积操作

    • 最外层循环遍历输出特征图的每个通道。
    • 第二个循环遍历输入特征图的每个通道。
    • 第三个和第四个循环遍历输出特征图的每个元素。
    • 最内层两个循环遍历每个卷积核的元素。
  2. 在最内层循环中,首先检查当前卷积核的索引是否在输入特征图的边界内,如果是,则从输入特征图中取出相应的元素 fVal

  3. 然后,将 fVal 与对应卷积核的元素相乘,并将结果累加到输出特征图的对应位置。

7、输入4*4*2,卷积核3*3*2,步长1*1,padding为1*1,膨胀系数2,输出2*2*2

  1. void demo6()
  2. {
  3. 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};
  4. float K[] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,
  5. 1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9
  6. };
  7. float O[] = {0,0,0,0,0,0,0,0};
  8. int padX = 1;
  9. int padY = 1;
  10. int dilationX = 2;
  11. int dilationY = 2;
  12. int strideX = 1;
  13. int strideY = 1;
  14. int width = 4;
  15. int height = 4;
  16. int kSizeX = 3;
  17. int kSizeY = 3;
  18. int channel = 2;
  19. int filters = 2;
  20. int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
  21. int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;
  22. int outC = filters;
  23. for (int oc = 0; oc < outC; ++oc)
  24. {
  25. for (int c = 0; c < channel; ++c)
  26. {
  27. for(int i=0;i<outH;i++)
  28. {
  29. for(int j=0;j<outW;j++)
  30. {
  31. for(int m=0;m<kSizeY;m++)
  32. {
  33. for(int n=0;n<kSizeX;n++)
  34. {
  35. float fVal = 0;
  36. if( ((n+j*strideX)*dilationX-padX)>-1 && ((m+i*strideY)*dilationY-padY)>-1&&
  37. ((n+j*strideX)*dilationX-padX)<=width && ((m+i*strideY)*dilationY-padY>-1)<=height)
  38. {
  39. fVal = F[c*width*height + ((m+i*strideY)*dilationX-padX)*width+((n+j*strideX)*dilationY-padY)];
  40. }
  41. O[oc*outH*outW+i*outW+j]+=K[oc*outC*kSizeX*kSizeY+c*kSizeX*kSizeY+m*kSizeX+n]*fVal;
  42. }
  43. }
  44. }
  45. }
  46. }
  47. }
  48. for (int oc = 0; oc < outC; ++oc)
  49. {
  50. for (int i = 0; i < outH; ++i)
  51. {
  52. for (int j = 0; j < outW; ++j)
  53. {
  54. std::cout<<O[oc*outH*outW+i*outW+j]<<" ";
  55. }
  56. std::cout<<std::endl;
  57. }
  58. std::cout<<std::endl;
  59. }
  60. }

三、可执行的卷积操作代码

  1. #include <iostream>
  2. void demo0() //输入3*3*1,卷积核3*3*1,输出为1*1*1
  3. {
  4. float F[] = {1,2,3,4,5,6,7,8,9};
  5. float K[] = {1,2,3,4,5,6,7,8,9};
  6. float O = 0;
  7. int width = 3;
  8. int height = 3;
  9. int kSizeX = 3;
  10. int kSizeY = 3;
  11. for(int m=0;m<kSizeY;m++)
  12. {
  13. for(int n=0;n<kSizeX;n++)
  14. {
  15. O+=K[m*kSizeX+n]*F[m*width+n];
  16. }
  17. }
  18. std::cout<<O<<" ";
  19. }
  20. void demo1() //输入4*4*1,卷积核3*3*1,步长为1*1,no padding,输出为1*1*1
  21. {
  22. //计算公式
  23. // (height + 2 * paddingY - (dilationY * (kSizeY - 1) + 1)) / strideY + 1;
  24. // (width + 2 * paddingX - (dilationX * (kSizeX - 1) + 1)) / strideX + 1;
  25. float F[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
  26. float K[] = {1,2,3,4,5,6,7,8,9};
  27. float O[] = {0,0,0,0};
  28. int padX = 0;
  29. int padY = 0;
  30. int dilationX = 1;
  31. int dilationY = 1;
  32. int strideX = 1;
  33. int strideY = 1;
  34. int width = 4;
  35. int height = 4;
  36. int kSizeX = 3;
  37. int kSizeY = 3;
  38. int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
  39. int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;
  40. for(int i=0;i<outH;i++)
  41. {
  42. for(int j=0;j<outW;j++)
  43. {
  44. for(int m=0;m<kSizeY;m++)
  45. {
  46. for(int n=0;n<kSizeX;n++)
  47. {
  48. O[i*outW+j]+=K[m*kSizeX+n]*F[(m+i)*width+(n+j)];
  49. /* int inputIndex = (i * strideY + m) * width + (j * strideX + n);
  50. O[i * outW + j] += K[m * kSizeX + n] * F[inputIndex]; */
  51. }
  52. }
  53. }
  54. }
  55. for (int i = 0; i < outH; ++i)
  56. {
  57. for (int j = 0; j < outW; ++j)
  58. {
  59. std::cout<<O[i*outW+j]<<" ";
  60. }
  61. std::cout<<std::endl;
  62. }
  63. }
  64. void demo2()
  65. {
  66. float F[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
  67. float K[] = {1,2,3,4};
  68. //float K[] = {1,2,3,4,5,6,7,8,9};
  69. float O[] = {0,0,0,0};
  70. int padX = 0;
  71. int padY = 0;
  72. int dilationX = 1;
  73. int dilationY = 1;
  74. int strideX = 2;
  75. int strideY = 2;
  76. int width = 4;
  77. int height = 4;
  78. int kSizeX = 2;
  79. int kSizeY = 2;
  80. int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
  81. int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;
  82. for(int i=0;i<outH;i++)
  83. {
  84. for(int j=0;j<outW;j++)
  85. {
  86. for(int m=0;m<kSizeY;m++)
  87. {
  88. for(int n=0;n<kSizeX;n++)
  89. {
  90. O[i*outW+j]+=K[m*kSizeX+n]*F[(m+i*strideY)*width+(n+j*strideX)];
  91. }
  92. }
  93. }
  94. }
  95. for (int i = 0; i < outH; ++i)
  96. {
  97. for (int j = 0; j < outW; ++j)
  98. {
  99. std::cout<<O[i*outW+j]<<" ";
  100. }
  101. std::cout<<std::endl;
  102. }
  103. }
  104. void demo3()
  105. {
  106. float F[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
  107. //float K[] = {1,2,3,4};
  108. float K[] = {1,2,3,4,5,6,7,8,9};
  109. float O[] = {0,0,0,0};
  110. int padX = 1;
  111. int padY = 1;
  112. int dilationX = 1;
  113. int dilationY = 1;
  114. int strideX = 2;
  115. int strideY = 2;
  116. int width = 4;
  117. int height = 4;
  118. int kSizeX = 3;
  119. int kSizeY = 3;
  120. int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
  121. int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;
  122. for(int i=0;i<outH;i++)
  123. {
  124. for(int j=0;j<outW;j++)
  125. {
  126. for(int m=0;m<kSizeY;m++)
  127. {
  128. for(int n=0;n<kSizeX;n++)
  129. {
  130. float fVal = 0;
  131. //考虑边界强情况
  132. if((n+j*strideX-padX)>-1&&(m+i*strideY-padY>-1)&&(n+j*strideX-padX)<=width&&(m+i*strideY-padY>-1)<=height)
  133. {
  134. fVal = F[(m+i*strideY-padX)*width+(n+j*strideX-padY)];
  135. }
  136. O[i*outW+j]+=K[m*kSizeX+n]*fVal;
  137. }
  138. }
  139. }
  140. }
  141. for (int i = 0; i < outH; ++i)
  142. {
  143. for (int j = 0; j < outW; ++j)
  144. {
  145. std::cout<<O[i*outW+j]<<" ";
  146. }
  147. std::cout<<std::endl;
  148. }
  149. }
  150. void demo4()
  151. {
  152. 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};
  153. //float K[] = {1,2,3,4};
  154. float K[] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9};
  155. float O[] = {0,0,0,0};
  156. int padX = 1;
  157. int padY = 1;
  158. int dilationX = 1;
  159. int dilationY = 1;
  160. int strideX = 2;
  161. int strideY = 2;
  162. int width = 4;
  163. int height = 4;
  164. int kSizeX = 3;
  165. int kSizeY = 3;
  166. int channel = 2;
  167. int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
  168. int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;
  169. for (int c = 0; c < channel; ++c)
  170. {
  171. for(int i=0;i<outH;i++)
  172. {
  173. for(int j=0;j<outW;j++)
  174. {
  175. for(int m=0;m<kSizeY;m++)
  176. {
  177. for(int n=0;n<kSizeX;n++)
  178. {
  179. float fVal = 0;
  180. if((n+j*strideX-padX)>-1&&(m+i*strideY-padY>-1)&&(n+j*strideX-padX)<=width&&(m+i*strideY-padY>-1)<=height)
  181. {
  182. fVal = F[c*width*height + (m+i*strideY-padX)*width+(n+j*strideX-padY)];
  183. }
  184. O[i*outW+j]+=K[c*kSizeX*kSizeY+m*kSizeX+n]*fVal;
  185. }
  186. }
  187. }
  188. }
  189. }
  190. for (int i = 0; i < outH; ++i)
  191. {
  192. for (int j = 0; j < outW; ++j)
  193. {
  194. std::cout<<O[i*outW+j]<<" ";
  195. }
  196. std::cout<<std::endl;
  197. }
  198. }
  199. void demo5()
  200. {
  201. 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};
  202. //float K[] = {1,2,3,4};
  203. float K[] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,
  204. 1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9
  205. };
  206. float O[] = {0,0,0,0,0,0,0,0};
  207. int padX = 1;
  208. int padY = 1;
  209. int dilationX = 1;
  210. int dilationY = 1;
  211. int strideX = 2;
  212. int strideY = 2;
  213. int width = 4;
  214. int height = 4;
  215. int kSizeX = 3;
  216. int kSizeY = 3;
  217. int channel = 2;
  218. int filters = 2;
  219. int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
  220. int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;
  221. int outC = filters;
  222. for (int oc = 0; oc < outC; ++oc)
  223. {
  224. for (int c = 0; c < channel; ++c)
  225. {
  226. for(int i=0;i<outH;i++)
  227. {
  228. for(int j=0;j<outW;j++)
  229. {
  230. for(int m=0;m<kSizeY;m++)
  231. {
  232. for(int n=0;n<kSizeX;n++)
  233. {
  234. float fVal = 0;
  235. if((n+j*strideX-padX)>-1&&(m+i*strideY-padY>-1)&&(n+j*strideX-padX)<=width&&(m+i*strideY-padY>-1)<=height)
  236. {
  237. fVal = F[c*width*height + (m+i*strideY-padX)*width+(n+j*strideX-padY)];
  238. }
  239. O[oc*outH*outW+i*outW+j]+=K[oc*channel*kSizeX*kSizeY+c*kSizeX*kSizeY+m*kSizeX+n]*fVal;
  240. }
  241. }
  242. }
  243. }
  244. }
  245. }
  246. for (int oc = 0; oc < outC; ++oc)
  247. {
  248. for (int i = 0; i < outH; ++i)
  249. {
  250. for (int j = 0; j < outW; ++j)
  251. {
  252. std::cout<<O[oc*outH*outW+i*outW+j]<<" ";
  253. }
  254. std::cout<<std::endl;
  255. }
  256. std::cout<<std::endl<<std::endl;
  257. }
  258. }
  259. void demo6()
  260. {
  261. // (height + 2 * paddingY - (dilationY * (kSizeY - 1) + 1)) / strideY + 1;
  262. // (width + 2 * paddingX - (dilationX * (kSizeX - 1) + 1)) / strideX + 1;
  263. 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};
  264. //float K[] = {1,2,3,4};
  265. float K[] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,
  266. 1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9
  267. };
  268. float O[] = {0,0,0,0,0,0,0,0};
  269. int padX = 1;
  270. int padY = 1;
  271. int dilationX = 2;
  272. int dilationY = 2;
  273. int strideX = 1;
  274. int strideY = 1;
  275. int width = 4;
  276. int height = 4;
  277. int kSizeX = 3;
  278. int kSizeY = 3;
  279. int channel = 2;
  280. int filters = 2;
  281. int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
  282. int outW = (width+2*padX-(dilationX*(kSizeX-1)+1)) / strideX + 1;
  283. int outC = filters;
  284. for (int oc = 0; oc < outC; ++oc)
  285. {
  286. for (int c = 0; c < channel; ++c)
  287. {
  288. for(int i=0;i<outH;i++)
  289. {
  290. for(int j=0;j<outW;j++)
  291. {
  292. for(int m=0;m<kSizeY;m++)
  293. {
  294. for(int n=0;n<kSizeX;n++)
  295. {
  296. float fVal = 0;
  297. if( ((n+j*strideX)*dilationX-padX)>-1 && ((m+i*strideY)*dilationY-padY)>-1&&
  298. ((n+j*strideX)*dilationX-padX)<=width && ((m+i*strideY)*dilationY-padY>-1)<=height)
  299. {
  300. fVal = F[c*width*height + ((m+i*strideY)*dilationX-padX)*width+((n+j*strideX)*dilationY-padY)];
  301. }
  302. O[oc*outH*outW+i*outW+j]+=K[oc*channel*kSizeX*kSizeY+c*kSizeX*kSizeY+m*kSizeX+n]*fVal;
  303. }
  304. }
  305. }
  306. }
  307. }
  308. }
  309. for (int oc = 0; oc < outC; ++oc)
  310. {
  311. for (int i = 0; i < outH; ++i)
  312. {
  313. for (int j = 0; j < outW; ++j)
  314. {
  315. std::cout<<O[oc*outH*outW+i*outW+j]<<" ";
  316. }
  317. std::cout<<std::endl;
  318. }
  319. std::cout<<std::endl;
  320. }
  321. }
  322. int main(int argc, char *argv[])
  323. {
  324. //demo0();
  325. demo1();
  326. //demo2();
  327. //demo3();
  328. //demo4();
  329. //demo5();
  330. //demo6();
  331. }

(1)将上述文件为my_code.cc源文件。

如果要使用不同类型的demo,可以直接在main函数中调用。

(2)然后在终端运行:

g++ -o my_code my_code.cc

此时已编译出可执行的二进制my_code。

(3)执行my_code文件

./my_code

最后说明:其实我个人觉得这种方式使用起来除了简单易懂,但在实际操作中并不可取。主要是所有的变量值都需要在demo()函数中手工赋值,耦合性和实用性较差。但是目前也没有更好的实测,等过阵子我的程序写完后,再和大家分享。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/726616
推荐阅读
相关标签
  

闽ICP备14008679号