利用Tensorflow2.0实现卷积神经网络CNN

2020-07-20 11:19:42 浏览数 (1)

前面几节课我们给大家介绍的都是全连接神经网络,但全连接神经网络有个明显的缺点,那就是当网络层数较多时(尤其是在图像识别任务中),它每层的参数数量容易变得很大,不好控制。所以本节课老shi准备给大家介绍另外一种非常重要的网络结构——卷积神经网络。卷积神经网络(Convolutional Neural Network, CNN)近几年在深度学习中的应用非常广泛,特别是在图像识别、语音识别以及本文处理方面。可以说,卷积神经网络是深度学习中最重要的神经网络之一,例如图像识别中非常有名的LeNet、AlexNet、 ResNet、VGGNet、InceptionNet等网络结构都是在卷积神经网络基础上得来的。

全连接神经网络 VS 卷积神经网络

全连接神经网络之所以不太适合图像识别任务,主要有以下几个方面的问题:

1.参数数量太多

假设一个输入图片由1000*1000像素组成,那么输入层就有1000*1000=100万节点。假设第一个隐藏层有100个节点,那么仅这一层就有(1000*1000 1)*100=1亿个参数,这个参数量确实非常大!如果我们的网络层数再多一些,那么参数数量就会更多,因此它的扩展性较差。

2.没有利用像素之间的位置信息

对于图像识别任务来说,每个像素和其周围像素的联系是比较紧密的,和离得很远的像素之间的联系可能就很小了。如果一个神经元和上一层所有神经元相连,那么就相当于对于一个像素来说,把图像的所有像素都等同看待,这不符合前面的假设。当我们完成每个连接权重的学习之后,最终可能会发现,有大量的权重,它们的值都是很小的(也就是这些连接其实无关紧要)。努力学习大量并不重要的权重,这样的学习必将是非常低效的。

3.网络层数限制

我们知道网络层数越多其表达能力越强,但是通过梯度下降法来训练深度全连接神经网络很困难的,因为全连接神经网络的梯度很难传递超过3层(梯度会消失)。因此,我们不可能得到一个很深的全连接神经网络,也就限制了它的能力。

那么,卷积神经网络又是怎么解决以上这些问题的呢?主要有四个思路:

1.局部连接

这是最容易想到的,每个神经元不再像全连接网络那样和上一层的所有神经元连接,而是只和一小部分神经元连接,这样参数量就大大减少了。

2.权值共享

每一组连接可以共享同一个权重,而不是每个连接有一个不同的权重,这样也大大减少了很多参数。

3.下采样

卷积神经网络可以通过池化层来进行下采样减少每层的样本数,同样也可以进一步减少参数量,同时还可以提高模型的鲁棒性。

4.充分利用位置信息

卷积核通过不断的卷积操作充分利用每个像素点之间的位置信息,更适用于图像识别任务中。

总之,对于图像识别任务来说,卷积神经网路通过尽可能保留重要参数,去掉大量不重要的参数,来达到更好的学习效果。

什么是卷积神经网络

首先,我们先获得一个感性的认识,以下是卷积神经网络的示意图。

卷积神经网络由若干卷积层(Convolution Layer)、池化层(Pooling Layer)和全连接层(Fully Connected Network)组成。它的常用架构模式为:

INPUT -> [[CONV]*N -> POOL?]*M -> [FC]*K

也就是输入数据INPUT,经过N个卷积层叠加,然后(可选)叠加一个池化层,重复这个结构M次,最后叠加K个全连接层。

注:每个卷积层后不一定需要连接池化层,看实际情况而定。

卷积神经网络各层计算

1、卷积层

我们用一个简单的例子来讲述如何计算卷积,假设有一个5*5的图像,使用3*3的卷积核(filter)进行卷积,最后想得到一个3*3的Feature Map,如下图所示:

为了描述清楚卷积计算过程,我们首先对图像的每个像素进行编号,用X(i,j)表示图像image的第i行第j列元素;对卷积核filter的每个权重进行编号,用W(m,n)表示第m行第n列权重,用Wb表示卷积核的偏置项(上图中每个卷积核filter的偏置项bias都是0);对Feature Map的每个元素进行编号,用a(i,j)表示Feature Map的第i行第j列元素;用f表示激活函数(这个例子选择relu函数作为激活函数)。然后,使用下列公式计算卷积:

例如,对于Feature Map左上角元素来说,其卷积计算方法为:

=relu(1 0 1 0 1 0 0 0 1 0)

=relu(4)

=4

下面的动图显示了整个Feature Map的计算过程:

以上是卷积步幅等于1的情况,当卷积步幅设置为2时,Feature Map就变成了2*2了。这说明Feature Map的大小是和原来图像大小和步幅有关系。它们满足以下关系:

在上面的两个公式中,W2是卷积后Feature Map的宽度,W1是卷积前图像的宽度,F是卷积核filter的宽度,P是原始图像周围填充0的数量(也就是在原始图像周围补几圈0,如果P是1,那么就是补1圈0,以此类推),S是步幅;H2是卷积后Feature Map的高度,H1是卷积前原始图像的高度。

注:卷积神经网络中的卷积和数学中的卷积是有区别的,卷积神经网络中的卷积严格来说应该叫互相关操作,只是通常我们都习惯叫卷积操作而已。

2、池化层

池化层主要的作用是下采样,通过去掉Feature Map中不重要的样本,进一步减少参数数量。池化的方法很多,最常用的是Max Pooling。Max Pooling实际上就是在n*n的样本中取最大值,作为采样后的样本值。下图是2*2卷积核,步长为2的池化操作:

除了Max Pooling之外,常用的还有Mean Pooling(取均值),对于深度为D的Feature Map,各层独立做池化操作,因此池化后的深度仍为D(池化深度不变,样本数减少)。

3、全连接层

全连接神经网络前面已经详细介绍过了,这里不再赘述。

最后,我们一起来看一个卷积神经网络CNN实现人脸分类的实例。有兴趣的同学可以自己动手试一下。ok,本节课到此。下节课给大家介绍深度学习中常用的激活函数,敬请期待!

代码语言:javascript复制
#coding:utf8
import numpy as np
from sklearn import datasets
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
import  tensorflow.keras as keras
import warnings
warnings.filterwarnings("ignore")

# 导入数据
faces_data = datasets.fetch_olivetti_faces()


# 显示原始图片
i = 0
plt.figure(figsize=(20, 20))
for img in faces_data.images:
    #总共400张图片,每个人10个头像,共40个人
    plt.subplot(20, 20, i 1)
    plt.imshow(img, cmap="gray")
    plt.xticks([])
    plt.yticks([])
    plt.xlabel(faces_data.target[i])
    i = i   1
plt.show()



X = faces_data.images
y = faces_data.target
# 400张图片,每张图片64x64,灰色图片通道数为1
X = X.reshape(400, 64, 64, 1)  

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)



model = keras.Sequential()
# 第一层卷积,卷积核数为128,卷积核3x3,激活函数使用relu,输入是每张原始图片64x64*1
model.add(keras.layers.Conv2D(128, kernel_size=3, activation='relu', input_shape=(64, 64, 1)))
# 第一池化层
model.add(keras.layers.MaxPool2D((2, 2), strides=2))

# 第二层卷积,卷积核数为64,卷积核3x3,激活函数使用relu
model.add(keras.layers.Conv2D(64, kernel_size=3, activation='relu'))
# 第二池化层
model.add(keras.layers.MaxPool2D((2, 2), strides=2))

#把多维数组压缩成一维,里面的操作可以简单理解为reshape,方便后面全连接层使用
model.add(keras.layers.Flatten())
#对应cnn的全连接层,40个人对应40种分类,激活函数使用softmax,进行分类
model.add(keras.layers.Dense(40, activation='softmax'))


model.compile(optimizer='adam',loss='sparse_categorical_crossentropy', metrics=['accuracy'])

# 进行训练和预测
model.fit(X_train, y_train, epochs=10)
y_predict = model.predict(X_test)
# 打印实际标签与预测结果
print(y_test[0], np.argmax(y_predict[0]))

0 人点赞