python手写神经网络之im2col原理及实现

im2col就是img to colomn主要是把图像转成column,原因和用途也很清晰,CNN中数据是四维的,并且有滑动窗口的存在,如果用for循环,计算效率不敢看。

 

那么原理也很简单,展开、复制、向量化。

 

但是从示意图到实现,还是有一个地方比较绕,所以我一下也没想到实现,还是看着参考代码分析了一下才明白。

 

示意图:

其实示意图可以写两种,不带batch的,和带batch的,前者更简单,但是只算一个循序渐进的推导过程,无论如何最终都要用带batch的版本,所以我就直接带batch画展开图了。(前者的一个好处是图不复杂,方便再一次展开观察广播过程,比如把filter复制N次等操作,所以两种最好都画一下,因为广播操作交给numpy去做了,所以带batch的版本就隐藏广播细节了,不然没法画)

python手写神经网络之im2col原理及实现

其实宏观的我这样画应该很直观了,就是把图片按一个滑窗一个行来转换,batch反应在行数上,其实不增加理解的难度。

filter每个作为一列,最后行列直接矩阵乘法,每个行和每个列都有交集,对应地,每个具体的窗口数据和每个具体的filter都应该有交集。

 

 

这是具体实现的shape操作,col有6个维度,这里可能是稍微有那么点抽象的,但是核心思路就一样,实现图一的展开效果,具体怎么抽象,其实都只是实现细节,不过为了便于理解这个细节,我还是按照这个过程画了图四。

python手写神经网络之im2col原理及实现

 

 

 

图三:代码部分:

python手写神经网络之im2col原理及实现

for循环略微抽象!

抽象的点主要在哪?在于,7*7的输入,5*5的kernel,3*3的输出。不是以窗口视角通过3*3的循环提取了9次5*5的块。而是提取了25次3*3的块。

其实很好理解,其实就是抠出来了(5,5)份的(3,3)个对应位置的数值,仔细看y_max=y+stride*out_h和y:y_max:stride,这两个细节,证明每次的切块都不是一个原图中的实际(紧密形状)的切块,而是有跳跃的。

直觉:我想要3*3个5*5的切块,结果,得到5*5个3*3的切块,每一个3*3的切块,包含了全部9个输出各自的一个组成部分(二十五个组成部分之一,如果你要算上bias,相当于输出的二十六个组成部分之一)。这种多维空间的思考很看几何直觉的,如果实在不理解,可以多花图,多用变量描述,对于我,到这一步已经很容易理解了,所以不再过多的展开,过于繁琐。建议实在不行就跳过。不理解也没关系,为什么有这么“抽象”的一步,主要就是为了下边的reshape能够按意愿去操作(这操作其实是和图一计算的shape呼应的),最后,transpose之后就符合直觉了!

 

 

图四:专门针对循环内部的那个用OH和OW“滑窗”的操作做的等价转换。

尽量用比较直觉的形式去表达,但是因为再画就太大了,所以点到为止,可以看到,5*5个3*3的切片,都拿出切块中的第九个点,就组成了原始的第九个窗口(对应第九个输出所对应的输入)

python手写神经网络之im2col原理及实现

 

那么问题来了,为什么不能写一个符合直觉的循环呢?为什么不直接用滑窗的形式去提取?并且也省略了transpose那一步。因为效率?transpose操作本身不花时间吗?是和内存排列有关,这种切法效率更高?

 

所以我怎么可能不去尝试呢?一方面是探究如果不这样是不是就不能实现,另一方面也为了给大家展示一个符合直觉的过程!

下面是我自己的实现:

注意一致性,col的定义变了,reshape也要按照图一的目的做相应改变(transpose不能完全省略,还是要改变channel的位置的)。

python手写神经网络之im2col原理及实现

python手写神经网络之im2col原理及实现

 

python手写神经网络之im2col原理及实现

 

至少从实现结果上,应该是一致的,那么效率呢。

 

 

下面计算一下时间,反而我的实现还更快一些!!

python手写神经网络之im2col原理及实现

python手写神经网络之im2col原理及实现

 

 

虽然自己做的更改看起来是能达到同样效果,并且效率还更高,不过保留其他可能存在的局限性,比如反向操作col2img的难度(col2img作为一个函数,input肯定是固定的,output既然也一样,那么好像col2img哪怕共享同一种实现,也是不干扰的。)

 

另一方面,我好像也不能完全说第一个参考实现不符合直觉,可能是视角不同,但是我感觉那种略抽象。他是以一个卷积核元素为单位去看的。for循环内,一次一个卷积核元素和它所有对应的滑动输入。

 

另外一个就是整体赋值的自动匹配问题

col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]
col[:,:,y,x,:,:] = img[:,:,y_start:y_start + filter_h, x_start:x_start+filter_w]

切片的形状分别是(N,C,3,3)和(N,C,5,5),col定义中,前两者是NC,赋值中,固定了y,x,剩下那(3,3)和(5,5)就自动匹配上了。