python代码实现卷积示意图快速制作

1. 问题描述

绘制卷积示意图时,通常需要计算卷积结果。单通道的卷积计算过程如下图所示,卷积核在特征图上不断滑动,卷积核与其覆盖的特征图区域逐点相乘并求和。当特征图和卷积核尺寸很小时,手动计算还是可行的。
3x3 的特征图和 2x2 的卷积核计算卷积,其实只需要进行 4 次乘积加和的操作。
在这里插入图片描述
那么,如果想要计算尺寸大一点的特征图和卷积核的结果时,还是这么简单吗?
5x5 的特征图和 3x3 的卷积核做卷积,在不填充(no padding)的时候,需要计算 9 次乘积加和操作。
每次需要对两个 3x3 的矩阵进行乘积加和操作,需要进行 9 次乘法和 8 次加法。
所以对 5x5 的特征图和 3x3 的卷积核做卷积,需要进行 (9 + 8)*9 = 153 次数学运算,此外运算过程中还要不断移动卷积滑窗的位置,在新的特征图区域上计算卷积。

小伙伴们,你凌乱了吗?手动计算好像有点难哈。
现在要是有个程序能自动计算就好了。所以,我写了一段简单的代码计算卷积。
python代码实现卷积示意图快速制作_第1张图片

2. 解决办法

用 python 写了一个简单的函数conv,实现卷积操作。
调用conv函数需要给定特征图矩阵卷积核矩阵。这两个矩阵大家可以根据需要任意设定。

def conv(image,filter):
    result = []
    for i in range(image.shape[0]-(filter.shape[0]-1)):
        for j in range(image.shape[1]-(filter.shape[1]-1)):
            value = np.sum(image[i:i+filter.shape[0],j:j+filter.shape[1]]*filter)
            result.append(value)
    result = np.array(result)
    res = result.reshape(image.shape[0]-filter.shape[0]+1,image.shape[1]-filter.shape[1]+1)
    return res

下面给出一个使用conv函数计算卷积的例子

python代码实现卷积示意图快速制作_第2张图片

  1. 准备好要计算的特征图矩阵
import numpy as np
featuremap = np.array([[1,3,4,0,1],
         [6,6,0,1,2],
         [1,2,4,2,0],
         [3,4,3,0,1],
         [2,0,1,5,2]])
  1. 准备好卷积核矩阵
filter = np.array([[2,5,0],
                 [0,1,3],
                 [1,0,2]])
  1. 调用conv函数计算卷积
conv(featuremap,filter)

计算结果如下,将它填到输出卷积图上就大功告成了。

array([[32, 35, 19],
       [65, 26, 12],
       [29, 37, 26]])

3. 进阶升级

卷积操作会导致特征图的尺寸变小,损失特征图的边缘信息。所以,进行卷积前通常会进行补零操作(padding)。
那该怎么计算呢?
输入特征图矩阵时,加一圈零就行了呗?

[[1,3,4,0,1],
 [6,6,0,1,2],
 [1,2,4,2,0],
 [3,4,3,0,1],
 [2,0,1,5,2]]

手动改成

[[0,0,0,0,0,0,0],
 [0,1,3,4,0,1,0],
 [0,6,6,0,1,2,0],
 [0,1,2,4,2,0,0],
 [0,3,4,3,0,1,0],
 [0,2,0,1,5,2,0],
 [0,0,0,0,0,0,0]]

好像不太智能的样子~~
python代码实现卷积示意图快速制作_第3张图片
所以,对conv函数进行了改进,得到了calculate_conv函数。
调用calculate_conv函数,需要指定卷积模式mode,'same’模式下输入特征图和输出特征图大小相同,进行了补零操作;
'valid’模式下输出特征图变小,不进行补零操作,和之前的conv函数计算结果相同。
此外,calculate_conv函数还可以指定卷积核的滑动步长stride,stride的默认值为1。

def calculate_conv(image,filter,mode,stride=1):
    result = []
    if mode=='same':
        pad_width = (filter.shape[0])//2
        image = np.pad(image,pad_width=pad_width,mode='constant',constant_values = 0)
    elif mode=='valid':
        image = image
        pad_width = 0
    target_hsize = (image.shape[0]-filter.shape[0])//stride + 1
    target_wsize = (image.shape[1]-filter.shape[1])//stride + 1
    for i in range(target_hsize):
        for j in range(target_wsize):
            value = np.sum(image[i:i+filter.shape[0],j:j+filter.shape[1]]*filter)
            result.append(value)
    result = np.array(result)
    res = result.reshape(target_hsize,target_wsize)
    return res

下面给出一个使用calculate_conv函数计算卷积的例子

  1. 使用之前的特征图矩阵 featuremap 和卷积核矩阵 filter 。
import numpy as np

featuremap = np.array([[1,3,4,0,1],
         [6,6,0,1,2],
         [1,2,4,2,0],
         [3,4,3,0,1],
         [2,0,1,5,2]])
         
filter = np.array([[2,5,0],
                 [0,1,3],
                 [1,0,2]])
  1. 调用calculate_conv函数

'same’模式下,调用函数

calculate_conv(featuremap,filter,'same')

卷积计算结果为

array([[22, 21, 12,  7,  2],
       [33, 32, 35, 19,  9],
       [45, 65, 26, 12, 12],
       [20, 29, 37, 26, 10],
       [17, 29, 39, 17,  7]])

'valid’模式下,调用函数

calculate_conv(featuremap,filter,'valid')

卷积计算结果为

array([[32, 35, 19],
       [65, 26, 12],
       [29, 37, 26]])

将卷积计算结果写到输出特征图上,卷积示意图的绘制就大功告成了。
完结撒花~~
python代码实现卷积示意图快速制作_第4张图片

你可能感兴趣的:(问题解决,python,深度学习,cnn,卷积示意图绘制,卷积计算)