>- **🍨 本文为[🔗365天深度学习训练营](https://mp.weixin.qq.com/s/rbOOmire8OocQ90QM78DRA) 中的学习记录博客**
>- **🍖 原作者:[K同学啊 | 接辅导、项目定制](https://mtyjkh.blog.csdn.net/)**
我的环境:
- 系统环境:WSL2+Ubuntu22.04
- 语言环境:Python3.8.18
- 编译器:vscode+jupyter notebook
- 深度学习环境:TensorFlow2.10.0
T3:天气识别
🍺要求:
- 本地读取并加载数据。(完成)
- 测试集accuracy到达93% (最终达到96.44%,完成)
🍻拔高:
- 测试集accuracy到达95%(最终达到96.44%,完成)
- 调用模型识别一张本地图片(完成)
prefetch()
本期介绍了一个应该挺有用的函数:prefetch()
正常的训练进程是:
cpu准备数据批1→gpu训练数据批1→cpu准备数据批2→gpu训练数据批2→C3→G3
在一方工作的时候另一方处于闲置状态,浪费了大量的时间。
prefetch()的作用是将准备数据和训练数据的行为并行进行:
cpu准备数据批1→gpu训练数据批1同时cpu准备数据批2→gpu训练数据批2同时cpu准备数据批3→C3G4→C4、G5
理论上最高可以做到省略50%的训练时间。
测试训练时间:
Epoch 10/10
29/29 [==============================] - 1s 18ms/step - loss: 0.1480 - accuracy: 0.9433 - val_loss: 0.4325 - val_accuracy: 0.8267
取消prefetch()函数的参与:
train_ds = train_ds.cache().shuffle(1000)
val_ds = val_ds.cache()
发现训练速度为:
Epoch 10/10
29/29 [==============================] - 0s 17ms/step - loss: 0.0949 - accuracy: 0.9678 - val_loss: 0.4347 - val_accuracy: 0.8800
测试,使用了并行反而慢了1ms。
分析原因可能是因为该数据集过小,函数进行任务分配的时间>直接用GPU进行数据准备的时间
所以该函数适合在大批次量的数据集、复杂模型上使用,在本次作业中不适合
模型优化1(无效):
周期调至50,观察训练记录:
训练集准确率趋近1,验证集准确率却低于90%
老问题,过拟合。
尝试降低模型复杂度:正则化、增加dropout层、减少卷积层都无改善,验证准确率始终低于90%。
优化2(升至92.44%):
重新分析数据集特征,意识到训练样本数只有900,样本数过少
恢复回初始模型,采用数据增强的方式来获得更多样本:
data_augmentation = tf.keras.Sequential([
layers.experimental.preprocessing.RandomRotation(factor=0.2)
])
augmented_train_ds = train_ds.map(lambda x, y: (data_augmentation(x, training=True), y))
epochs = 200
history = model.fit(
augmented_train_ds,
validation_data=val_ds,
epochs=epochs
)
该函数的作用是在每次训练周期开始时将输入图像随机旋转<20°
使得模型获得各种偏移的输入图像
同时考虑到因为输入图像的增强,模型更难获得规律,将训练周期增加为200
训练记录如下:
Epoch 200/200
2023-12-06 22:00:01.735523: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:114] Plugin optimizer for device_type GPU is enabled.
29/29 [==============================] - 2s 61ms/step - loss: 0.0015 - accuracy: 1.0000 - val_loss: 0.5383 - val_accuracy: 0.9244
训练集准确率趋近1,验证集准确率升至0.9244
优化3(失败):
模型仍有轻度过拟合,尝试少量减少模型复杂度:
在2的基础上,增加第二个dropout层
model = models.Sequential([
layers.experimental.preprocessing.Rescaling(1./255, input_shape=(img_height, img_width, 3)),
layers.Conv2D(16, (3, 3), activation='relu', input_shape=(img_height, img_width, 3)),
layers.AveragePooling2D((2, 2)),
layers.Conv2D(32, (3, 3), activation='relu'),
layers.AveragePooling2D((2, 2)),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.Dropout(0.3),
layers.Flatten(),
layers.Dropout(0.3), #增加第二个dropout层
layers.Dense(128, activation='relu'),
layers.Dense(num_classes)
])
代码重新运行,结果:
训练集准确率趋近1,测试集准确率反而下降。
优化4(升至95.11%):
重新分析图像,发现模型准确率上升波动非常大。
在3的基础上尝试缩小优化器步长:
# 设置优化器
opt = tf.keras.optimizers.Adam(learning_rate=0.0001)
同时为模型设置早停函数:
class CustomCallback(tf.keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs=None):
# 获取验证集准确率
val_accuracy = logs.get('val_accuracy')
# 添加条件判断,如果验证集准确率大于0.95,则停止训练
if val_accuracy > 0.95:
print("Validation accuracy > 0.95. Stopping training.")
self.model.stop_training = True
# 创建自定义回调实例
custom_callback = CustomCallback()
epochs = 200
history = model.fit(
augmented_train_ds,
validation_data=val_ds,
epochs=epochs,
callbacks=[custom_callback]
)
代码重新运行,结果如下:
Epoch 106/200
1/29 [>.............................] - ETA: 1s - loss: 0.0332 - accuracy: 1.0000
2023-12-06 21:42:21.155474: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:114] Plugin optimizer for device_type GPU is enabled.
28/29 [===========================>..] - ETA: 0s - loss: 0.0594 - accuracy: 0.9777 Validation Accuracy at epoch 106: 0.9511111378669739
Validation accuracy > 0.95. Stopping training.
29/29 [==============================] - 2s 58ms/step - loss: 0.0594 - accuracy: 0.9778 - val_loss: 0.2358 - val_accuracy: 0.9511
成功跳出,最终验证集准确率0.9511,完成目标。
优化5(升至96.00%):
观察图像,发现上升仍有少量波动,且还有上升空间。
在4的基础上再次缩小优化器步长:
# 设置优化器
opt = tf.keras.optimizers.Adam(learning_rate=0.00005)
增加周期数为1000,上调早停准确率0.96:
augmented_train_ds = train_ds.map(lambda x, y: (data_augmentation(x, training=True), y))
class CustomCallback(tf.keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs=None):
# 获取验证集准确率
val_accuracy = logs.get('val_accuracy')
# 添加条件判断,如果验证集准确率大于0.96,则停止训练
if val_accuracy > 0.96:
print("Validation accuracy > 0.96. Stopping training.")
self.model.stop_training = True
# 创建自定义回调实例
custom_callback = CustomCallback()
epochs = 1000
history = model.fit(
augmented_train_ds,
validation_data=val_ds,
epochs=epochs,
callbacks=[custom_callback]
)
代码重新运行,训练结果为:
Epoch 176/1000
1/29 [>.............................] - ETA: 1s - loss: 0.0114 - accuracy: 1.0000
2023-12-06 22:32:37.179504: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:114] Plugin optimizer for device_type GPU is enabled.
29/29 [==============================] - ETA: 0s - loss: 0.0238 - accuracy: 0.9944Validation accuracy > 0.96. Stopping training.
29/29 [==============================] - 2s 60ms/step - loss: 0.0238 - accuracy: 0.9944 - val_loss: 0.2558 - val_accuracy: 0.9600
最终达到(96.00%)
优化6(最高96.44%):
准确率上升仍有轻微波动
在5的基础上再次缩小优化器步长:
# 设置优化器
opt = tf.keras.optimizers.Adam(learning_rate=0.00001)
上调早停准确率至0.965:
if val_accuracy > 0.965:
print("Validation accuracy > 0.965. Stopping training.")
self.model.stop_training = True
对0.9600附近进行进一步探索
于是代码自优化器设置单元格开始继续运行,最终未能出现0.965以上的模型,观察训练记录,准确率最高为:
Epoch 45/1000
1/29 [>.............................] - ETA: 1s - loss: 0.0016 - accuracy: 1.0000
2023-12-06 23:13:19.872240: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:114] Plugin optimizer for device_type GPU is enabled.
29/29 [==============================] - 2s 60ms/step - loss: 0.0069 - accuracy: 0.9989 - val_loss: 0.3044 - val_accuracy: 0.9644
最高96.44%的准确率
可以通过在模型中记录每个周期的模型,采用回溯的方法找回这个模型
然后用再次调低优化器步长的方式做进一步拟合
介于时间问题,不做后续扩展
调用模型识别一张本地图片:
模型调用识别代码:
import numpy as np
from tensorflow.keras.preprocessing import image
# 从本地读取图片
img_path = "/home/wjh/CNN/练习/T3天气识别/QQ图片20231206230208.jpg" # 替换为你的图片路径
img = image.load_img(img_path, target_size=(img_height, img_width))
img_array = image.img_to_array(img)
img_array = np.expand_dims(img_array, axis=0) # 添加一个维度,以符合模型输入的要求
# 预处理图像
img_array = np.vstack([img_array]) # 添加一个维度,以符合模型输入的要求
# 使用模型进行预测
predictions = model.predict(img_array)
# 获取预测结果
predicted_class = np.argmax(predictions[0])
predicted_class_name = class_names[predicted_class]
# 打印结果
print(f"Predicted class and class_name: {predicted_class} {predicted_class_name}")
输出为:
1/1 [==============================] - 0s 21ms/step
Predicted class and class_name: 3 sunrise
成功