—简述
这是一个简单的神经网络示例,使用python的Keras库来训练手写数字分类。对于初学者来说,可能无法立刻搞懂这个示例的全部内容。但是没关系,下面我将会详细说明示例中每个步骤。
MNIST问题也被看做是深度学习的“Hello world”,对于零基础想学习深度学习的初学者来说是一个不错的选择,之所以选择keras,是因为它具有以下重要特性:
- 相同的代码可以在CPU或GPU上无缝切换运行
- 具有用户友好的API,便于快速开发深度学习模型的原型。
- 内置支持卷积网络(用于计算机视觉)、循环网络(用于序列处理)以及二者的任意组合。
- 支持任意网络架构:多输入或多输出模型、层共享、模型共享等。这也就是说,Keras能够构建任意深度学习模型,无论是生成式对抗网络还是神经图灵机。
言归正传,下面给出具体实例过程:
1、加载MNIST数据集
MNIST数据集是由 60000 张训练图像和 10000 张测试图像组成,图像大小是 28 x 28 的灰度图像,已经预先加载在了Keras库中,其中包括4个Numpy数组。
# 加载Keras中的MNIST数据集
from keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
其中 train_images 和 train_labels 组成训练集(training set),模型将从此数据集进行学习,test_images 和 test_labels 一起组成测试集,用于对模型进行测试。
也可以将上述数据集进行打印,查看具体内容,
如
>> train_image.shape
(6000, 28, 28)
>> train_labels
array([5, 0, 4, ..., 6, 8], dtype=uint8)
2、图像数据预处理
要对加载的数据进行预处理,以适应网络要求的形状,并将所有值缩放到[ 0, 1] 之间,由于我们训练的图像是 28 x 28 的灰度图,被保存在 uint8 类型的数组中,也就是值的范围在 [0, 255] 之间,形状为 (60000, 28, 28),所以最后要转换为一个 float32 数组, 其形状变为 (60000, 28 * 28),取值范围为 0~1。
# 准备图像数据
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype('float32') / 255
test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype('float32') / 255
# 准备标签,需要对标签进行分类编码转换成二进制格式
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)
# 为了验证训练的模型,此处从训练集中取出 1/3 的数据应于验证训练的模型
x_val = train_images[:20000]
partial_x_train = train_images[20000:]
y_val = train_labels[:20000]
partial_y_train = train_labels[20000:]
3、模型定义
下面是构建网络模型的过程,模块 layers 可以看作是构建网络层的工具,模块 models 可以看作是创建网络模型的一个容器,可以把创建的网络层加入到这个容器中。
# 导入数据处理模块,可以看作
from keras import models
from keras import layers
# 一个空的网络结构
model = models.Sequential()
# 添加两个全连接层
model.add(layers.Dense(512, activation='relu', input_shape=(28*28,)))
model.add(layers.Dense(10, activation='softmax'))
4、编译模型
代码很短只需一行,但需要设置三个参数,其中
- loss: 所代表的是损失函数,使网络模型训练数据朝向正确的方向前进。
- optimizer: 基于训练数据和损失函数来更新网络的机制。
- metric: 在训练和测试过程中需要监控的指标,本例只关心精度,即正确分类的图像所占的比例。
# 设置训练网络的必要参数,如果要用到其他优化器还需要导入模块 optimizers
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
5、训练模型
也就是在训练数据上拟合模型,其中前两个参数为训练数据,即,包含图像和标签的数据。
- epochs 为迭代次数,即训练整个数据集要迭代的次数,可根据机器性能设置参数,详细调参后续文章会叙述。
- batch_size 表示每次训练数据按此批量大小进行训练,也可以是其它参数,但是最好取 2 的整数倍。
- validation_data 为验证模型的数据集,即验证集。
history = model.fit(partial_x_train,
partial_y_train,
epochs=20,
batch_size=128,
validation_data=(x_val, y_val))
# 评估模型,即在测试集上的性能
test_loss, test_acc = model.evaluate(test_images, test_labels)
可以对上述数据进行打印,如
>>print('test_acc:', test_acc)
test_acc: 0.9809
>>history_dict = history.history
>>history_dict.keys()
dict_keys(['val_loss', 'val_acc', 'loss', 'acc'])
部分训练过程如下:
6、绘制训练结果
- 绘制训练损失与验证损失的训练结果
import matplotlib.pyplot as plt
# 纵坐标,所需绘制的数据
history_dict = history.history
loss_values = history_dict['loss']
val_loss_values = history_dict['val_loss']
# 横坐标,步长
epochs = range(1, len(loss_values) + 1)
# 绘制图像
plt.plot(epochs, loss_values, 'bo', label='Training loss')
plt.plot(epochs, val_loss_values, 'b', label='Validation loss')
# 标题
plt.title('Training and validation loss')
# 横、纵坐标标签
plt.xlabel('Epochs')
plt.ylabel('Loss')
# 自适应标签的位置
plt.legend()
# 显示图像
plt.show()
结果如下:
- 绘制训练精度与验证精度的训练结果
# 清除图像
plt.clf()
acc = history_dict['acc']
val_acc = history_dict['val_acc']
plt.plot(epochs, val_acc, 'bo', label='Training acc')
plt.plot(epochs, acc, 'b', label='Validation acc')
plt.title('Training and validation acc')
plt.xlabel('Epochs')
plt.ylabel('Acc')
plt.legend()
plt.show()
结果如下:
7、预测图像
现在我们来预测未训练的图像(测试集)并显示出来,首先重新加载 MNIST 数据集,因为前面过程预处理使得图像数据格式变化。
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
predictions = model.predict(test_images)
可以将数据打印出来,得到是一个张量的形式,每个向量中包含有 10 数据,代表是 0~9 某个数字的概率。结果如下
>>print(predictions)
[[8.0789969e-15 3.0667143e-20 4.9133229e-13 ... 1.0000000e+00
7.1623703e-16 6.8349046e-12]
[6.9563542e-22 5.9805968e-16 1.0000000e+00 ... 0.0000000e+00
2.3937719e-17 2.0886891e-33]
[1.8092266e-13 9.9999917e-01 2.1047970e-09 ... 1.1447231e-07
7.2424598e-07 5.0652759e-12]
...
[1.4542224e-27 4.8825887e-20 6.9998436e-24 ... 4.1665107e-14
2.3709947e-12 7.7242823e-11]
[3.8461803e-22 6.4347756e-23 1.8406260e-26 ... 6.4342509e-26
4.2752926e-11 2.6701102e-24]
[1.3350630e-23 2.6857994e-33 6.6915423e-23 ... 9.5699816e-33
9.6743583e-28 1.2382327e-28]]
>> import numpy as np
>> np.argmax(predictions[10])
0
上述测试了测试集 test_images 中第10个图像的数字是0,为了验证是不是准确的我们将 test_images 中
第10个图像打印出来,结果如下
>> tes = test_images[10]
>> plt.imshow(tes, cmap=plt.cm.binary)
>> plt.show()
结果显示我们预测的结果是准确的。
完整代码如下:
from keras.datasets import mnist
from keras import models
from keras import layers
from keras.utils import to_categorical
import matplotlib.pyplot as plt
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype('float32') / 255
test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype('float32') / 255
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)
x_val = train_images[:20000]
partial_x_train = train_images[20000:]
y_val = train_labels[:20000]
partial_y_train = train_labels[20000:]
network = models.Sequential()
network.add(layers.Dense(512, activation='relu', input_shape=(28*28,)))
network.add(layers.Dense(10, activation='softmax'))
network.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
history = network.fit(partial_x_train, partial_y_train, epochs=20, batch_size=128, validation_data=(x_val, y_val))
test_loss, test_acc = network.evaluate(test_images, test_labels)
# 纵坐标,所需绘制的数据
history_dict = history.history
loss_values = history_dict['loss']
val_loss_values = history_dict['val_loss']
# 横坐标,步长
epochs = range(1, len(loss_values) + 1)
# 绘制图像
plt.plot(epochs, loss_values, 'bo', label='Training loss')
plt.plot(epochs, val_loss_values, 'b', label='Validation loss')
# 标题
plt.title('Training and validation loss')
# 横、纵坐标标签
plt.xlabel('Epochs')
plt.ylabel('Loss')
# 自适应标签的位置
plt.legend()
# 显示图像
plt.show()