深入理解卷积(卷积核到底要不要翻卷)

前言

我最初接触卷积,是在学习tensorflow的时候,其中有一个函数可以实现卷积操作:conv2d()。这个函数实现的功能,简单来说,就是将卷积核在输入的矩阵上平移,对应位置的乘积的和加上一个bias,即为输出。然而,有一天,我突然看到,发现对两个矩阵做卷积运算的时候,作为卷积算子的矩阵要逆时针旋转180度!!!WTF!!没有人,也没有书,告诉我,卷积除了平移,还有一个翻卷操作。于是,我便开始了卷积的探索之旅。

0.做一个实验

这里主要做一个实验,分别对tensorFlow和scipy.signal中的卷积操作做实验对比:

scipy.signal中

from scipy.signal import convolve2d as conv2d
A = np.array([[1, 2, 1], [2, 1, 2], [2, 1, 1]]) # A 为输入特征
W = np.array([[1, 1], [1, 2]])  # W 为卷积核
out1 = conv2d(A,W,mode='valid')
print(out1)

结果为:
[[7 8]
[8 6]]

tensorFlow中

import tensorflow as tf
a = tf.constant([1,2,1,2,1,2,2,1,1], dtype = tf.float32, shape = [1,3,3,1])
b = tf.constant([1,1,1,2], dtype = tf.float32,shape = [2,2,1,1])
c = tf.nn.conv2d(a,b,strides = [1,1,1,1],padding='VALID')
with tf.Session() as sess:
    print(sess.run(a))
    print(b)
    print(sess.run(c))

结果为:
[[[[7.]
[8.]]

[[7.]
[6.]]]]
结果竟然不一样,显然,scipy.signal中对卷积核进行了180度的翻转,而tensorFlow中并没有这个操作。

1.卷积的定义

在数学中,概念是非常重要的,如果连概念都不清楚,何谈理解呢。下面是从wiki百科中得到的知识:(下面的一小部分定义就当作是学习英文了,毕竟这样的表达会更准确)
Definition:
The convolution of ff,and gg is tritten f gf\ * g,using an asterisk or star. It is defined as the intergral of the product of the two functions after one is reversed and shifted. As such, it is a particular kind of intergral transform:
(fg)(t)=+f(τ)g(tτ)(f*g)(t)=\int_{-\infty}^{+\infty}f(\tau)g(t-\tau)
While the symbol tt is used above, it need not represent the time domain. But in that context, the convolution formula can be described as a weighted average of the function f(τ)f(\tau) at the moment tt where the weighting is given by g(τ)g(-\tau) simply shifted by amount tt. As tt changes,the weighting function emphasizes different parts of the input function.

For functionsff,gg supported on only[0,)[0,\infty)(i.e, zero for negative argumenets), the integration limits can be truncated, resulting in:
(fg)(t)=0tf(τ)g(tτ)dτ forf,g:[0,)R(f*g)(t)=\int_0^tf(\tau)g(t-\tau)d\tau \ for f,g:[0,\infty)\rightarrow \mathbb R

如果是离散的形式,则定义为:
(fg)(t)=+f(τ)g(tτ)(f*g)(t)=\sum_{-\infty}^{+\infty}f(\tau)g(t-\tau)
这里举一个离散形式简单的例子来介绍卷积:
乘法操作:42 * 137
其中函数f(τ)g(tτ)f(\tau)g(t-\tau)可以看作f(x)g(y)f(x)g(y), 且x+y=tx+y=t(tt为所有可能的情况).
42=(2×100+4×101)42=(2\times10^0+4\times10^1)可看作序列(2,4)
137=(7×100+3×101+1×102)137=(7\times10^0+3\times10^1+1\times10^2)可看作序列(7,3,1)

xx 0 1
f(x)f(x) 2 4
yy 0 1 2
f(x)f(x) 7 3 1

也就是说,42可以看成序列(2,4),137可以看成序列(7,3,1),这两个序列的卷积是如何计算的呢?

  1. x+y=0x+y=0的情况:(x=0,y=0)(x=0,y=0)
  2. x+y=1x+y=1的情况:(x=0,y=1)(x=0,y=1),(x=1,y=0)(x=1,y=0)
  3. x+y=2x+y=2的情况:(x=0,y=2)(x=0,y=2),(x=1,y=1)(x=1,y=1)
  4. x+y=3x+y=3的情况:(x=1,y=2)(x=1,y=2)
    (2,4)卷积(7,1,3) = (2X7, 2X3+4X7, 2X1+4X3, 4X1) = (14,34,14,4)
    而14+34X10+14X100+4X1000 = 42X137
    可以看出,一个卷积操作,要把所有的tt遍历到(动态来看,就是一条滑动的直线)。对于每个ttx+y=tx+y=t都为一条斜对角直线,这条直线上所有的(x,y)(x,y)序列所表示的f(x)f(x)g(y)g(y),都要做乘法操作,然后求和。执行这样的操作一直到这条直线滑动完毕,就完成了一个卷积操作。
    深入理解卷积(卷积核到底要不要翻卷)
    其他人介绍卷积时,会添加一个生动的卷毛巾的动图,可以搜索看一下,加深理解程度。
    深入理解卷积(卷积核到底要不要翻卷)

矩阵的卷积到底要不要翻转

在数学中,两个矩阵进行卷积操作,卷积核是要翻卷的,如下面动图所示,它们的位置翻转对应。
深入理解卷积(卷积核到底要不要翻卷)
然而,在深度学习中的卷积似乎不是这样的,区别就是,在深度学习中,卷积核不需要翻卷。

That is a problem!!,不过,最终,我找到了答案。
出现这种差异的本质原因是:数学中的卷积和卷积神经网络中的卷积严格意义上是两种不同的运算

1.上文可以清晰地看到数学中卷积运算的特点:
卷积核与原始的矩阵乘积,是围绕着中心元素进行180度旋转后,才是对应的元素。

2.卷积神经网络的卷积本质上是一种spatial filter 为什么这么说呢?
我们来看两种图像空间滤波的常见操作
a)平滑滤波
b)边缘提取
这两种件事情,很容易通过设计特定的“卷积核”,然后将其与像素矩阵的对应元素(不进行上述的旋转)相乘得到。
3.此“卷积”与彼“卷积”的联系与区别 :
**最直观的就是:是否进行翻转,然后再进行对应元素的加权求和。 **
其实本质上来说是两者的用途不同

  • 数学中卷积,主要是为了诸如信号处理、求两个随机变量和的分布等而定义的运算,所以需要“翻转”是根据问题的需要而确定的
  • 卷积神经网络中“卷积”,是为了提取图像的特征,其实只借鉴了“加权求和”的特点
  • 还有一点一定要说的是:数学中的“卷积核”都是已知的或者给定的,卷积神经网络中“卷积核”本来就是trainable的参数,不是给定的,根据数据训练学习的,那么翻不翻转还有什么关系呢?因为无论翻转与否对应的都是未知参数!