首次接触TensorFlow,使用CNN卷积神经网络识别mnist手写字的数据集

538 阅读7分钟

初步接触应该对这有什么学习目标呢?

  • 了解TensorFlow安装?
  • 了解模型?
  • 了解如何构建?
  • 大致清楚参数含义?
  • 评估模型?
  • 解决实际问题?

了解安装?

安装TensorFlow 使用的是默认的最新版本2.3

pip3 install tensorflow

PS:

1的版本数据集是在tensorflow.examples.tutorials.mnist

2的版本数据集切换到tensorflow.keras.datasets 且API相对简洁了

了解模型?如何构建?

在做这个事情之前, 我简单的了解了CNN神经网络是怎样的

(一个或多个)卷积层+池化层+全连接层

参考了网上的实现方法,构建了一个CNN类,来构建这个模型

class CNN(object):
    def __init__(self):
        # 一层层线性叠加的网络架构 ;按顺序一层一层训练,一层一层往前的那种。没有什么环的结构。比如像前馈网络那样。
        model = models.Sequential()
        # 第一层卷积,卷积核大小为3*3 ,32个,28*28为待训练图片的大小 ;激活函数为relu
        model.add(layers.Conv2D(32,(3,3),activation='relu',input_shape=(28,28,1)))
        # 池化层
        model.add(layers.MaxPooling2D((2,2)))
        # 第二层卷积,卷积核大小为3*3,64个
        model.add(layers.Conv2D(64,(3,3),activation='relu'))
        # 池化层
        model.add(layers.MaxPooling2D((2,2)))
        # 第三层卷积, 卷积核大小为3*3,64个
        model.add(layers.Conv2D(64,(3,3),activation='relu'))
        # Flatten层用来将输入“压平”,即把多维的输入一维化,常用在从卷积层到全连接层的过渡。
        model.add(layers.Flatten())
        # layers.dense ;全连接层  相当于添加一个层
        model.add(layers.Dense(64,activation='relu'))
        # 最后一层的激活函数是softmax,10位恰好可以表达0-9十个数字。
        model.add(layers.Dense(10,activation='softmax'))
        # 通过model.summary()输出模型各层的参数状况
        model.summary()
        self.model = model

大致清楚参数含义?

首次接触,遇到不懂的API或概念,查阅后大致了解如下:

tensorflow.keras.layers —— 代表神经网络里的层

Conv2d —— 原生版本中的tf.nn.conv2d() —— 一个卷积函数

其中Conv2d(32,(3,3),activation='relu',input_shape=(28,28,1))

32 代表的是filters参数,即卷积中的滤波器数,即输出的维度

(3,3)代表的是kernel_size参数,即卷积核大小,指定2D卷积窗口的高度和宽度

activation='relu'代表的是激活函数使用relu函数

此处的relu函数 f(x) = max(0,x)与传统的激活函数相比(如sigmod函数)

  • 更加有效率的梯度下降以及反向传播:避免了梯度爆炸和梯度消失问题
  • 简化计算过程,提高了计算效率

layers.MaxPooling2D()——用于2D输入的最大池化层——其中的参数(2,2)代表pool_size ,用于指定池窗口的大小

池化层的作用又是什么呢?

池化层夹在连续的卷积层中间, 用于压缩数据和参数的量,减小过拟合。

简而言之,如果输入是图像的话,那么池化层的最主要作用就是压缩图像。(所以这里应该就是压缩图像了)

layers.Flatten() —— 把多维的输入一维化,常用在从卷积层到全连接层的过渡

最后一层的激活函数是softmax

因为使用该函数,且unit=10 恰好可以表达0-9十个数字。

查阅了softmax的作用:softmax用于多分类过程中,它将多个神经元的输出,映射到(0,1)区间内,可以看成概率来理解,从而来进行多分类!(所以此处对应的是0-9这10个数字)

model.summary()——输出模型各层的参数状况

如:

Model: "sequential_5"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_10 (Conv2D)           (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d_6 (MaxPooling2 (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_11 (Conv2D)           (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_7 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
conv2d_12 (Conv2D)           (None, 3, 3, 64)          36928     
_________________________________________________________________
flatten_4 (Flatten)          (None, 576)               0         
_________________________________________________________________
dense_8 (Dense)              (None, 64)                36928     
_________________________________________________________________
dense_9 (Dense)              (None, 10)                650       
=================================================================
Total params: 93,322
Trainable params: 93,322

使用该模型进行训练

# 数据集
mnist = tf.keras.datasets.mnist
(x_train,y_train),(x_test,y_test) = mnist.load_data()
x_train = x_train.reshape((60000,28,28,1))
x_test = x_test.reshape((10000,28,28,1))
x_train, x_test = x_train / 255.0 , x_test / 255.0
# 训练
cnn = CNN()
cnn.model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])
cnn.model.fit(x_train,y_train,epochs=20)

输出结果如下(大概训练了20分钟):

Epoch 1/20
1875/1875 [==============================] - 46s 25ms/step - loss: 0.1443 - accuracy: 0.9553
Epoch 2/20
1875/1875 [==============================] - 63s 34ms/step - loss: 0.0468 - accuracy: 0.9858
Epoch 3/20
1875/1875 [==============================] - 69s 37ms/step - loss: 0.0325 - accuracy: 0.9901
Epoch 4/20
1875/1875 [==============================] - 58s 31ms/step - loss: 0.0260 - accuracy: 0.9918
Epoch 5/20
1875/1875 [==============================] - 55s 29ms/step - loss: 0.0203 - accuracy: 0.9936
Epoch 6/20
1875/1875 [==============================] - 59s 31ms/step - loss: 0.0155 - accuracy: 0.9949
Epoch 7/20
1875/1875 [==============================] - 51s 27ms/step - loss: 0.0134 - accuracy: 0.9957
Epoch 8/20
1875/1875 [==============================] - 52s 28ms/step - loss: 0.0113 - accuracy: 0.9961
Epoch 9/20
1875/1875 [==============================] - 53s 28ms/step - loss: 0.0100 - accuracy: 0.9968
Epoch 10/20
1875/1875 [==============================] - 54s 29ms/step - loss: 0.0086 - accuracy: 0.9973
Epoch 11/20
1875/1875 [==============================] - 72s 38ms/step - loss: 0.0079 - accuracy: 0.9976
Epoch 12/20
1875/1875 [==============================] - 66s 35ms/step - loss: 0.0076 - accuracy: 0.9977
Epoch 13/20
1875/1875 [==============================] - 60s 32ms/step - loss: 0.0056 - accuracy: 0.9984
Epoch 14/20
1875/1875 [==============================] - 52s 28ms/step - loss: 0.0076 - accuracy: 0.9977
Epoch 15/20
1875/1875 [==============================] - 47s 25ms/step - loss: 0.0053 - accuracy: 0.9982
Epoch 16/20
1875/1875 [==============================] - 48s 25ms/step - loss: 0.0043 - accuracy: 0.9987
Epoch 17/20
1875/1875 [==============================] - 46s 25ms/step - loss: 0.0069 - accuracy: 0.9983
Epoch 18/20
1875/1875 [==============================] - 46s 25ms/step - loss: 0.0051 - accuracy: 0.9982
Epoch 19/20
1875/1875 [==============================] - 51s 27ms/step - loss: 0.0037 - accuracy: 0.9989
Epoch 20/20
1875/1875 [==============================] - 46s 25ms/step - loss: 0.0057 - accuracy: 0.9982

其中使用的优化器是adam 损失函数是sparse_categorical_crossentropy

之前了解到的只有普通的梯度下降,查阅了此处的adam之后,有了大致了解:

在 keras 中的优化器有 SGD,RMSprop,Adagrad,Adadelta,Adam


所以应如何选择?

如果数据是稀疏的,就用自适用方法,即 Adagrad, Adadelta, RMSprop, Adam。

RMSprop, Adadelta, Adam 在很多情况下的效果是相似的。Adam 就是在 RMSprop 的基础上加了 bias-correction 和 momentum,

随着梯度变的稀疏,Adam 比 RMSprop 效果会好。

整体来讲,Adam 是最好的选择。

很多论文里都会用 SGD,没有 momentum 等。SGD 虽然能达到极小值,但是比其它算法用的时间长,而且可能会被困在鞍点。

如果需要更快的收敛,或者是训练更深更复杂的神经网络,需要用一种自适应的算法。


此处的损失函数sparse_categorical_crossentropy又是什么呢?

是具有整数目标的分类交叉熵——定义在tensorflow/python/keras/backend.py

为什么使用这个?而不是均方差损失函数?

均方误差(MSE)的含义是求一个batch中n个样本的n个输出与期望输出的差的平方的平均值

在训练神经网络过程中,我们通过梯度下降算法来更新w和b,因此需要计算损失函数对w和b的导数,然后更新w、b。

因为sigmoid函数的性质,w和b更新非常慢

为了克服这个不足,引入了categorical_crossentropy(交叉熵损失函数)

交叉熵是用来评估当前训练得到的概率分布与真实分布的差异情况。

它刻画的是实际输出(概率)与期望输出(概率)的距离,也就是交叉熵的值越小,两个概率分布就越接近。


里面的epoch是什么意思呢?

1个epoch等于使用训练集中的全部样本训练一次,通俗的讲epoch的值就是整个数据集被轮几次。


为了防止过拟合,往里加入layers.Dropout(0.2)

意思是防止过拟合随机拿走一些神经元 0.2代表rate参数,意为训练的时候想拿掉多少神经元,按比例计算。0就是没有dropout,1就是整个层都没了

注意:dropout只能用在训练中,测试的时候不能dropout,要用完整的网络测试哦


模型评估?解决实际问题?

model.evaluate(x_test,y_test)

输出结果为

313/313 [==============================] - 0s 1ms/step - loss: 0.0666 - accuracy: 0.9818

为了看看这个模型是不是真的能检测我的手写数字,我写了10个数字,拍了照片

def predict(img_path):
    img = Image.open(img_path).convert('L').resize((28,28))
    flatten_img = np.reshape(img, (28, 28, 1))
    x = np.array([1 - flatten_img])
    x = x / 255.0
    y = cnn.model.predict(x)
    print(img_path)
    print('        -> Predict digit', np.argmax(y[0]))
for i in range(0,10):
    predict('./mnist-t/{}.jpeg'.format(i))

结果如下:

./mnist-t/0.jpeg
        -> Predict digit 9
./mnist-t/1.jpeg
        -> Predict digit 1
./mnist-t/2.jpeg
        -> Predict digit 2
./mnist-t/3.jpeg
        -> Predict digit 3
./mnist-t/4.jpeg
        -> Predict digit 9
./mnist-t/5.jpeg
        -> Predict digit 5
./mnist-t/6.jpeg
        -> Predict digit 6
./mnist-t/7.jpeg
        -> Predict digit 7
./mnist-t/8.jpeg
        -> Predict digit 8
./mnist-t/9.jpeg
        -> Predict digit 9

只错了0和4