卷积神经网络 (Convolutional Neural Network, CNN): 是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的周围单元,对于大型图像处理有出色表现。

1 原理

要学习卷积神经网络,当然首先得学习朴素的神经网络:https://io.zouht.com/109.html

接下来当然得了解卷积:https://io.zouht.com/175.html

我们知道,朴素的神经网络是通过误差逆传播方法进行参数学习,整个过程无需人工干预参数,是系统自学习的。

我们也知道,卷积是一种特征提取的方式,通过构造特定卷积核,这个卷积核就能完成特定的事情例如对数据特征进行提取。

那么能不能将上述二者结合,形成一个能够自学习构造卷积核,让这个卷积核能够捕获数据中某种特征,最后通过提取出的特征实现特定功能呢?这便是卷积神经网络!

通常来说,卷积神经网络只是包含了几层”卷积层“,卷积层提取特征后,会输入“全连接层”(即感知机层),让全连接层进一步拟合。

2 前向传播

对于一个(二维)卷积层,它有两种参数:

  • n×n 的卷积核参数:Wn×n
  • 偏置:b

它的输入和输入都是矩阵:

  • 输入 p×q 大小的矩阵:Gp×q
  • 输出 (p2)×(q2) 大小的矩阵:H(p2)×(q2)

前向传播过程其实就是进行卷积:

H(p2)×(q2)=Gp×qWn×n+b

注 1:卷积运算常用 ​ 来表示,请注意它不是乘法。
注 2:该偏置是实数,加偏置的意义是矩阵每个位置分别加偏置。

不过需要注意的是,在朴素的神经网络中我们一般一层的输入输出都是向量,但是(二维)卷积输入输出都是矩阵。

因此,在卷积层和全连接层之间,需要插入一个展平层,前向传播时负责把矩阵拉平成向量,反向传播时负责把梯度从向量压回矩阵。

3 反向传播

神经网络的核心是反向传播,而反向传播的核心就是求导。

3.1 对 W​ 求偏导

我们要求的是:

EW

首先考虑卷积核的参数之一:

Ewij=p=0n1q=0n1Ehpqhpqwij

我们把卷积展开来写:

hij=α=0n1β=0n1gi+αn12,j+βn12wαβ+b

那么:

hpqwij=α=0n1β=0n1gp+αn12,q+βn12wαβ+bwij=gp+in12,q+jn12

那么:

Ewij=p=0n1q=0n1Ehpqhpqwij=p=0n1q=0n1gp+in12,q+jn12Ehpq

我们仔细观察上面这个式子,是不是可以把它看作卷积的一部分?我们把它组合成卷积:

EW=GEH

3.2 对 b 求偏导

我们仍然从卷积的一部分开始:

hij=α=0n1β=0n1gi+αn12,j+βn12wαβ+b

那么:

hijb=1

那么:

Ed=i,jEhijhijd=i,jEhij

3.3 对 G 求偏导

上面两步完成了本层的参数梯度计算,但是我们还得为计算前一层的梯度,要不然就没法继续往前传了。

我们要求的是:

EG

首先考虑输入矩阵其中一项:

Egij=p=0n1q=0n1Ehpqhpqgij

由于:

hij=α=0n1β=0n1gi+αn12,j+βn12wαβ+b

那么:

hpqgij=α=0n1β=0n1gp+αn12,q+βn12wαβ+bgij=wip+n12,jq+n12

那么:

Egij=p=0n1q=0n1wip+n12,jq+n12Ehpq

和 3.1 中操作一样,我们可以把它看作卷积:

EG=WEH

4 代码实现

以下是我使用 C++ 编写的神经网络的源代码,其中实现了卷积层,可以和上文的公式对照学习:

https://github.com/ChrisKimZHT/Neural-Net-cpp

实际上,3.1 ~ 3.3 推导出的三个梯度,代码表示就如下:

Matrix ConvolutionalLayer::backward(const Matrix &d_output, double learning_rate) {
    Matrix d_input = d_output.convolution(weights.transpose(), false);
    Matrix d_filters = input.convolution(d_output.transpose(), true);
    double d_bias = d_output.sum();

    weights -= d_filters * learning_rate;
    bias -= d_bias * learning_rate;

    return d_input;
}
文章目录