关于卷积神经网络的一份介绍

发布于:2025-02-28 ⋅ 阅读:(110) ⋅ 点赞:(0)

在深度学习(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,则计算公式为:

L2=\sqrt{\sum_{i=1\;\;j=1}^{n}A(i,j)^2}

2.3 激活函数

常见的激活函数有四种,分别如下:

1. Sigmoid函数f(x)=\frac{1}{1+e^{-x}}

2. Tanh函数f(x)=tanh(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}}

3. ReLU函数f(x)=max(0,x)

4. Leaky ReLU函数f(x)=max(\alpha x,x)       其中,α是一个很小的正常数。

5. Softmax函数:通常用于多分类问题中,将输入转换为概率分布。对于一个K维向量z,其第i个元素定义为:f(z_i)=\frac{e^{z_i}}{ \sum_{j=1}^{k}e^{z_j}}

四、误差反向传播

首先,我们对于卷积层中的加权输入作出这样的计算公式:

z_{ij}^{Fk}= \sum_{i=1\;\;j=1}^{n}w_{ij}x_{ij}^{Fk}+b^{Fk}

然后假设激活函数为:a(),那么就得到:a_{ij}^{Fk}

在池化层中,我们同样采用最大池化方法,那么就得到加权输出为:

z_{ij}^{Pk}=Max(x^{Fk}_{kl})=Max(x^{Fk}_{(i-1)n+1:(i-1)n+n:(j-i)n+1:(j-1)n+n})

在公式中,k和l分别表示所覆盖窗口的坐标,可以理解为x轴和y轴。

因为没有激活函数,所以为:a_{ij}^{Pk}=z^{Pk}_{ij}

在输出层,有:

Z^O_n=\sum_{ij}^{On}w^{On}_{ij}a^{Pk}_{ij}+b^O_n

a^O_n=a(z_n^O)

此外,还可通过标签计算出平方误差为:C=\frac{1}{2}(\sum_{i=1}^{n}(t_i-a_i^O)^2)

其中的t_i表示第i个标签。

在此基础上,我们定义一个变量用来表示第l层第j个神经单元的误差:

\delta ^l_j=\frac{\partial C}{\partial z^l_j}

那么在卷积层和输出层的神经单元误差分别为:

\delta ^{Fk}_{ij}=\frac{\partial C}{\partial z^{Fk}_{ij}}\;\;\;\;\;\;\delta ^O_n=\frac{\partial C}{\partial z^O_n}

关于输出层的神经单元误差,通过链式法则,还可以表示为:

\delta ^O_n=\frac{\partial C}{\partial z^O_n}=\frac{\partial C}{\partial a^O_n}\frac{\partial a^O_n}{\partial z^O_n}=\frac{\partial C}{\partial a^O_n}a'(z^O_n)=(a^O_n-t_n)a'(z_n^O)

然后,我们知道\frac{\partial C}{\partial z^{Fk}_{ij}}可以通过链式法则用输出层的加权输入对C求偏导得到,所以实现的反向传播,所以,在最后得到了公式:

\delta ^{Fk}_{ij}=(\sum_{i=1}^{n}\delta ^O_iw^{Oi}_{k-i'j'})*a'(z^{Fk}_{ij})*\left\{\begin{matrix} 1 &when \;a^{Fk}_{ij}\; is \;maximum\\ 0 & otherwise \end{matrix}\right.

五、代码实践

在这篇文章中,我先用 tensorflow 来进行预测,在下篇文章中则自行实现简单的CNN,tensorflow 代码如下:

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])

其输出为:

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

此上


网站公告

今日签到

点亮在社区的每一天
去签到