??本来想着多更新一些关于深度学习的文章,但这方面知识专业度很高,如果作者本身都掌握不好,又怎么能写出好文章分享呢?
??距离第一篇关于深度学习的文章:深度学习笔记1——激活函数,已经过去了9个多月,在沉淀了9个月后,这次写出了第二篇关于深度学习的文章,而且出于快速上手代码编写的目的,这次直接进行手写数字识别的实战,且看下文:
一、准备工作
设备\库 | 型号\版本 |
---|
显卡 | GTX1650 | 驱动程序版本 | 457.49 | tensorflow-gpu版本 | 2.4.0 | keras版本 | 2.4.3 | Python版本 | 3.7.3 |
二、下载MNIST数据集
Keras已经对MNIST数据集做了集成,可以直接通过API下载与使用。
2.1 导入所需的库与模块
from keras.datasets import mnist
import matplotlib.pyplot as plt
2.2 下载数据集
(x_train_original, y_train_original), (x_test_original, y_test_original) = mnist.load_data()
下载好的数据集系统会存放在C盘用户下的.keras中的datasets文件夹下:
C:\Users\Lenovo\.keras\datasets
三、数据集可视化
3.1 单张图像可视化
def mnist_visualize_single(mode, idx):
if mode == 0:
plt.imshow(x_train_original[idx], cmap=plt.get_cmap('gray'))
title = 'label=' + str(y_train_original[idx])
plt.title(title)
plt.xticks([])
plt.yticks([])
plt.show()
else:
plt.imshow(x_test_original[idx], cmap=plt.get_cmap('gray'))
title = 'label=' + str(y_test_original[idx])
plt.title(title)
plt.xticks([])
plt.yticks([])
plt.show()
我们调用这个函数
mnist_visualize_single(mode=0, idx=0)
可以看到,训练数据的第一张图像是5。
3.2 多张图像可视化
def mnist_visualize_multiple(mode, start, end, length, width):
if mode == 0:
for i in range(start, end):
plt.subplot(length, width, 1 + i)
plt.imshow(x_train_original[i], cmap=plt.get_cmap('gray'))
title = 'label=' + str(y_train_original[i])
plt.title(title)
plt.xticks([])
plt.yticks([])
plt.show()
else:
for i in range(start, end):
plt.subplot(length, width, 1 + i)
plt.imshow(x_test_original[i], cmap=plt.get_cmap('gray'))
title = 'label=' + str(y_test_original[i])
plt.title(title)
plt.xticks([])
plt.yticks([])
plt.show()
我们调用这个函数
mnist_visualize_multiple(mode=0, start=0, end=4, length=2, width=2)
可以看到,训练数据的前四张图像分别是5、0、4、1。
3.3 原始数据量可视化
print('训练集图像的尺寸:', x_train_original.shape)
print('训练集标签的尺寸:', y_train_original.shape)
print('测试集图像的尺寸:', x_test_original.shape)
print('测试集标签的尺寸:', y_test_original.shape)
程序运行结果:
训练集图像的尺寸: (60000, 28, 28)
训练集标签的尺寸: (60000,)
测试集图像的尺寸: (10000, 28, 28)
测试集标签的尺寸: (10000,)
从这里可以知道,MNIST数据集是(28×28)的灰度图像
四、数据预处理
4.1 验证集分配
我们从训练集的60000张图像中,分离出10000张图像用作验证集。
x_val = x_train_original[50000:]
y_val = y_train_original[50000:]
x_train = x_train_original[:50000]
y_train = y_train_original[:50000]
print('验证集图像的尺寸:', x_val.shape)
print('验证集标签的尺寸:', y_val.shape)
程序运行结果:
验证集图像的尺寸: (10000, 28, 28)
验证集标签的尺寸: (10000,)
4.2 图像数据预处理
我们需要先将图像转换为四维矩阵用于网络训练,且需要把图像类型从Uint8转化为float32,提高训练精度。
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1).astype('float32')
x_val = x_val.reshape(x_val.shape[0], 28, 28, 1).astype('float32')
x_test = x_test_original.reshape(x_test_original.shape[0], 28, 28, 1).astype('float32')
原始图像数据的像素灰度值范围是0-255,为了提高模型的训练精度,通常将数值归一化至0-1。
x_train = x_train / 255
x_val = x_val / 255
x_test = x_test / 255
我们打印一下数据集传入网络的尺寸:
print('训练集传入网络的图像尺寸:', x_train.shape)
print('验证集传入网络的图像尺寸:', x_val.shape)
print('测试集传入网络的图像尺寸:', x_test.shape)
打印结果为:
训练集传入网络的图像尺寸: (50000, 28, 28, 1)
验证集传入网络的图像尺寸: (10000, 28, 28, 1)
测试集传入网络的图像尺寸: (10000, 28, 28, 1)
五、构建网络
5.1 导入所需的库与模块
import numpy as np
import pandas as pd
from sklearn.metrics import confusion_matrix
import seaborn as sns
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
from keras.utils import np_utils
from keras.utils.vis_utils import plot_model
5.2 定义网络模型
我们使用CNN模型做手写数字的分类,当然也可以使用感知机。
模型的构建采用序贯模型结构,网络由卷积层-池化层-卷积层-池化层-平铺层-全连接层-全连接层组成。最后一层的全连接层采用softmax 激活函数做10分类。
def CNN_model():
model = Sequential()
model.add(Conv2D(filters=16, kernel_size=(5, 5), activation='relu', input_shape=(28, 28, 1)))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Conv2D(filters=32, kernel_size=(5, 5), activation='relu', input_shape=(28, 28, 1)))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Flatten())
model.add(Dense(100, activation='relu'))
model.add(Dense(10, activation='softmax'))
print(model.summary())
return model
model = CNN_model()
5.3 网络模型参数可视化
我们使用print(model.summary()) 得到神经网路的每一层参数。
print(model.summary())
输出结果为:
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param
=================================================================
conv2d (Conv2D) (None, 24, 24, 16) 416
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 12, 12, 16) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 8, 8, 32) 12832
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 4, 4, 32) 0
_________________________________________________________________
flatten (Flatten) (None, 512) 0
_________________________________________________________________
dense (Dense) (None, 100) 51300
_________________________________________________________________
dense_1 (Dense) (None, 10) 1010
=================================================================
Total params: 65,558
Trainable params: 65,558
Non-trainable params: 0
_________________________________________________________________
5.4 网络结构可视化
为了更直观的展示网络的结构,我们可以通过图的方式了解网络各层之间的关系:
plot_model(model, to_file='CNN_model.png', show_shapes=True, show_layer_names=True, rankdir='TB')
plt.figure(figsize=(10, 10))
img = plt.imread('CNN_model.png')
plt.imshow(img)
plt.axis('off')
plt.show()
网络结构图可以更加直观的看出网络各层关系以及张量流的传递路径,此外每个网络层的输入输出尺寸都能很好的体现。
六、编译训练网络
6.1 编译网络
Keras通过model.compile() 编译网络,其可以定义损失函数、优化器、评估指标等参数。
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
这里使用交叉熵categorical_crossentropy 作为损失函数,这是最常用的多分类任务的损失函数,常搭配softmax 激活函数使用。
优化器我们使用自适应矩估计:Adam ,Adam简直不要太好用!!!
评价指标我们使用精度:accuracy 。
6.2 训练网络
Keras可以通过多种函数训练网络,这里我们使用model.fit() 训练网络模型,函数中可以定义训练集数据与训练集标签,验证集数据与验证集标签、训练批次、批处理大小等。
train_history = model.fit(x_train, y_train, validation_data=(x_val, y_val), epochs=20, batch_size=32, verbose=2)
这里返回值train_history 中包含了训练过程的许多信息,例如训练损失和训练精度等。
接下来网络就会开始训练,训练过程如下:
Epoch 1/20
1563/1563 - 9s - loss: 0.1665 - accuracy: 0.9486 - val_loss: 0.0610 - val_accuracy: 0.9821
Epoch 2/20
1563/1563 - 6s - loss: 0.0524 - accuracy: 0.9833 - val_loss: 0.0473 - val_accuracy: 0.9849
Epoch 3/20
1563/1563 - 6s - loss: 0.0353 - accuracy: 0.9891 - val_loss: 0.0416 - val_accuracy: 0.9873
Epoch 4/20
1563/1563 - 6s - loss: 0.0293 - accuracy: 0.9908 - val_loss: 0.0354 - val_accuracy: 0.9900
Epoch 5/20
1563/1563 - 6s - loss: 0.0195 - accuracy: 0.9939 - val_loss: 0.0372 - val_accuracy: 0.9898
Epoch 6/20
1563/1563 - 6s - loss: 0.0180 - accuracy: 0.9940 - val_loss: 0.0416 - val_accuracy: 0.9876
Epoch 7/20
1563/1563 - 6s - loss: 0.0150 - accuracy: 0.9953 - val_loss: 0.0425 - val_accuracy: 0.9884
Epoch 8/20
1563/1563 - 6s - loss: 0.0108 - accuracy: 0.9961 - val_loss: 0.0365 - val_accuracy: 0.9908
Epoch 9/20
1563/1563 - 6s - loss: 0.0120 - accuracy: 0.9957 - val_loss: 0.0406 - val_accuracy: 0.9890
Epoch 10/20
1563/1563 - 6s - loss: 0.0087 - accuracy: 0.9970 - val_loss: 0.0437 - val_accuracy: 0.9893
Epoch 11/20
1563/1563 - 6s - loss: 0.0081 - accuracy: 0.9973 - val_loss: 0.0487 - val_accuracy: 0.9884
Epoch 12/20
1563/1563 - 6s - loss: 0.0067 - accuracy: 0.9978 - val_loss: 0.0470 - val_accuracy: 0.9906
Epoch 13/20
1563/1563 - 6s - loss: 0.0075 - accuracy: 0.9976 - val_loss: 0.0388 - val_accuracy: 0.9905
Epoch 14/20
1563/1563 - 6s - loss: 0.0068 - accuracy: 0.9977 - val_loss: 0.0554 - val_accuracy: 0.9897
Epoch 15/20
1563/1563 - 6s - loss: 0.0056 - accuracy: 0.9981 - val_loss: 0.0512 - val_accuracy: 0.9908
Epoch 16/20
1563/1563 - 6s - loss: 0.0058 - accuracy: 0.9981 - val_loss: 0.0479 - val_accuracy: 0.9914
Epoch 17/20
1563/1563 - 6s - loss: 0.0056 - accuracy: 0.9980 - val_loss: 0.0482 - val_accuracy: 0.9912
Epoch 18/20
1563/1563 - 6s - loss: 0.0057 - accuracy: 0.9982 - val_loss: 0.0528 - val_accuracy: 0.9904
Epoch 19/20
1563/1563 - 6s - loss: 0.0046 - accuracy: 0.9985 - val_loss: 0.0538 - val_accuracy: 0.9895
Epoch 20/20
1563/1563 - 6s - loss: 0.0038 - accuracy: 0.9986 - val_loss: 0.0581 - val_accuracy: 0.9900
6.3 训练过程可视化
我们可以通过图的方式展示神经网路在训练时的损失与精度的变化,我们定义函数:
def show_train_history(train_history, train, validation):
plt.plot(train_history.history[train])
plt.plot(train_history.history[validation])
plt.title('Train History')
plt.ylabel(train)
plt.xlabel('Epoch')
plt.legend(['train', 'validation'], loc='best')
plt.show()
我们调用该函数:
show_train_history(train_history, 'accuracy', 'val_accuracy')
show_train_history(train_history, 'loss', 'val_loss')
上图分别表示网络训练过程中在训练集与验证集上的精度与损失的变化折现图。通过折线图我们可以判断网络是否发生过拟合等情况。
6.4 保存训练好的网络模型
Keras有多种保存网络模型的函数,这里我们使用model.save() 。
model.save('handwritten_numeral_recognition.h5')
.h5模型中包含模型结构、模型权重、模型编译信息等。
七、网络预测
7.1 测试集预测结果
Keras通过函数model.evaluate() 测试神经网路在测试集上的情况。
score = model.evaluate(x_test, y_test)
score中包含了测试集上的损失与精度信息,我们打印一下:
print('Test loss:', score[0])
print('Test accuracy:', score[1])
输出打印结果:
Test loss: 0.0538315623998642
Test accuracy: 0.9905999898910522
从结果我们可知,我们的神经网路在测试集上的精度可以达到99%,说明网络还是不错的。
我们通过model.predict() 对测试集图像进行预测:
predictions = model.predict(x_test)
predictions = np.argmax(predictions, axis=1)
print('前20张图片预测结果:', predictions[:20])
前20张图像的预测结果如下:
前20张图片预测结果: [7 2 1 0 4 1 4 9 5 9 0 6 9 0 1 5 9 7 3 4]
7.2 测试集预测结果图像可视化
我们可以像第三部分那样,构造一个函数可以既显示图片,又能显示图像预测的结果,定义以下函数:
def mnist_visualize_multiple_predict(start, end, length, width):
for i in range(start, end):
plt.subplot(length, width, 1 + i)
plt.imshow(x_test_original[i], cmap=plt.get_cmap('gray'))
title_true = 'true=' + str(y_test_original[i])
title_prediction = ',' + 'prediction' + str(model.predict_classes(np.expand_dims(x_test[i], axis=0)))
title = title_true + title_prediction
plt.title(title)
plt.xticks([])
plt.yticks([])
plt.show()
调用该函数:
mnist_visualize_multiple_predict(start=0, end=9, length=3, width=3)
我们可以得到测试集上图像的真实标签与预测标签。
第9张图像已经很抽象了,网络仍能精准判别出数字是5。
7.3 显示混淆矩阵
通过建立混淆矩阵可以更直观的感知每一种类别的误差。 首先构造混淆矩阵:
cm = confusion_matrix(y_test_original, predictions)
cm = pd.DataFrame(cm)
然后我们构造一个类名:
class_names = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
最后我们定义混淆矩阵可视化函数:
def plot_confusion_matrix(cm):
plt.figure(figsize=(10, 10))
sns.heatmap(cm, cmap='Oranges', linecolor='black', linewidth=1, annot=True, fmt='', xticklabels=class_names, yticklabels=class_names)
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.title("Confusion Matrix")
plt.show()
我们调用这个函数:
plot_confusion_matrix(cm)
可视化结果如图:
八、完整程序
from keras.datasets import mnist
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.metrics import confusion_matrix
import seaborn as sns
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
from keras.utils import np_utils
from keras.utils.vis_utils import plot_model
import tensorflow as tf
import os
import keras
config = tf.compat.v1.ConfigProto()
config.gpu_options.allow_growth = True
sess = tf.compat.v1.Session(config=config)
(x_train_original, y_train_original), (x_test_original, y_test_original) = mnist.load_data()
"""
数据可视化
"""
def mnist_visualize_single(mode, idx):
if mode == 0:
plt.imshow(x_train_original[idx], cmap=plt.get_cmap('gray'))
title = 'label=' + str(y_train_original[idx])
plt.title(title)
plt.xticks([])
plt.yticks([])
plt.show()
else:
plt.imshow(x_test_original[idx], cmap=plt.get_cmap('gray'))
title = 'label=' + str(y_test_original[idx])
plt.title(title)
plt.xticks([])
plt.yticks([])
plt.show()
def mnist_visualize_multiple(mode, start, end, length, width):
if mode == 0:
for i in range(start, end):
plt.subplot(length, width, 1 + i)
plt.imshow(x_train_original[i], cmap=plt.get_cmap('gray'))
title = 'label=' + str(y_train_original[i])
plt.title(title)
plt.xticks([])
plt.yticks([])
plt.show()
else:
for i in range(start, end):
plt.subplot(length, width, 1 + i)
plt.imshow(x_test_original[i], cmap=plt.get_cmap('gray'))
title = 'label=' + str(y_test_original[i])
plt.title(title)
plt.xticks([])
plt.yticks([])
plt.show()
print('训练集图像的尺寸:', x_train_original.shape)
print('训练集标签的尺寸:', y_train_original.shape)
print('测试集图像的尺寸:', x_test_original.shape)
print('测试集标签的尺寸:', y_test_original.shape)
"""
数据预处理
"""
x_val = x_train_original[50000:]
y_val = y_train_original[50000:]
x_train = x_train_original[:50000]
y_train = y_train_original[:50000]
print('验证集图像的尺寸:', x_val.shape)
print('验证集标签的尺寸:', y_val.shape)
print('======================')
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1).astype('float32')
x_val = x_val.reshape(x_val.shape[0], 28, 28, 1).astype('float32')
x_test = x_test_original.reshape(x_test_original.shape[0], 28, 28, 1).astype('float32')
x_train = x_train / 255
x_val = x_val / 255
x_test = x_test / 255
print('训练集传入网络的图像尺寸:', x_train.shape)
print('验证集传入网络的图像尺寸:', x_val.shape)
print('测试集传入网络的图像尺寸:', x_test.shape)
y_train = np_utils.to_categorical(y_train)
y_val = np_utils.to_categorical(y_val)
y_test = np_utils.to_categorical(y_test_original)
"""
定义网络模型
"""
def CNN_model():
model = Sequential()
model.add(Conv2D(filters=16, kernel_size=(5, 5), activation='relu', input_shape=(28, 28, 1)))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Conv2D(filters=32, kernel_size=(5, 5), activation='relu', input_shape=(28, 28, 1)))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Flatten())
model.add(Dense(100, activation='relu'))
model.add(Dense(10, activation='softmax'))
print(model.summary())
return model
"""
训练网络
"""
model = CNN_model()
plot_model(model, to_file='CNN_model.png', show_shapes=True, show_layer_names=True, rankdir='TB')
plt.figure(figsize=(10, 10))
img = plt.imread('CNN_model.png')
plt.imshow(img)
plt.axis('off')
plt.show()
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
train_history = model.fit(x_train, y_train, validation_data=(x_val, y_val), epochs=20, batch_size=32, verbose=2)
model.save('handwritten_numeral_recognition.h5')
def show_train_history(train_history, train, validation):
plt.plot(train_history.history[train])
plt.plot(train_history.history[validation])
plt.title('Train History')
plt.ylabel(train)
plt.xlabel('Epoch')
plt.legend(['train', 'validation'], loc='best')
plt.show()
show_train_history(train_history, 'accuracy', 'val_accuracy')
show_train_history(train_history, 'loss', 'val_loss')
score = model.evaluate(x_test, y_test)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
predictions = model.predict(x_test)
predictions = np.argmax(predictions, axis=1)
print('前20张图片预测结果:', predictions[:20])
def mnist_visualize_multiple_predict(start, end, length, width):
for i in range(start, end):
plt.subplot(length, width, 1 + i)
plt.imshow(x_test_original[i], cmap=plt.get_cmap('gray'))
title_true = 'true=' + str(y_test_original[i])
title_prediction = ',' + 'prediction' + str(model.predict_classes(np.expand_dims(x_test[i], axis=0)))
title = title_true + title_prediction
plt.title(title)
plt.xticks([])
plt.yticks([])
plt.show()
mnist_visualize_multiple_predict(start=0, end=9, length=3, width=3)
cm = confusion_matrix(y_test_original, predictions)
cm = pd.DataFrame(cm)
class_names = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
def plot_confusion_matrix(cm):
plt.figure(figsize=(10, 10))
sns.heatmap(cm, cmap='Oranges', linecolor='black', linewidth=1, annot=True, fmt='', xticklabels=class_names, yticklabels=class_names)
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.title("Confusion Matrix")
plt.show()
plot_confusion_matrix(cm)
九、总结
??到此,深度学习的第二篇博文就完成了,这也是我写的第一个keras实战博文,从构思到跑程序再到完成这篇博文用了整整4个小时,肩膀已经巨酸,要去休息加干饭。
??其实有很多知识点限于篇幅原因没有详细介绍。例如Adam优化器为什么常用?为什么使用独热编码等,如果有不懂的小伙伴可以私聊我,或者有机会我会继续介绍相关知识。
??博主水平有限,如果有不对的地方还望多多指教,最后希望这篇文章可以帮助到任何一位想进入深度学习的伙伴!!!
|