卷积运算-矩阵方式

介绍

在caffe(开源深度学习框架)中,做 卷积运算 时,不是 像下图那样,设计一个滑动的卷积核,进行运算。

为了 在GPU中可以最大限度的加速,做法如下:
是将 图片矩阵 转成 n行1列的矩阵,将 卷积核 扩展成 m行n列的矩阵,
让两个矩阵 做矩阵乘法 运算:

m行n列的矩阵    x   n行1列的矩阵 = m行1列的矩阵

然后根据 卷积结果尺寸计算公式,算出卷积结果尺寸为i行j列,

将m行1列的矩阵转成i行j列,这就是最后卷积结果

新图像矩阵

假设有一个图片的numpy矩阵,大小是4x4,如下:

将该矩阵转成 n行1列,即16行1列的矩阵

新卷积核矩阵

新卷积核矩阵的 大小

假设卷积核大小是3x3,值如下:

为了让卷积核能够与上图 16行1列的新图像矩阵,进行 矩阵乘法 运算,必须将卷积核扩展成16列,即:新卷积核矩阵为 m行16列(行数暂未知),计算式子如下:

m行16列矩阵 x 16行1列矩阵 = m行1列

结果是 m行1列(行数暂未知),将m行1列转成i行j列后就是 卷积结果,所以m的值,一定等于卷积结果的元素数量。

上文给出卷积结果大小的计算公式:(N1-N2+2P)/S+1

这里为了易于理解,不填充,步长为1,所以卷积结果的大小为:(3-2+2*0)/1+1=2,即:卷积结果大小为:(2,2),共有4个元素

所以 m值为4,也就是 新卷积核大小是:4行16列

新卷积核矩阵的元素

第1次 卷积运算(上面矩阵是卷积核,下面矩阵是图像),如下图:

所以新卷积核矩阵的 第1行值:

1  2  3  0  4  5  6  0  7  8  9  0  0  0  0  0

第2次 卷积运算(上面矩阵是卷积核,下面矩阵是图像),如下图:

所以新卷积核矩阵的 第2行值:

0  1  2  3  0  4  5  6  0  7  8  9  0  0  0  0

第3次 卷积运算(上面矩阵是卷积核,下面矩阵是图像),如下图:

所以新卷积核矩阵的 第3行值:

0  0  0  0  1  2  3  0  4  5  6  0  7  8  9  0

第4次 卷积运算(上面矩阵是卷积核,下面矩阵是图像),如下图:

所以新卷积核矩阵的 第4行值:

0  0  0  0  0  1  2  3  0  4  5  6  0  7  8  9

新卷积核矩阵如下:

总结成公式为:

矩阵乘法运算

结果大小为:4行1列,在转成 2行2列,就是卷积结果

代码

按照上面理论实现的 卷积代码,经测试,图片尺寸稍微大些的,占用内存就会极大,假设图像尺寸是500 x 500的,卷积核尺寸是3 x 3,那么新卷积核矩阵的列就是:500 x 500=250000,行数是:(500-3+2*0)/1+1=498,那么新卷积核矩阵是:498行250000列,使用这种方式占用内存很大

# 卷积运算,
def run_conv2(data,kernel,step=1):
    """
    data:要计算的图像数据
    kernel:卷积核值
    step:步长
    """
    data_w=data.shape[1]
    data_h=data.shape[0]
#     卷积核大小
    kernel_w=kernel.shape[1]
    kernel_h=kernel.shape[0]
#     结果大小
    result_w=(data_w-kernel_w)//step+1
    result_h=(data_h-kernel_h)//step+1
    result_count=result_w*result_h

    new_data=data.reshape(-1,1)
    new_data_rows=new_data.shape[0]

    new_kernel=np.zeros((result_count,new_data_rows))


    zero_num=0
    for row_index in range(result_count):
        if row_index%result_w==0:
            zero_num=(row_index//result_w)*data_w
        else:
            zero_num+=1


        for kernel_row_index in range(kernel_w):
            start_index=zero_num+kernel_row_index*data_w
            new_kernel[row_index,start_index:start_index+kernel_w]=kernel[kernel_row_index]

    return np.matmul(new_kernel,new_data).reshape(result_h,result_w)