- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
目标:
搭建CNN网络模型实现多云、下雨、晴、日出四种天气状态的识别,并用真实天气做预测
具体实现:
(一)环境:
语言环境:Python 3.10
编 译 器: PyCharm
框 架: Tensorflow
**(二)具体步骤:
1. 使用GPU
之前的GPU一直没检测出来 ,今天参考windows安装tensorflow-gpu / CUDA / cuDNN - 前端大兵 - 博客园和 Build from source on Windows | TensorFlow,主要的就是保证python版本、cuda版本, cuDNN以及Tensorflow版本一致。我的Python版本是3.8,cuda安装的是11.2,查看如下:
那么cudnn和tensowflow-gpu的版本也找到了,直接下载安装就成。看成果:
import os
import PIL
import pathlib
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, models
gpus = tf.config.list_physical_devices("GPU")
print(gpus)
if gpus:
gpu0 = gpus[0]
# print(gpu0)
tf.config.experimental.set_memory_growth(gpu0, True)
tf.config.set_visible_devices([gpu0], "GPU")
解决。
2.查看数据
在工程根目录下创建了一个datasets目录,然后下面有一个weather_photos的子目录,再下面分为cloudy(多云)、rain(雨天)、shine(晴天)和sunrise(日出)4种天气类型。
# 导入数据集
data_dir = "datasets/weather_photos/"
data_dir = pathlib.Path(data_dir) # 将目录转换成pathlib.Path类对象,以便使用Path的各种方法
print(data_dir)
# 查看数据
# 查看目录下所在.jpg文件的数据。glob方法是按.jpg去匹配所有文件。要能够使用glob方法,需要上当是pathlib.Path类的对象。
image_count = len(list(data_dir.glob('*/*.jpg')))
print("图片总数为:", image_count)
# 看一下shine目录下的图片
shine = list(data_dir.glob('sunrise/*.jpg'))
print("shine目录共有:%d 个图片" % len(shine))
im = PIL.Image.open(str(shine[0]))
im.show()
3.加载数据
使用image_dataset_from_directory方法将文件目录中的数据加载到tf.data.Dataset中.
# 加载数据
batch_size = 32
image_height = 180
image_width = 180
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
data_dir, # 文件所在目录
validation_split=0.2,
subset="training",
seed=123,
image_size=(image_height, image_width),
batch_size=batch_size
)
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
data_dir,
validation_split=0.2,
subset="validation",
seed=123,
image_size=(image_height, image_width),
batch_size=batch_size
)
#print(train_ds, val_ds)
class_names = train_ds.class_names
print(class_names)
4.配置数据集
# 预提取数据,加速运行
AUTOTUNE = tf.data.AUTOTUNE
# cache():将数据加载到内存中,加速运行
# shuffle():打乱数据,参考:https://zhuanlan.zhihu.com/p/42417456
# prefetch():预取数据,加速运行
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
5.构建CNN网络
# 构建CNN网络
num_classes = 4
model = models.Sequential([
layers.experimental.preprocessing.Rescaling(1./255, input_shape=(image_height, image_width, 3)),
layers.Conv2D(16, (3, 3), activation='relu', input_shape=(image_height, image_width, 3)),
layers.AveragePooling2D((2, 2)), # 池化层1,2*2采样
layers.Conv2D(32, (3, 3), activation='relu'), # 卷积层2,卷积核3*3
layers.AveragePooling2D((2, 2)), # 池化层2,2*2采样
layers.Conv2D(64, (3, 3), activation='relu'), # 卷积层3,卷积核3*3
layers.Dropout(0.3), # 让神经元以一定的概率停止工作,防止过拟合,提高模型的泛化能力。
layers.Flatten(), # Flatten层,连接卷积层与全连接层
layers.Dense(128, activation='relu'), # 全连接层,特征进一步提取
layers.Dense(num_classes) # 输出层,输出预期结果
])
print(model.summary())
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
rescaling (Rescaling) (None, 180, 180, 3) 0
conv2d (Conv2D) (None, 178, 178, 16) 448
average_pooling2d (AverageP (None, 89, 89, 16) 0
ooling2D)
conv2d_1 (Conv2D) (None, 87, 87, 32) 4640
average_pooling2d_1 (Averag (None, 43, 43, 32) 0
ePooling2D)
conv2d_2 (Conv2D) (None, 41, 41, 64) 18496
dropout (Dropout) (None, 41, 41, 64) 0
flatten (Flatten) (None, 107584) 0
dense (Dense) (None, 128) 13770880
dense_1 (Dense) (None, 4) 516
=================================================================
Total params: 13,794,980
Trainable params: 13,794,980
Non-trainable params: 0
_________________________________________________________________
6.编译
# 编译
# 设置优化器
opt = tf.keras.optimizers.Adam(learning_rate=0.001)
# 设置损失函数
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
model.compile(optimizer=opt,
loss=loss,
metrics=['accuracy'])
7.训练模型
# 训练模型
epochs = 10
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=epochs
)
Epoch 1/10
29/29 [==============================] - 7s 34ms/step - loss: 1.3898 - accuracy: 0.5567 - val_loss: 0.7499 - val_accuracy: 0.6311
Epoch 2/10
29/29 [==============================] - 0s 17ms/step - loss: 0.4811 - accuracy: 0.8222 - val_loss: 0.4955 - val_accuracy: 0.8089
Epoch 3/10
29/29 [==============================] - 1s 18ms/step - loss: 0.3696 - accuracy: 0.8644 - val_loss: 0.4990 - val_accuracy: 0.8044
Epoch 4/10
29/29 [==============================] - 0s 17ms/step - loss: 0.2541 - accuracy: 0.8967 - val_loss: 0.5235 - val_accuracy: 0.8089
Epoch 5/10
29/29 [==============================] - 0s 17ms/step - loss: 0.3015 - accuracy: 0.8911 - val_loss: 0.5760 - val_accuracy: 0.7822
Epoch 6/10
29/29 [==============================] - 0s 17ms/step - loss: 0.2394 - accuracy: 0.9122 - val_loss: 0.6048 - val_accuracy: 0.8044
Epoch 7/10
29/29 [==============================] - 0s 17ms/step - loss: 0.2118 - accuracy: 0.9311 - val_loss: 0.4987 - val_accuracy: 0.8000
Epoch 8/10
29/29 [==============================] - 0s 17ms/step - loss: 0.1307 - accuracy: 0.9544 - val_loss: 0.4711 - val_accuracy: 0.8311
Epoch 9/10
29/29 [==============================] - 0s 17ms/step - loss: 0.0958 - accuracy: 0.9667 - val_loss: 0.6813 - val_accuracy: 0.8000
Epoch 10/10
29/29 [==============================] - 0s 17ms/step - loss: 0.1063 - accuracy: 0.9678 - val_loss: 0.4737 - val_accuracy: 0.8489
8.模型评估
# 模型评估
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs_range = range(epochs)
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
9.数据增强
从上面的模型评估上看,训练准确率远高于验证准确率。是不是就是传说中的过拟合现象,查阅资料发现,过拟合的情况有很多,其中数据量不足也是其中一种原因,可以通过数据增加的方式,将原有的数据变换不同的形式加入到数据集中。比如一张图片,通过放大,缩小、旋转等数据增强,一同加入训练(加入训练的过程是自动的,不用额外处理,只需要处理怎么做数据增强就行)。参考: 数据增强 | TensorFlow Core
# 数据增强
data_augmentation = keras.Sequential([
layers.RandomFlip("horizontal", input_shape=(image_height, image_width, 3)), # 随机翻转
layers.RandomRotation(0.1), # 随机旋转
layers.RandomZoom(0.1), # 随机缩放
])
应用数据增强,直接加到model的Sequential里面:
model = models.Sequential([
data_augmentation, # 数据增强
layers.experimental.preprocessing.Rescaling(1./255, input_shape=(image_height, image_width, 3)),
layers.Conv2D(16, (3, 3), activation='relu', input_shape=(image_height, image_width, 3)),
layers.AveragePooling2D((2, 2)), # 池化层1,2*2采样
layers.Conv2D(32, (3, 3), activation='relu'), # 卷积层2,卷积核3*3
layers.AveragePooling2D((2, 2)), # 池化层2,2*2采样
layers.Conv2D(64, (3, 3), activation='relu'), # 卷积层3,卷积核3*3
layers.Dropout(0.3), # 让神经元以一定的概率停止工作,防止过拟合,提高模型的泛化能力。
layers.Flatten(), # Flatten层,连接卷积层与全连接层
layers.Dense(128, activation='relu'), # 全连接层,特征进一步提取
layers.Dense(num_classes) # 输出层,输出预期结果
])
再次训练:
从结果来看,不是太理想,似乎也没有解决过拟合的问题,本次主要学习方式方法,后面慢慢学习研究。
10.保存模型
# 保存模型
model.save('models/weatherModel.pth')
11.加载保存的模型,并进行图片预测
准备预测的图片如下(晴天):
新建立一个python文件,代码如下:
import tensorflow as tf
import numpy as np
image_height = 180
image_width = 180
class_names = ['cloudy', 'rain', 'shine', 'sunrise']
# 加载天气识别模型
model = tf.keras.models.load_model('models/weatherModel.pth')
# 预测一下真实照片
image_path = 'data/laker.jpg'
image = tf.keras.utils.load_img(image_path, target_size=(image_height, image_width))
image_array = tf.keras.utils.img_to_array(image) # 将PIL对象转换成numpy数组
# print(image_array.shape)
# 在image_array的第0位置增加一个维度即batch。(batch, height, width, channel)
image_array = tf.expand_dims(image_array, 0)
pre = model.predict(image_array)
# print(pre)
score = tf.nn.softmax(pre[0])
print(
"当前图片预测有{:.2f}%概率为{}天气 ".format(100 * np.max(score), class_names[np.argmax(score)])
)
结果:
预测是晴天,结果是正确的。
(三)总结
1.如何检测GPU并使用GPU进行大模型训练与预测
2.与之前不一样的数据集的加载与处理方式
3.不同的损失函数,池化层函数,学习率和准确率之间有什么关系?需要继续了解
4.怎么解决过拟合的问题呢,后续学习研究。
5.学习数据增加的方式方法
6.每次模型训练完,进行真实图片的预测,应该模型能够保存后直接调用才对,不可能每次使用都需要重新训练一遍。学习模型的保存与加载使用。