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、符号约定

F[] 为输入;
width 为输入的宽;
height 为输入的高;
channel 为输入的通道;

K[] 为 kernel;
kSizeX 为 kernel 的宽;
kSizeY 为 kernel 的高;
filters 为 kernel 的个数;
padX 为水平方向的填充;
padY 为垂直方向的填充;
strideX 为水平方向的步长;
strideY 为垂直方向的步长;


O[] 为输出;
outW 为输出的宽;
outH 为输出的高;
outChannel 为输出的通道;

2、输出大小计算公式

int outH = (height+2*padY-(dilationY*(kSizeY-1)+1)) / strideY + 1;
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()函数可直接调用

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. 前面都是定义输入矩阵和卷积核,同时定义一个浮点数变量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)实现代码

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;
    }
}

(2)代码说明

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

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

(1)实现代码

void demo2()
{
    // (height + 2 * paddingY - (dilationY * (kSizeY - 1) + 1)) / strideY + 1;
    // (width + 2 * paddingX - (dilationX * (kSizeX - 1) + 1)) / strideX + 1;

    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 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  = 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、输入4*4*1,卷积核3*3*1,步长1*1,padding为1*1,输出2*2*1

(1)实现代码

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;
    }
}

(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)实现代码

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、输入4*4*1,卷积核3*3*1,卷积核个数2,步长1*1,padding为1*1,输出2*2*1

(1)实现代码

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;
    }
}

(2)代码说明

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

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

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

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

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;
    }
}

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

#include <iostream>

void demo0()    //输入3*3*1,卷积核3*3*1,输出为1*1*1
{
    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<<" ";
}

void demo1()    //输入4*4*1,卷积核3*3*1,步长为1*1,no padding,输出为1*1*1
{
    //计算公式
    // (height + 2 * paddingY - (dilationY * (kSizeY - 1) + 1)) / strideY + 1;
    // (width + 2 * paddingX - (dilationX * (kSizeX - 1) + 1)) / strideX + 1;

    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)];
                    /* int inputIndex = (i * strideY + m) * width + (j * strideX + n);
                    O[i * outW + j] += K[m * kSizeX + n] * F[inputIndex]; */
                }
            }
        }
    }

    for (int i = 0; i < outH; ++i)
    {
        for (int j = 0; j < outW; ++j)
        {
            std::cout<<O[i*outW+j]<<" ";
        }
        std::cout<<std::endl;
    }
}

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 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  = 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;
    }
}

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};
    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;
    }
}

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};
    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;
    }
}

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};
    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*channel*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;
    }
}

void demo6()
{
    // (height + 2 * paddingY - (dilationY * (kSizeY - 1) + 1)) / strideY + 1;
    // (width + 2 * paddingX - (dilationX * (kSizeX - 1) + 1)) / strideX + 1;

    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};
    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*channel*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;
    }
}


int main(int argc, char *argv[])
{
    //demo0();
    demo1();
    //demo2();
    //demo3();
    //demo4();
    //demo5();
    //demo6();
}

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

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

(2)然后在终端运行:

g++ -o my_code my_code.cc

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

(3)执行my_code文件

./my_code

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/712771.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

环保评A的意义与价值

环保评A&#xff0c;这个看似简单的称谓&#xff0c;背后却蕴藏着深厚的环保理念和实践标准。在当今社会&#xff0c;环保已经成为一项全球性的议题&#xff0c;各国都在努力推动绿色发展&#xff0c;实现可持续发展目标。那么&#xff0c;环保评A究竟是全国性的认证还是地方性…

Java SSTI服务端模版注入漏洞原理与利用

文章目录 前言Velocity基础语法基础示例命令执行 靶场实践漏洞代码漏洞验证检测工具 FreeMarker基础示例漏洞示例CMS案例 Thymeleaf基础示例漏洞示例安全方案 总结 前言 SSTI&#xff08;Server Side Template Injection&#xff09;全称服务端模板注入漏洞&#xff0c;在 Jav…

开放式耳机值得入手买吗?可以对比这几款开放式耳机看看

居家办公时&#xff0c;选择一款合适的耳机能够有效地提高工作效率。入耳式耳机虽然能够有效地隔绝外界噪音&#xff0c;但长时间佩戴会对耳朵造成负担&#xff0c;甚至引发耳道感染。而头戴式耳机虽然能够提供更好的音质&#xff0c;但体积较大&#xff0c;佩戴起来不够灵活。…

PyTorch -- Batch Normalization(BN) 快速实践

Batch Normalization 可以 改善梯度消失/爆炸问题&#xff1a;前面层的梯度经过多次传递后会变得非常小(大)&#xff0c;从而导致网络收敛速度慢(不收敛)&#xff0c;应用 BN 可缓解加速网络收敛&#xff1a;BN 使得每个神经元的输入分布更加稳定减少过拟合&#xff1a;BN 可减…

求导,积分

求导公式&#xff1a; 复合函数求导法则&#xff1a;两个函数导函数的乘积. 例如&#xff1a;f(x)2x1,f(x)2,g(x)x^24x4,g(x)2x4 那么复合函数&#xff1a; g(f(x))(2x1)^24(2x1)4 把&#xff08;2x1&#xff09;看做整体,则g2(2x1)4 然后再求&#xff08;2x1&#xff09;的导函…

LeetCode | 2879.显示前三行

在 pandas 中&#xff0c;可以使用 head() 方法来读取 DataFrame 的前几行数据。如果想读取指定数量的行&#xff0c;可以在 head() 方法中传入一个参数 n&#xff0c;读取前 n 行 import pandas as pddef selectFirstRows(employees: pd.DataFrame) -> pd.DataFrame:retur…

Dictionary 字典

文章目录 一、什么是字典1.1 字典的创建方式 一、什么是字典 字典&#xff1a; 用来存储数据&#xff0c;与列表和元组不一样的是&#xff0c;字典以键值对的形式对数据进行存储&#xff0c;也就是 key 和 value。相当于 Java 中的 Map。 注意&#xff1a; 1、 key 的值不可重…

C++进阶(一)

个人主页&#xff1a;PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 前言 本篇博客是讲解函数的重载以及引用的知识点的。 文章目录 前言 1.函数重载 1.1何为函数重载 1.2函数重载的作用 1.3函数重载的实现 2.引用 2.1何为引用 2.2定义引用 2.3引用特性 2.4常引用 2…

认识一些分布函数-Frechet分布及其应用

1. 何为Frechet分布 Frechet分布也称为极值分布(EVD)类型II,用于对数据集中的最大值进行建模。它是四种常用极值分布之一。另外三种是古贝尔分布、威布尔分布和广义极值分布(Gumbel Distribution, the Weibull Distribution and the Generalized Extreme Value Distributi…

34 Debian如何配置ELK群集

作者:网络傅老师 特别提示:未经作者允许,不得转载任何内容。违者必究! Debian如何配置ELK群集 《傅老师Debian知识库系列之34》——原创 ==前言== 傅老师Debian知识库特点: 1、拆解Debian实用技能; 2、所有操作在VMware虚拟机实测完成; 3、致力于最终形成Debian知识手…

LVS-DR模式详解:提升网站性能的最佳解决方案

LVS-DR模式原理 用户请求到达Director Server&#xff1a; 用户请求到达Director Server&#xff08;负载均衡服务器&#xff09;&#xff0c;数据包首先到达内核空间的PREROUTING链。数据包源IP&#xff1a;CIP&#xff0c;目标IP&#xff1a;VIP&#xff0c;源MAC&#xff1a…

【内存管理之C语言数组】

1.栈空间上的C数组 糟糕的可用性&#xff0c;但是你将在遗留代码中见到它们 相同类型的对象的内存块 大小必须是常量表达式 第一个元素索引为0 2.指针和C数组 更奇怪的是&#xff1a;数组标识符退化为指向第一个元素的指针 3.访问数组 4.堆空间上的C数组 相同类型的对象的内…

数据库开发——并发控制(第十一章)

文章目录 前言并发执行例题一、封锁二、封锁协议三、可串行调度四、总结 学习目标&#xff1a;重点为并发控制的基本概念及几个基本协议 前言 数据库管理系统必须提供并发控制机制&#xff0c;保证事务的隔离性和一致性 并发执行例题 一、封锁 排他锁称为写锁&#xff0c;共…

智能化状态管理:自动状态流转处理模块

目录 基本背景介绍 具体实现 基本数据准备 基本数据表 状态转换常量 状态转换注解 任务处理模版 各任务实现逻辑 开启比对任务进行处理 降噪字段处理任务处理 开启业务数据比对处理 业务数据比对处理 开始核对数据生成最终报告处理 核对数据生成最终报告处理 状…

小红书教程简化版,从0开始走向专业,小红书-主理人培养计划 (13节)

课程目录 1-小红书分析与拆解.mp4 2-小红书电商玩法.mp4 3-小红书基础信息设置10_1.mp4 4-小红书如何开店&#xff1f;.mp4 5-小红书店铺设置&#xff08;1&#xff09;.mp4 5-小红书店铺设置.mp4 6-小红书笔记制作与产品发布.mp4 7-小红书运营的文案与标题.mp4 8-小红…

Spring Boot 自定义Starter

自定义starter 创建pom项目 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.ap…

MySQL的三种重要的日志

日志 Mysql有三大日志系统 Undo Log&#xff08;回滚日志&#xff09;&#xff1a;记录修改前的数据&#xff0c;用于事务回滚和 MVCC&#xff08;多版本并发控制&#xff09;。 Redo Log&#xff08;重做日志&#xff09;&#xff1a;记录数据变更&#xff0c;用于崩溃恢复&…

XAMPP PHP-CGI 远程代码执行漏洞(CVE-2024-4577)

漏洞概述&#xff1a; PHP 是一种被广泛应用的开放源代码的多用途脚本语言&#xff0c;PHP-CGI 是 PHP 自带的 FastCGI 管理器。是一个实现了 CGI 协议的程序&#xff0c;用来解释 PHP 脚本的程序&#xff0c;2024 年 6 月 7 日&#xff0c;推特安全上 orange 公开了其漏洞细节…

基于Wireshark实现对FTP的抓包分析

基于Wireshark实现对FTP的抓包分析 前言一、虚拟机Win10环境配置二、FileZilla客户端的安装配置下载FileZilla客户端安装FileZilla 三、FileZilla Server安装下载FileZilla Server安装 四、实现对FTP的抓包前置工作实现抓包完成抓包 前言 推荐一个网站给想要了解或者学习人工智…

MySQL学习笔记-进阶篇-SQL优化

SQL优化 插入数据 insert优化 1&#xff09;批量插入 insert into tb_user values(1,Tom),(2,Cat),(3,Jerry); 2&#xff09;手动提交事务 mysql 默认是自动提交事务&#xff0c;这样会导致频繁的开启和提交事务&#xff0c;影响性能 start transaction insert into tb_us…