在深度学习(DL)中经常会用到卷积神经网络,在这篇文章中,就将介绍卷积神经网络,具体包括其概念、卷积的含义、整个神经网络运作的过程以及卷积神经网络中的误差反向传播。
一、概念
卷积神经网络(Convolutional Neural Networks, CNNs)是一种专门设计用于处理具有类似网格结构的数据的深度学习模型,如图像数据(二维网格)、时间序列数据(一维网格),甚至视频数据(三维网格)。CNN在许多计算机视觉任务中取得了巨大的成功,包括图像分类、目标检测、人脸识别等。
一般来说,卷积神经网络包括四大层,分别为:输入层、卷积层、池化层以及输出层。
二、卷积
首先,我们知道卷积是一种两个函数间的相互作用,在图像处理、计算机视觉以及深度学习的背景下,卷积主要用于提取输入数据(如图像)的特征。
具体来说,就是对于一张图片,我们有一个模板,这个模板的长宽是要小于图片的长宽的,然后我们将这个模板不停覆盖住图像的一部分,然后对于覆盖住的地方进行相应计算,然后计算完成后,移动模板,最终使整个图像都被模板覆盖过。
比如有如下的一张图像,它是6*6的大小:

那么,我们设置一个模板,其大小为3*3,并进行如下的覆盖:
......

在上述不停用模板覆盖的过程中,我们还需要用对应方块内的值,即像素值,与模板(也称卷积核)下每个方块的大小进行点积计算,这样就会得到一个5*5的新矩阵,这称为特征图或激活图。
三、卷积神经网络整个过程
2.1 整体流程
那么,我们将上述的卷积过程放到整个的神经网络中,完整的流程是这样的:
先将图像存储为矩阵的形式,然后对于矩阵进行上述卷积操作,然后将所得的新矩阵利用激活函数进行计算,且是对于新矩阵中每一个元素,接着得到了第三个矩阵,对于这个矩阵进行池化操作,即分割成若干个互不相交的若干小块,每个小块内选取最大值去组成另一个矩阵(这是最大池化方式),这样就得到了池化层,接着将之转为一维向量,然后全连接得到全连接层,最后数据进入输出层,并根据最终输出的目的去设计输出层的具体任务:
对于分类任务,通常使用Softmax激活函数来生成每个类别的概率分布。Softmax确保所有输出的概率之和为1,适合多类别分类问题。
对于二分类任务,有时会使用Sigmoid激活函数,它能给出0到1之间的单个值,表示属于某一类的概率。
对于回归任务,可能不使用任何激活函数或使用线性激活函数,直接输出预测值。
在完成上述所有任务后,计算损失函数,以评估预测结果的好坏,再使用反向传播算法和梯度下降等优化算法调整网络权重,以最小化损失函数并提高模型性能。
将上述操作转为流程图就是:

2.2 池化操作
其中,关于池化操作,常见的方法有:
最大池化:选择窗口内的最大值作为输出。
平均池化:求取窗口下所有数据的均值作为输出。
随机池化:选择窗口下的随机一个值作为输出。
L2池化:计算窗口下的L2范数作为输出,即有一个矩阵A,其大小为n*n,则计算公式为:
2.3 激活函数
常见的激活函数有四种,分别如下:
- Sigmoid函数 :

2.Tanh函数 :

- ReLU函数 :

4.Leaky ReLU函数 : 其中,α是一个很小的正常数。

- Softmax函数:通常用于多分类问题中,将输入转换为概率分布。对于一个K维向量z,其第i个元素定义为:
四、误差反向传播
首先,我们对于卷积层中的加权输入作出这样的计算公式:
然后假设激活函数为:a(),那么就得到:
在池化层中,我们同样采用最大池化方法,那么就得到加权输出为:
在公式中,k和l分别表示所覆盖窗口的坐标,可以理解为x轴和y轴。
因为没有激活函数,所以为:
在输出层,有:
此外,还可通过标签计算出平方误差为:
其中的表示第i个标签。
在此基础上,我们定义一个变量用来表示第l层第j个神经单元的误差:
那么在卷积层和输出层的神经单元误差分别为:
关于输出层的神经单元误差,通过链式法则,还可以表示为:
然后,我们知道可以通过链式法则用输出层的加权输入对C求偏导得到,所以实现的反向传播,所以,在最后得到了公式:
五、代码实践
在这篇文章中,我先用 tensorflow 来进行预测,在下篇文章中则自行实现简单的CNN,tensorflow 代码如下:
python
import numpy as np
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.utils import to_categorical
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32')
X_test = X_test.reshape(X_test.shape[0], 28, 28, 1).astype('float32')
X_train /= 255
X_test /= 255
Y_train = to_categorical(y_train, 10)
Y_test = to_categorical(y_test, 10)
model = Sequential()
model.add(Conv2D(32, kernel_size=(5, 5), activation='relu', input_shape=(28, 28, 1)))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(X_train, Y_train, batch_size=128, epochs=10, verbose=1, validation_data=(X_test, Y_test))
score = model.evaluate(X_test, Y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
其输出为:
python
Epoch 1/10
469/469 [==============================] - 10s 20ms/step - loss: 0.2914 - accuracy: 0.9078 - val_loss: 0.0548 - val_accuracy: 0.9820
Epoch 2/10
469/469 [==============================] - 10s 21ms/step - loss: 0.0959 - accuracy: 0.9708 - val_loss: 0.0380 - val_accuracy: 0.9870
Epoch 3/10
469/469 [==============================] - 10s 22ms/step - loss: 0.0716 - accuracy: 0.9787 - val_loss: 0.0320 - val_accuracy: 0.9896
Epoch 4/10
469/469 [==============================] - 10s 22ms/step - loss: 0.0599 - accuracy: 0.9826 - val_loss: 0.0290 - val_accuracy: 0.9904
Epoch 5/10
469/469 [==============================] - 10s 22ms/step - loss: 0.0521 - accuracy: 0.9843 - val_loss: 0.0249 - val_accuracy: 0.9921
Epoch 6/10
469/469 [==============================] - 10s 22ms/step - loss: 0.0453 - accuracy: 0.9860 - val_loss: 0.0243 - val_accuracy: 0.9924
Epoch 7/10
469/469 [==============================] - 12s 25ms/step - loss: 0.0413 - accuracy: 0.9873 - val_loss: 0.0212 - val_accuracy: 0.9927
Epoch 8/10
469/469 [==============================] - 15s 32ms/step - loss: 0.0356 - accuracy: 0.9889 - val_loss: 0.0237 - val_accuracy: 0.9925
Epoch 9/10
469/469 [==============================] - 17s 36ms/step - loss: 0.0344 - accuracy: 0.9891 - val_loss: 0.0204 - val_accuracy: 0.9935
Epoch 10/10
469/469 [==============================] - 11s 23ms/step - loss: 0.0314 - accuracy: 0.9906 - val_loss: 0.0239 - val_accuracy: 0.9933
Test loss: 0.023907233029603958
Test accuracy: 0.9933000206947327
此上