机器学习:第四篇 快速构建一个CNN神经网络(识别MNIST手写数字)

本文最后更新于:6 小时前

每当想要学习神经网络的时候,每当想要自己搭建一个神经网络的时候,每当看到别人的神经网络错综复杂就劝退的时候,不如尝试一下搭建一个最简单的CNN试试。或许会有那种搭积木的感觉。

mnist数据集

mnist手写数字数据集是一系列的28x28x1的灰度图片,如图所示:
mnist-2021-06-15

环境搭建

我的环境如下

  • Python 3.7.4
  • Keras 2.2.4
  • Tensorflow 1.13.1

具体的安装办法可以百度一下。

网络结构

我们经常把神经网络比作黑盒模型,那么黑盒就要有一个输入与输出,我们知道MNIST手写数字是一张张28*28像素的图片,那么输出就是对应的0-9的标签数字。

输出层

如果直接输出0-9,那么就会出现数字之间的相关性,比如1后面是2,在生成的神经网络的权重就会带有数字的顺序特征,所以一般的输出采用的是one-hot模式,就是用一个1维的矩阵来表示输出。
例如:
3:[0 0 0 1 0 0 0 0 0 0]
5:[0 0 0 0 0 1 0 0 0 0]
各个输出矩阵之间相互正交,乘积为0。

最后会用一个全连接层来将卷积运算之后的矩阵映射到one-hot上,并使用softmax将输出转换为概率。

输入层

输入层就是一个28x28x1的矩阵,其中矩阵的值是0-1之间的浮点数,表示手写数字的灰度特征。

中间层

中间部分就是卷积层以及池化层,这两个层不断的叠加以及传递数据,卷积层就是用带加权值的卷积核提取图片特征,而池化层的作用则是减小处理后矩阵的输出大小和降低过拟合。

大致的结构就是
卷积->池化->卷积->池化->卷积->全连接
网上抄的图,不完全准确
注:网上抄的图,不完全准确。

具体代码

使用的是Keras深度学习框架,后端是TensorFlow后端。Keras使得神经网络的搭建变得异常简单。

导入数据并处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from keras.datasets import mnist
from keras.layers import Dense, Conv2D, MaxPooling2D, Flatten
from keras.models import Sequential
from keras.utils import to_categorical

(x_train, y_train), (x_test, y_test) = mnist.load_data() #加载mnist数据

x_train = x_train.reshape((-1, 28, 28, 1))
#重新变换数组的shape属性,其中-1表示自动计算大小,这里的-1是指x_train的数量(60000)

x_train = x_train.astype('float32')/255
#将数据转换为32位浮点型,并归一化处理

y_train = to_categorical(y_train)
#to_categorical就是将类别向量转换为二进制(只有0和1)的矩阵类型表示。
#其表现为将原有的类别向量转换为独热编码的形式。

x_test = x_test.reshape((-1, 28, 28, 1))
x_test = x_test.astype('float32')/255
y_test = to_categorical(y_test)
#以上同理

#再将训练集中的前50000个划为训练集,后10000个作为验证集。
x_val = x_train[:10000]
y_val = y_train[:10000]
partial_x_train = x_train[10000:]
partial_y_train = y_train[10000:]

通过导入mnist数据集,并且经过一个简单的处理,得到一系列的28x28x1的归一化矩阵。并将数据集分为训练集、验证集和测试集。

使用Keras搭建网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
model = Sequential()
#声明网络为顺序模型,这种模型是单输入单输出的模式,层之间只有相邻关系,不存在跨层连接
#还有一种模型是Model,多输入多输出,层与层之间任意连接。这种模型编译速度慢。
model.add(Conv2D(32, (3, 3), padding='same', activation='relu', input_shape=(28, 28, 1)))
#添加一个卷积层,padding声明为same,表示矩阵经过卷积之后,形状不变,激活函数为relu
model.add(MaxPooling2D((2, 2)))
#添加一个池化层
model.add(Conv2D(64, (3, 3),padding='same', activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Conv2D(64, (3, 3), padding='same', activation='relu'))
model.add(Flatten())
#添加一个过度层,将卷积层的结果压扁层一个一维矩阵
model.add(Dense(64, activation='relu'))
model.add(Dense(10, activation='softmax'))
#添加全连接层,并通过softmax输出概率
model.summary()
#这个函数是将以上的神经网络输出

model.compile(optimizer='adam',loss='categorical_crossentropy',metrics=['accuracy'])
#optimizer优化器选择adam
#loss损失函数选择categorical_crossentropy

通过简单的添加已经建立连接,一个最简单的CNN神经网络就构建好了。

1
metrics=['accuracy']

是准确率的判别,具体怎么选择可以参考知乎

开始训练

1
2
model.fit(partial_x_train, partial_y_train, validation_data=(x_val, y_val), epochs=5, batch_size=64)
#导入训练集以及验证集,开始训练模型

通过如上方式可以训练出来的一个带加权值的神经网络,将手写数字输入进网络就可以得到结果。
当然,也可以通过model.save(file_path)方式来保存以及通过model = load_model(file_path)加载h5模型。

测试模型

最后,通过测试集来测试评估一下模型的准确性。

1
2
test_loss, test_acc = model.evaluate(x_test, y_test)
print(str(test_acc*100)+"%")

当然,你也可以自己手写一个数字,然后通过将图片处理成28x28的灰度图,通过y_pred = model.predict(X_test,batch_size = 1)来预测你手写的数字是什么。

看一下结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
WARNING:tensorflow:From c:\users\debin\appdata\local\programs\python\python37\lib\site-packages\tensorflow\python\framework\op_def_library.py:263: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_1 (Conv2D) (None, 28, 28, 32) 320
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 14, 14, 32) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 14, 14, 64) 18496
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 7, 7, 64) 0
_________________________________________________________________
conv2d_3 (Conv2D) (None, 7, 7, 64) 36928
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 3, 3, 64) 0
_________________________________________________________________
conv2d_4 (Conv2D) (None, 3, 3, 64) 36928
_________________________________________________________________
flatten_1 (Flatten) (None, 576) 0
_________________________________________________________________
dense_1 (Dense) (None, 64) 36928
_________________________________________________________________
dense_2 (Dense) (None, 10) 650
=================================================================
Total params: 130,250
Trainable params: 130,250
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:From c:\users\debin\appdata\local\programs\python\python37\lib\site-packages\tensorflow\python\ops\math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.cast instead.
Train on 50000 samples, validate on 10000 samples
Epoch 1/5
2021-06-15 19:49:26.050750: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
50000/50000 [==============================] - 113s 2ms/step - loss: 0.1919 - acc: 0.9380 - val_loss: 0.0904 - val_acc: 0.9739
Epoch 2/5
50000/50000 [==============================] - 110s 2ms/step - loss: 0.0540 - acc: 0.9825 - val_loss: 0.0524 - val_acc: 0.9841
Epoch 3/5
50000/50000 [==============================] - 110s 2ms/step - loss: 0.0372 - acc: 0.9880 - val_loss: 0.0491 - val_acc: 0.9851
Epoch 4/5
50000/50000 [==============================] - 108s 2ms/step - loss: 0.0280 - acc: 0.9913 - val_loss: 0.0453 - val_acc: 0.9869
Epoch 5/5
50000/50000 [==============================] - 108s 2ms/step - loss: 0.0211 - acc: 0.9928 - val_loss: 0.0533 - val_acc: 0.9838
10000/10000 [==============================] - 7s 722us/step
accuracy in test data is 98.72999999999999%

额,98.73%,对不起,打扰了。。。。

总结

使用Keras搭建神经网络相对于直接使用TensorFlow来搭建会简单很多,只需要将数据预处理,然后搭建起网络并选择合适的激活函数以及优化器,输出结果就可以了。总的来说还是很轻松的。

太久没更新了,趁端午假期赶紧更新一下,更新完假期刚好结束了。