在上一篇我们了解了卷积的概念,并且使用numpy实现了卷积。另一篇介绍了如何在tensorflow框架中调用API进行卷积操作。今天再介绍一个实现卷积操作的方案,使用im2col实现卷积,实际在OpenCV源码中也可以看到im2col的算法,顺便提一下opencv也可以直接部署深度学习模型,调用方法可以参考这里。
im2col
im2col算法原理
im2col就是把图像转化为列向量,很多文章都有讲解,https://zhuanlan.zhihu.com/p/63974249 链接讲的比较详细。我按自己理解画了几个图,也方面大家理解
上图就是im2col的原理,把一个矩阵转化为一行。(实际应该是im2row)
如果看过numpy实现卷积的文章应该都知道卷积的过程,如果是二维图像,卷积核会在图像上滑动进行卷积,那在im2col实现卷积,怎么操作呢?
如果核大小是3*3,那每次卷积的图像就是依次为右边的四个矩阵了~
再把四个矩阵按图一的方式转化为一行,就是下图
同样,我们把卷积核也按im2col进行转化
总结下
一个6_3的图像,我们转换为了9_4的矩阵
一个3_3的卷积核,我们转换为了9_1的矩阵
两个矩阵相乘,是不是一个41的矩阵~
_如果是正常卷积呢?回忆下卷积过程,或者跑下代码,应该也是得到一个4_1的结果~(以VALID方式)
对比下:
标准卷积的计算过程:4次3_3矩阵和3_3矩阵相乘累加
im2col实现卷积的计算过程:9_4矩阵和1_9矩阵相乘
根据矩阵相乘的定义,两个结果是一致的,但im2col肯定是优化版的卷积过程~
通过上面几个图,大家应该就就了解了什么叫im2col,以及它如何实现卷积了。
下面简单直接~上代码
im2col实现卷积代码实现
对于基础知识reshape和transpose的加深理解这里
下面我们直接写im2col的代码,注释已经比较详细
代码语言:javascript复制def im2col(inputs,filter,padding="SAME",stride=1):
N, C, H ,W = inputs.shape
filter_size = filter.shape[2]
# default np.floor
filter_center = int(filter_size / 2.0)
filter_center_ceil = int(np.ceil(filter_size / 2.0))
# SAME模式,输入和输出大小一致,所以要在外面填充0
if padding == "SAME":
padding_inputs = np.zeros([N,C,H filter_center_ceil, W filter_center_ceil], np.float32)
padding_inputs[:,:,filter_center:-filter_center, filter_center:-filter_center] = inputs
inputs = padding_inputs
N ,C,H2, W2 = inputs.shape
print("N ,C,H2, W2",N ,C,H2, W2,"inputsn",inputs)
out_h = H
out_w = W
col = np.empty((N * H * W, filter_size * filter_size * C))
outsize = out_w * out_h
#每个样本outsize行,每行就是fs*fs*C按行顺序reshape,如果是N个样本就是N行
for y in range(out_h):
y_min = y * stride
y_max = y_min filter_size
y_start = y * out_w
for x in range(out_w):
x_min = x * stride
x_max = x_min filter_size
#取出kernel大小的数据块reshape为一行,N个样本reshape为N行
#这里::outsize就是间隔outsize放下一个样本的行
col[y_start x::outsize, :] = inputs[:,:,y_min:y_max, x_min:x_max].reshape(N, -1)
return col
同样把filter进行im2col操作
代码语言:javascript复制def trans_weight(filter):
# print("filtern",filter)
C_out, C_in, K, K = filter.shape
weight = filter.reshape(C_out,-1)
# print("weightn",weight)
return weight
然后进行卷积操作,为了和之前numpy实现的卷积以及Tensorflow进行卷积的结果对比,我们输入和之前一样:
代码语言:javascript复制inputs = np.zeros([1, 3, 9, 9])
for n in range(1):
for i in range(3):
for j in range(9):
for z in range(9):
inputs[n][i][j][z] = i j z
filter = np.zeros([2, 3, 3, 3])
for i in range(2):
for j in range(3):
for x in range(3):
for y in range(3):
filter[i][j][x][y] = i j x y
conv_out = conv_im2col(inputs, filter)
#[1,9,9,2]---[1,2,9,9]
conv_out =np.transpose(conv_out,(0,3,1,2))
print(conv_out)
大家可以自己动手跑下,卷积结果和之前我们使用numpy实现卷积结果是一致的~