>- **🍨 本文为[🔗365天深度学习训练营](https://mp.weixin.qq.com/s/Nb93582M_5usednAKp_Jtw) 中的学习记录博客**
>- **🍖 原作者:[K同学啊 | 接辅导、项目定制](https://mtyjkh.blog.csdn.net/)**
>- **🚀 文章来源:[K同学的学习圈子](https://www.yuque.com/mingtian-fkmxf/zxwb45)**
一、代码环境:
- python3.8
- tensorflow==2.3.3(cpu)
- pycharm编辑
二、数据准备
此次采用的手写体数字数据集为tensorflow内部提供的数据集,只需从tensorflow导入datasets,而后加载数据集即可datasets.mnist.load_data(),就会自动下载数据集。
在这里,对数据做了进一步的处理:
- 每个数据进行归一化处理
- 简化计算:通过归一化,将有量纲的表达式转换为无量纲的纯量,避免不同物理意义和量纲的输入变量不能平等使用,从而简化计算过程。
- 提高模型准确性:归一化后的图像可以使得模型更容易学习到图像的特征,从而提高模型的准确性。
- 抵抗几何变换的攻击:图像归一化可以找出图像中的不变量,从而得知这些图像原本就是一样的或者一个系列的。在经历处理或攻击后,经过相同参数的图像归一化处理后能够得到相同形式的标准图像。
- 每个样本数据进行reshape,原始数据为(6000, 28, 28)这里增加一维通道信息。
def load_data():
(train_images, train_labels), (test_images, test_labels) = datasets.mnist.load_data()
train_images, test_images = train_images / 255.0, test_images / 255.0
train_images = train_images.reshape((60000, 28, 28, 1))
test_images = test_images.reshape((10000, 28, 28, 1))
# 查看数据维数信息
print("训练集Train数据的维度信息 ===> ", train_images.shape)
print("验证集Val数据的维度信息 ===> ", test_images.shape)
print("训练集Train标签的维度信息 ===> ", train_labels.shape)
print("验证集Val标签的维度信息 ===> ", test_labels.shape)
return train_images, train_labels, test_images, test_labels
查看下载好的手写体数据集:
def plt_show(train_images, train_labels):
plt.figure(figsize=(20,10))
for i in range(20):
plt.subplot(2,10,i+1)
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(train_images[i], cmap=plt.cm.binary)
plt.xlabel(train_labels[i])
plt.savefig("show.jpg")
在下面就能看见所选数据集的部分数据样本:
三、搭建网络模型(LeNet-5)
lenet-5主要包含7层:
- 输入层:32x32x1
- 第一层卷积层:
- 卷积核大小:3x3
- 卷积核个数:32
- 输出特征图:28x28
- 神经元数量:28x28x6
- 激活函数:ReLU
- 第二层池化层:
- 进行2x2采样,每四个区域进行矩阵内积操作,得到一个1/4的特征图
- 第三层卷积层:
- 卷积核大小:3x3
- 卷积核个数:64
- 激活函数:ReLU
- 第四层池化层:
- 进行2x2采样,每四个区域进行矩阵内积操作,得到一个1/4的特征图
- Flatten层:
- 连接卷积层与全连接层
- Dense层:全连接层,特征进一步提取,64为输出空间的维数
- Dense层:输出层,输出预期结果,10为输出空间的维数
def model_load():
# 创建并设置卷积神经网络
# 卷积层:通过卷积操作对输入图像进行降维和特征抽取
# 池化层:是一种非线性形式的下采样。主要用于特征降维,压缩数据和参数的数量,减小过拟合,同时提高模型的鲁棒性。
# 全连接层:在经过几个卷积和池化层之后,神经网络中的高级推理通过全连接层来完成。
model = models.Sequential([
# 设置二维卷积层1,设置32个3*3卷积核,activation参数将激活函数设置为ReLu函数,input_shape参数将图层的输入形状设置为(28, 28, 1)
# ReLu函数作为激活励函数可以增强判定函数和整个神经网络的非线性特性,而本身并不会改变卷积层
# 相比其它函数来说,ReLU函数更受青睐,这是因为它可以将神经网络的训练速度提升数倍,而并不会对模型的泛化准确度造成显著影响。
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
# 池化层1,2*2采样
layers.MaxPooling2D((2, 2)),
# 设置二维卷积层2,设置64个3*3卷积核,activation参数将激活函数设置为ReLu函数
layers.Conv2D(64, (3, 3), activation='relu'),
# 池化层2,2*2采样
layers.MaxPooling2D((2, 2)),
layers.Flatten(), # Flatten层,连接卷积层与全连接层
layers.Dense(64, activation='relu'), # 全连接层,特征进一步提取,64为输出空间的维数,activation参数将激活函数设置为ReLu函数
layers.Dense(10) # 输出层,输出预期结果,10为输出空间的维数
])
# 打印网络结构
model.summary()
return model
查看模型的输出结果:
模型构建完成即可开始训练:
def fit_model(model):
train_images, train_labels, test_images, test_labels = load_data()
model.compile(
optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
history = model.fit(
train_images,
train_labels,
epochs=150,
validation_data=(test_images, test_labels))
model_name = f"mnist_.h5"
model_path = os.path.join("./checkpoints", model_name)
if not os.path.exists("./checkpoints"): os.mkdir("./checkpoints")
model.save(model_path)
print("Model Save to %s" % model_path)
上述代码,将训练完成的参数保存为*.h5文件,便于后期的模型的检验与预测。
在这里可以看到随着训练轮次的增加,验证集上的效果越来越好。
四、模型预测结果分析
model_path = "./checkpoints/mnist_.h5"
mnist_model = load_model(model_path)
print(test_images[0].shape)
test_img = np.expand_dims(test_images[0],0)
p = mnist_model.predict(test_img)
labels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(labels[np.argmax(p)])
print(test_labels[0])
加载之前保存的模型文件,由于模型输入为4维,所以需增加一个维度,通过索引类别的最大值找到对应的序列id,而后对labels进行索引,得到预测的数字,在输出真实的label,发现预测正确,都为7.