绘制卷积示意图时,通常需要计算卷积结果。单通道的卷积计算过程如下图所示,卷积核在特征图上不断滑动,卷积核与其覆盖的特征图区域逐点相乘并求和。当特征图和卷积核尺寸很小时,手动计算还是可行的。
3x3 的特征图和 2x2 的卷积核计算卷积,其实只需要进行 4 次乘积加和的操作。
那么,如果想要计算尺寸大一点的特征图和卷积核的结果时,还是这么简单吗?
5x5 的特征图和 3x3 的卷积核做卷积,在不填充(no padding)的时候,需要计算 9 次乘积加和操作。
每次需要对两个 3x3 的矩阵进行乘积加和操作,需要进行 9 次乘法和 8 次加法。
所以对 5x5 的特征图和 3x3 的卷积核做卷积,需要进行 (9 + 8)*9 = 153 次数学运算,此外运算过程中还要不断移动卷积滑窗的位置,在新的特征图区域上计算卷积。
小伙伴们,你凌乱了吗?手动计算好像有点难哈。
现在要是有个程序能自动计算就好了。所以,我写了一段简单的代码计算卷积。
用 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
函数计算卷积的例子
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]])
conv
函数计算卷积conv(featuremap,filter)
计算结果如下,将它填到输出卷积图上就大功告成了。
array([[32, 35, 19],
[65, 26, 12],
[29, 37, 26]])
卷积操作会导致特征图的尺寸变小,损失特征图的边缘信息。所以,进行卷积前通常会进行补零操作(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]]
好像不太智能的样子~~
所以,对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
函数计算卷积的例子
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]])
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]])