前言
卷积网络,也叫卷积神经网络(Convolutional Neural Network,CNN),是一种专门处理具有网格状拓扑结构的数据的神经网络。CNN泛指那些至少在网络的一层中使用卷积运算来代替一般矩阵乘法运算的神经网络 [1]。因此,在CNN中,卷积运算是关键。本篇介绍CNN中卷积运算,以及其与传统卷积运算的区别和联系。本篇为《深度学习》(Yoshua Bengio, et al.)第9章学习笔记,主要涵盖第9.1节内容,经本人学习、理解并加以加工。由于水平有限,如有不当之处,欢迎大家指出,一起学习交流。
1. 传统的卷积运算
百度百科给出的卷积运算的定义是:卷积(Convolution)是通过两个函数 f f f和 g g g生成第三个函数的一种数学算子,表征函数 f f f与 g g g经过翻转和平移的重叠部分函数值乘积对重叠长度的积分。数学地,在一维情况下,连续卷积的定义如下: s ( t ) = ( f ∗ g ) ( t ) = ∫ − ∞ ∞ f ( τ ) g ( t − τ ) d τ . s(t) = (f*g)(t) = \int_{-\infin}^{\infin} f(\tau) g(t-\tau) d\tau. s(t)=(f∗g)(t)=∫−∞∞f(τ)g(t−τ)dτ.相应地,离散卷积的定义如下: s ( n ) = ( f ∗ g ) ( n ) = ∑ i = − ∞ ∞ f ( n ) g ( n − i ) . s(n) = (f*g)(n) = \sum_{i=-\infin}^{\infin} f(n) g(n-i). s(n)=(f∗g)(n)=i=−∞∑∞f(n)g(n−i).
如果上述函数 g g g表示概率的话,卷积相当于加权平均 [1]。
注释:
从上述离散卷积的定义可以一窥其计算过程大致包含下面四个步骤:翻转( − i -i −i导致对 g g g进行翻转)、位移( n − i n-i n−i导致对 g g g进行位移)、相乘、求和。相信很多人在学习高数时也学过卷积及其对应的图解法,其形象生动地说明了上述计算过程。具体例子和计算过程,不再赘述(懒得手敲公式和制图了。。。),可参考超链接内容。个人在此只是想帮大家回忆并强调,翻转(Flip)这一操作。因为CNN中的卷积运算往往没有翻转,而是实现的相关函数(Cross-correlation)这一操作,接下来我们会详细说。
当我们处理图像数据时,经常遇到的是二维卷积。其连续形式和离散形式分别为:
S
(
x
,
y
)
=
(
F
∗
G
)
(
x
,
y
)
=
∫
τ
1
=
−
∞
∞
∫
τ
2
=
−
∞
∞
F
(
τ
1
,
τ
2
)
G
(
x
−
τ
1
,
y
−
τ
2
)
d
τ
1
d
τ
2
.
S(x,y) = (F*G)(x,y) = \int_{\tau_1=-\infin}^{\infin} \int_{\tau_2=-\infin}^{\infin} F(\tau_1,\tau_2) G(x-\tau_1,y-\tau_2) d\tau_1 d\tau_2.
S(x,y)=(F∗G)(x,y)=∫τ1=−∞∞∫τ2=−∞∞F(τ1,τ2)G(x−τ1,y−τ2)dτ1dτ2.
和
S
(
m
,
n
)
=
(
F
∗
G
)
(
m
,
n
)
=
∑
i
=
−
∞
∞
∑
j
=
−
∞
∞
F
(
i
,
j
)
G
(
m
−
i
,
n
−
j
)
.
S(m,n) = (F*G)(m,n) = \sum_{i=-\infin}^{\infin} \sum_{j=-\infin}^{\infin} F(i,j) G(m-i,n-j).
S(m,n)=(F∗G)(m,n)=i=−∞∑∞j=−∞∑∞F(i,j)G(m−i,n−j).
和一维离散卷积一样,二维离散卷积的计算也可以通过翻转( − i -i −i, − j -j −j,对 G G G而言)、位移( m − i m-i m−i, n − j n-j n−j, 对 G 而 言 对G而言 对G而言)、相乘、求和得到。这里需要对矩阵 G G G沿着两个坐标轴同时“对调”,也就是相当于将矩阵 G G G翻转了180度。从这个意义上来说,二维离散卷积的翻转和一维离散卷积的翻转一致。但是需要注意的是,矩阵 F F F和 G G G是按位相乘,而非传统的矩阵相乘。
2. CNN中的卷积运算
我们再回到《深度学习》第9.1节。在机器学习或者CNN中,我们经常需要对图像进行卷积,以提取特征:
S
(
i
,
j
)
=
(
I
∗
K
)
(
i
,
j
)
=
∑
m
∑
n
I
(
m
,
n
)
K
(
i
−
m
,
j
−
n
)
.
S(i,j) = (I*K)(i,j) = \sum_{m} \sum_{n} I(m,n) K(i-m,j-n).
S(i,j)=(I∗K)(i,j)=m∑n∑I(m,n)K(i−m,j−n). 在机器学习或者CNN术语中,
I
I
I称作输入(Input),其对应数据组成的多维数组(即张量,下同);
K
K
K称作内核(Kernel),其对应参数组成的多维数组;输出
S
S
S有时被称作特征图谱(Feature map)[1]。
相较上述定义,下述公式在机器学习库中更容易完成,因为内核的尺寸往往要远小于输入的尺寸:
S
(
i
,
j
)
=
(
K
∗
I
)
(
i
,
j
)
=
∑
m
∑
n
I
(
i
−
m
,
j
−
n
)
K
(
m
,
n
)
.
S(i,j) = (K*I)(i,j) = \sum_{m} \sum_{n} I(i-m,j-n) K(m,n) .
S(i,j)=(K∗I)(i,j)=m∑n∑I(i−m,j−n)K(m,n). 该公式利用了卷积的可交换性(Commutative)。我们可以利用卷积的定义或者结合图像来很容易地证明、理解该性质、公式。
注释:
我们看个简单的例子:
I
=
[
1
2
3
4
5
6
7
8
9
10
11
12
]
I = \begin{bmatrix} 1&2&3&4 \\ 5&6&7&8 \\ 9&10&11&12 \end{bmatrix}
I=⎣⎡159261037114812⎦⎤
K
=
[
2
3
7
8
]
K = \begin{bmatrix} 2&3\\ 7&8 \end{bmatrix}
K=[2738]
我们按照上述公式,来计算
S
(
0
,
0
)
S(0,0)
S(0,0):
S
(
0
,
0
)
=
(
I
∗
K
)
(
0
,
0
)
=
∑
m
∑
n
I
(
m
,
n
)
K
(
0
−
m
,
0
−
n
)
=
I
(
0
,
0
)
K
(
0
,
0
)
=
2
S(0,0) = (I * K)(0,0) = \sum_{m} \sum_{n} I(m,n) K(0-m,0-n) = I(0,0) K(0,0) = 2
S(0,0)=(I∗K)(0,0)=m∑n∑I(m,n)K(0−m,0−n)=I(0,0)K(0,0)=2
S
(
0
,
0
)
=
(
K
∗
I
)
(
0
,
0
)
=
∑
m
∑
n
K
(
m
,
n
)
I
(
0
−
m
,
0
−
n
)
=
K
(
0
,
0
)
I
(
0
,
0
)
=
2
S(0,0) = (K * I)(0,0) = \sum_{m} \sum_{n} K(m,n) I(0-m,0-n) = K(0,0) I(0,0) = 2
S(0,0)=(K∗I)(0,0)=m∑n∑K(m,n)I(0−m,0−n)=K(0,0)I(0,0)=2
依此类推,
S
=
I
∗
K
=
[
2
7
12
17
12
17
49
69
89
56
53
129
149
169
100
63
142
157
172
96
]
S = I * K = \begin{bmatrix} 2 & 7 & 12 & 17 &12 \\ 17 & 49 & 69 & 89 & 56 \\ 53 & 129 & 149 & 169 & 100 \\ 63 & 142 & 157 & 172 & 96 \end{bmatrix}
S=I∗K=⎣⎢⎢⎡217536374912914212691491571789169172125610096⎦⎥⎥⎤
S
=
K
∗
I
=
[
2
7
12
17
12
17
49
69
89
56
53
129
149
169
100
63
142
157
172
96
]
S = K * I = \begin{bmatrix} 2 & 7 & 12 & 17 &12 \\ 17 & 49 & 69 & 89 & 56 \\ 53 & 129 & 149 & 169 & 100 \\ 63 & 142 & 157 & 172 & 96 \end{bmatrix}
S=K∗I=⎣⎢⎢⎡217536374912914212691491571789169172125610096⎦⎥⎥⎤需要注意的是:
- 对于
S
(
0
,
0
)
S(0,0)
S(0,0),上述计算中的
m
m
m和
n
n
n只有取0时,
K
(
0
−
m
,
0
−
n
)
K(0-m,0-n)
K(0−m,0−n)、
I
(
0
−
m
,
0
−
n
)
I(0-m,0-n)
I(0−m,0−n)才有意义。在实际计算时,对于不在“定义域”内的值,我们按照0处理。在机器学习库中有不同的补零操作,在后续文章中我们会再详细讨论
2.这里,多维数组或矩阵 I I I和 K K K的序号是从0开始的(例如Python),所以是 K ( i − m , j − n ) K(i-m, j-n) K(i−m,j−n), I ( i − m , j − n ) I(i-m, j-n) I(i−m,j−n),如果我们按照Matlab的习惯,矩阵序号从1开始,那么 K ( i − m , j − n ) K(i-m, j-n) K(i−m,j−n), I ( i − m , j − n ) I(i-m, j-n) I(i−m,j−n)应相应改成 K ( i − m + 1 , j − n + 1 ) K(i-m+1, j-n+1) K(i−m+1,j−n+1), I ( i − m + 1 , j − n + 1 ) I(i-m+1, j-n+1) I(i−m+1,j−n+1)
卷积运算的可交换性实际上是由翻转造成的。其在证明时很有用,但在神经网络的应用中却不是很重要。许多机器学习库在实际实现“卷积”时采用的是相关函数:
S
(
i
,
j
)
=
(
I
∗
K
)
(
i
,
j
)
=
∑
m
∑
n
I
(
i
+
m
,
j
+
n
)
K
(
m
,
n
)
.
S(i,j) = (I*K)(i,j) = \sum_{m} \sum_{n} I(i+m,j+n) K(m,n).
S(i,j)=(I∗K)(i,j)=m∑n∑I(i+m,j+n)K(m,n).从上述定义可以看出,相关函数和卷积运算几乎一样,只是没有对内核进行翻转。一般,我们遵循将这两种运算都称作卷积。
注释:
- 这里,多维数组或矩阵 I I I和 K K K的序号如果从1开始(例如Matlab),应为 i + m − 1 i+m-1 i+m−1, j + n − 1 j+n-1 j+n−1
下图(图9.1)给出了一个二维卷积(相关函数)的例子。该例子中,没有对核进行翻转,因此是进行的相关函数运算。而且该例限制只对内核完全处在输入中的位置进行输出,即有效卷积(Valid convolution),而上面的例子计算的是完整卷积。有关它们的区别,我们将在后面的文章中讨论。
如果严格按照卷积的定义(但还是做有效卷积),那么应该先对内核进行翻转,得到
[
z
y
x
w
]
\begin{bmatrix} z & y \\ x & w \end{bmatrix}
[zxyw]
因此,输出的第一个值(左上角)为
a
z
+
b
y
+
e
x
+
f
w
az + by + ex + fw
az+by+ex+fw
如下图所示,依此类推。
可以看出,对输入和内核进行卷积,其实就是将内核(进行翻转)在输入上按照行列方向不断地移动,并按位进行矩阵相乘,再求和,就得到输出对应位置的值。
最后,我们从矩阵相乘的角度来理解卷积运算。
卷积运算作为一种特殊的线性运算,其离散形式可以看作是矩阵的相乘。对于一维的离散卷积,其可以写成向量和矩阵相乘的形式,只是内核对应的矩阵是个特殊的Toeplitz矩阵,其每一行都相对上一行平移了一个单位。对于二维的离散卷积,其可以写成矩阵与矩阵相乘的形式,这时内核对应的矩阵是个特殊的双重分块循环矩阵。至于如何将卷积写成矩阵相乘的形式,可以参考参考文献[4]。机器学习库实际在实现时,也是利用的矩阵乘法。下面给个简单的一维离散卷积的例子。
注释:
a
=
[
1
2
3
]
,
b
=
[
4
5
]
a = [1~2~3],~~b=[4~5]
a=[1 2 3], b=[4 5]那么
a
∗
b
=
b
∗
a
=
[
4
13
22
15
]
a*b = b*a = [4~13~22~15]
a∗b=b∗a=[4 13 22 15]其对应的矩阵相乘为
[
1
2
3
0
]
×
[
4
5
0
0
0
4
5
0
0
0
4
5
5
0
0
4
]
⏟
Toeplitz矩阵
\begin{bmatrix} 1& 2 & 3 & 0 \end{bmatrix} \times \underbrace{ \begin{bmatrix} 4 & 5 & 0 & 0 \\ 0 & 4 & 5 & 0 \\ 0 & 0 & 4 & 5 \\ 5 & 0 & 0 & 4 \end{bmatrix} }_{\text{Toeplitz矩阵}}
[1230]×Toeplitz矩阵
⎣⎢⎢⎡4005540005400054⎦⎥⎥⎤
[
4
5
0
0
]
×
[
1
2
3
0
0
1
2
3
3
0
1
2
2
3
0
1
]
⏟
Toeplitz矩阵
\begin{bmatrix} 4 & 5 & 0 & 0 \end{bmatrix} \times \underbrace{ \begin{bmatrix} 1 & 2 & 3 & 0 \\ 0 & 1 & 2 & 3 \\ 3 & 0 & 1 & 2 \\ 2 & 3 & 0 & 1 \end{bmatrix} }_{\text{Toeplitz矩阵}}
[4500]×Toeplitz矩阵
⎣⎢⎢⎡1032210332100321⎦⎥⎥⎤
从矩阵相乘这点来看,任何一个使用矩阵乘法而不依赖矩阵结构特殊性质的神经网络算法,都适用于卷积运算,并且不需要对神经网络做出大的修改。