所用环境为Colab.
定义任务目标
拿到一些手写数字的图片, 每张图片上含有一个手写的数字, 把这些数字识别出来, 划分到10个类别中(从0到9).
数据收集
数据收集
使用 MNIST 数据集.
1
2
3
4
5
6
7
8
9
| # 加载数据集(需联网)
from tensorflow.keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
# 检查数据
train_images.shape, train_labels.shape, test_images.shape, test_labels.shape, train_images.dtype
# ((60000, 28, 28), (60000,), (10000, 28, 28), (10000,), dtype('uint8'))
train_images.ndim # 训练集张量的轴的个数
|
数据可视化
看一下数据集里的图片到底长什么样子, 图片对应的标签是什么.
1
2
3
4
5
6
7
8
9
| import matplotlib.pyplot as plt
# 显示训练集中的第5张图片
digit = train_images[4]
plt.imshow(digit, cmap=plt.cm.binary)
plt.show()
# 查看训练集中的第5张图片的标签
train_labels[4]
|
数据标注
已标注好
数据清理
无需清理
选择模型评估方法
确定简单基准(模型应能超越这个基准)
数字一共有10类(0~9), 如果为手写数字图片随机指定分类, 能分类正确的概率为10%, 因此建立的模型的分类正确率应该超过10%.
评估方法选择
这里选择精度(accuracy)作为模型评估的指标. 精度, 即正确分类的图像所占比例.
怎样用? 构建好模型后, 在模型编译阶段, 将model.compile()
的metrics
参数值设定为["accuracy"]
构建第一个模型
特征选择(过滤没有信息量的特征; 开发新特征)
在本例中特征就是图片的张量值了.
选择架构
两个密集连接层(全连接层).
层1: Dense层, 输出空间的维数units
为512
, 激活函数activation
为relu
.
层2: Dense层, 输出空间的维数units
为10
, 激活函数activation
为softmax
. 这是一个10路的softmax分类层, 它的输出是一个数组, 数组元素为10个概率值(总和为1), 表示图像分别属于10个类别(数字)的概率值.
训练配置(损失函数, 批量大小, 学习率)
优化器 optimizer
这里选rmsprop
损失函数 loss function
这里选sparse_categorical_crossentropy
训练轮数
这里训练5轮.
数据批量大小
批量大小设为128.
模型构建代码
1
2
3
4
5
6
7
8
9
10
11
| from tensorflow import keras
from tensorflow.keras import layers
# 构建模型
model = keras.Sequential([
layers.Dense(512, activation="relu"),
layers.Dense(10, activation="softmax")
])
# 编译模型
model.compile(optimizer="rmsprop", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
|
数据预处理
数据划分: 训练集, 验证集, 测试集
mnist数据集已划分为训练集和测试集. 这里不再划分训练集和验证集.
数据向量化
从前面知训练集的shape为(60000, 28, 28)
, shape为(60000, 28 * 28)
, 即一条记录(一个向量)表示一张图片.
1
2
3
4
| train_images = train_images.reshape((60000, 28 * 28))
test_images = test_images.reshape((10000, 28 * 28))
train_images.shape, test_images.shape
# ((60000, 784), (10000, 784))
|
数据规范化
从前面知训练集的数据类型为uint8
, 将其变换为一个float32
数组.
数据点(相当于图片上的一个像素点的值)的取值范围是0~255
, 把值缩放到0~1
.
1
2
3
4
5
| train_images = train_images.astype("float32") / 255
test_images = test_images.astype("float32") / 255
train_images.dtype, test_images.dtype, train_images.min(), train_images.max()
# (dtype('float32'), dtype('float32'), 0.0, 1.0)
|
处理缺失值
不需要处理缺失值.
拟合模型
1
| history = model.fit(train_images, train_labels, epochs=5, batch_size=128)
|
输出:
Epoch 1/5 469/469 [==============================] - 7s 13ms/step - loss: 0.2651 - accuracy: 0.9239
Epoch 2/5 469/469 [==============================] - 5s 11ms/step - loss: 0.1070 - accuracy: 0.9686
Epoch 3/5 469/469 [==============================] - 5s 10ms/step - loss: 0.0701 - accuracy: 0.9792
Epoch 4/5 469/469 [==============================] - 6s 12ms/step - loss: 0.0510 - accuracy: 0.9844
Epoch 5/5 469/469 [==============================] - 5s 10ms/step - loss: 0.0377 - accuracy: 0.9890
可以看到, 训练了5轮后, 在训练集上的精度达到了0.9890
.
1
2
3
| history.history # 一个字典, 键是"指标"; 值是列表, 指标在每轮训练时的值.
# {'loss': [0.26506927609443665, 0.10701579600572586, 0.07010699808597565, 0.051034048199653625, 0.03765270486474037], 'accuracy': [0.9239166378974915, 0.968583345413208, 0.979200005531311, 0.9843833446502686, 0.9889833331108093]}
|
利用模型进行预测
1
2
3
4
5
6
7
8
9
10
11
| test_digits = test_images[0:10] # 只预测测试集中的前10张图片
predictions = model.predict(test_digits)
predictions[0] # 对第1张图片的预测结果, 数组, 各元素为概率值
predictions[0].argmax() # 第1张图片的预测结果, 最大的元素的索引为7, 表明这张图片的预测结果是7
predictions[0][7] # 索引7对应的元素的值(该图片对应数字是7的概率值)
test_labels[0] # 查看测试集第一张图片的标签, 看预测结果是否和实际结果一样
|
评价模型
前面选择精度(accuracy)作为模型评估的指标, 因此在对模型效果进行评价时, 使用模型在整个测试集上的平均精度.
1
2
3
4
| test_loss, test_acc = model.evaluate(test_images, test_labels)
print(f"测试精度: {test_acc}")
# 313/313 [==============================] - 1s 3ms/step - loss: 0.0642 - accuracy: 0.9804
# 测试精度: 0.980400025844574
|
可以看到, 模型在测试集上的精度为0.9804, 比在训练集上略低.
改进模型
模型在测试集上的精度能达到98%, 已经很不错了, 就暂时不改进了.
部署模型
不部署.