[DL]CNN中的两个计算问题
CNN感受野的计算
没有什么比一道题能够更好的说明问题,如下。
假设网络结构如下:
Type | Size | Stride |
---|---|---|
conv1 | 3 | 1 |
pooling1 | 2 | 2 |
conv2 | 3 | 1 |
pooling2 | 2 | 2 |
conv3 | 3 | 1 |
conv4 | 3 | 1 |
pooling3 | 2 | 2 |
问题是,给定上述网络结构,计算pooling3的感受野大小?
一种通用的思路是按照自顶向下的方式计算,也就是从pooling3开始到conv1。具体如下。
再次之前,回顾一下如何计算conv或者pooling之后的输出特征图大小,如下,
out = (in+2*padding-kernel_size)/step + 1
由网络结构可知,pooling3的一个值对应输入大小为2*2,也就是conv4的输出。由上述计算公式,很容易得到conv4的输入,为4*4。
依次类推,得到conv1的输入是30*30,故可以得到结论:pooling3的感受野大小为30*30。
代码实现可以参考1。
CNN的卷积核大小
知道如何计算感受野之后,可以思考一个问题,卷积核越小越好吗?
按照上述计算方法,可以很容易计算得出:
一个7*7的卷积核的输出值对应的感受野和三个3*3的卷积核一样;
一个5*5的卷积核的输出值对应的感受野和两个3*3的卷积核一样;
既然这样,当然可以用后者代替前者,但是优点是什么?
假设输入是H*W*C,这里我们假定卷积核的个数也为C,卷积核大小为7*7。那么首先需要思考,一次卷积的计算量是多少?
计算量来自乘法和加法,乘法的次数是卷积和输入特征图的对应位相乘,加法来自相乘结果后相加,这样加法会比乘法少一次,但是考虑到每个卷积核对应一个bias(有些网络结构不需要bias),需要再加一次。故一次卷积的乘法和加法次数一样,都是卷积核大小的平方。
这样,在评估纯卷积场景下的计算量的时候,可以用乘-加作为单位,大小为为卷积核大小的平方。
现在可以回答上述的问题了。参数量为C*(7*7*C),假设输出和输入大小保持一致,则需要的乘-加 次数为C*(7*7*C)*H*W,也就是说需要考虑卷积次数。
如果是三个3*3的卷积呢?容易得到参数量为3*C*(3*3*C),乘-加次数为3*C*(3*3*C) *H*W。
嗯,参数个数和乘-加次数,三个3*3卷积都是一个7*7卷积的27/49 倍。
这下就不难理解为啥要用3*3卷积了。不明白啊,为啥不用2*2卷积呢?有些资料认为是因为3*3是最小非对称卷积核,能够捕捉到各个方向(斜线,中间)的信号,而2*2是对称卷积。个人目前对此不能完全认同,有待后续思考。
乘-加次数是和参数个数成正比例关系的,那么参数量能不能进一步减少?
保证感受野相同的前提下,一个3*3卷积分解为1*3卷积和3*1两个卷积。两个小卷积的参数量是原来的2/3倍。
总结:按照目前的发展,脱离感受野谈参数量减少,都是耍流氓。虽然小卷积能够保证感受野相同的前提下,减少参数量和计算量,但是这会不会带来优化的难度,对参数空间造成的影响是什么,似乎都不是很清楚。感受野的大小对任务性能到底是如何影响的,如何有效度量而非看任务最后的评价指标。假设显存足够,速度够快,优化难度低,大卷积核何尝不是一种选择?
参考文献:
1.写代码计算感受野
结论是:大部分场景下,使用大小为3的卷积。