活动地址:CSDN21天学习挑战赛
目录
>- 本文为[365天深度学习训练营](https://mp.weixin.qq.com/s/k-vYaC8l7uxX51WoypLkTw) 中的学习记录博客
>- 参考文章地址: [深度学习100例-卷积神经网络(CNN)天气识别 | 第5天](https://mtyjkh.blog.csdn.net/article/details/117186183)
本文开发环境:tensorflowgpu2.5,经过验证,2.4也可以运行,2.2反正不行,2.3据说可以
1.跑通代码
我这个人对于任何代码,我都会先去跑通之和才会去观看内容,哈哈哈,所以第一步我们先不管37=21,直接把博主的代码复制黏贴一份运行结果。(PS:做了一些修改,因为原文是jupyter,而我在pycharm)
import tensorflow as tf
import os
os.environ['TF_XLA_FLAGS'] = '--tf_xla_enable_xla_devices'
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
import tensorflow as tf
import matplotlib.pyplot as plt
import os,PIL
# 设置随机种子尽可能使结果可以重现
import numpy as np
np.random.seed(1)
# 设置随机种子尽可能使结果可以重现
import tensorflow as tf
tf.random.set_seed(1)
from tensorflow import keras
from tensorflow.keras import layers,models
import pathlib
data_dir="E:/2021_Project_YanYiXia/AI/21/实现天气图像识别/weather_photos/"
data_dir = pathlib.Path(data_dir)
image_count = len(list(data_dir.glob('*/*.jpg')))
print("图片总数为:",image_count)
# roses = list(data_dir.glob('sunrise/*.jpg'))
# PIL.Image.open(str(roses[0]))
batch_size = 32
img_height = 180
img_width = 180
"""
关于image_dataset_from_directory()的详细介绍可以参考文章:https://mtyjkh.blog.csdn.net/article/details/117018789
"""
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
data_dir,
validation_split=0.2,
subset="training",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size)
"""
关于image_dataset_from_directory()的详细介绍可以参考文章:https://mtyjkh.blog.csdn.net/article/details/117018789
"""
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
data_dir,
validation_split=0.2,
subset="validation",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size)
class_names = train_ds.class_names
print(class_names)
plt.figure(figsize=(20, 10))
for images, labels in train_ds.take(1):
for i in range(20):
ax = plt.subplot(5, 10, i + 1)
plt.imshow(images[i].numpy().astype("uint8"))
plt.title(class_names[labels[i]])
plt.axis("off")
for image_batch, labels_batch in train_ds:
print(image_batch.shape)
print(labels_batch.shape)
break
AUTOTUNE = tf.data.AUTOTUNE
print("AUTOTUNE", AUTOTUNE)
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
num_classes = 4
"""
关于卷积核的计算不懂的可以参考文章:https://blog.csdn.net/qq_38251616/article/details/114278995
layers.Dropout(0.4) 作用是防止过拟合,提高模型的泛化能力。
在上一篇文章花朵识别中,训练准确率与验证准确率相差巨大就是由于模型过拟合导致的
关于Dropout层的更多介绍可以参考文章:https://mtyjkh.blog.csdn.net/article/details/115826689
"""
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)), # 卷积层1,卷积核3*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) # 输出层,输出预期结果
])
model.summary() # 打印网络结构
# 设置优化器
opt = tf.keras.optimizers.Adam(learning_rate=0.001)
model.compile(optimizer=opt,
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
epochs = 10
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=epochs
)
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()
# import tensorflow as tf
# version=tf.__version__ #输出tensorflow版本
# gpu_ok=tf.test.is_gpu_available() #输出gpu可否使用(True/False)
# print("tf version:",version,"\nuse GPU:",gpu_ok)
# tf.test.is_built_with_cuda() # 判断CUDA是否可用(True/False)
点击pycharm即可运行出最后的预测结果!
2.代码分析
神经网络的整个过程我分为如下六部分,而我们也会对这六部分进行逐部分分析。那么这6部分分别是:
六步法:
1->import
2->train test(指定训练集的输入特征和标签)
3->class MyModel(model) model=Mymodel(搭建网络结构,逐层描述网络)
4->model.compile(选择哪种优化器,损失函数)
5->model.fit(执行训练过程,输入训练集和测试集的特征+标签,batch,迭代次数)
6->验证
2.1
导入:这里很容易理解,也就是导入本次实验内容所需要的各种库。在本案例中主要包括以下部分:
import tensorflow as tf
import os
os.environ['TF_XLA_FLAGS'] = '--tf_xla_enable_xla_devices'
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
from tensorflow import keras
from tensorflow.keras import layers,models
import pathlib
import matplotlib.pyplot as plt
import os,PIL
注意事项:这里我们发现比和普通代码多了两句os.environ。这是因为当时运行的时候有个报错,我去网上找的解决办法,但是不写两句也可以训练模型(tensorflow只要不影响训练,可以不管)
主要是tensorflow以及绘制,目录操作(pathlib)的库。
对于这里的话我们可以直接复制黏贴,当需要一些其他函数时,只需要添加对应的库文件即可。
2.2
设置训练集和测试集:对于神经网络的训练包括了两种数据集合,一个是训练集,一个是测试集。其中训练集数据较多,测试集较少,因为训练一个模型数据越多相对的模型更准确。
本文中利用的数据集在文章下方,该数据集是一个天气的彩色图像数据集合
本文中数据集处理较为复杂,包括了随机数,显示图片,数据集设置等,我们将分别对代码进行解释。
如上为随机数种子的设置,神经网络的方法起始我感觉就是扩展变量法,所以为了保证初始情况都一样,因此设置随机数种子。
本文数据集和之前数字识别和服装识别利用的minist数据集不一样,是存在与本地文件夹中,因此这里数据集的调用方法也不一样。
这里利用了文件夹函数,首先找到数据集的路径,然后通过函数设置路径文件夹,相当于图中的data_dir就是数据集了
这个看起来就很熟悉,这是设置图片的尺寸以及batch_size。
这里就是设置训练集和测试集了。利用了image_dataset_from_directory()
很明显可以看出来,这是一个图像数据来自文件夹函数。而这个文件夹就是data_dir。
该函数中的变量设置的意思如下:
我把该部分分为三个框,现在依次对框进行解释。
1.这里是吧训练集的标签打印出来,以及设置将要打印出来的图片的尺寸
2.这里是对训练集的数据进行打印和显示20张
3.这里是吧训练集的数量,尺寸,通道数,batchsize再次打印出来
该部分主要是加速电脑训练的。
到这里,数据集的设置就结束了。我们可以发现相对于来说,代码多了很多,而这是因为数据集来源不一样,以及对于数据显示我们多显示了一下。但核心内容还是设置数据集
2.3
网络模型搭建:这里也是神经网络的重点了!废话不多说,直接开始!
本文的神经网络的结构图如下:
在搭建模型的时候,我们将按照这个图片进行模型的搭建。
卷积层1:16通道,3x3尺寸,步长1的卷积核
layers.Conv2D(16, (3, 3), activation='relu', input_shape=(img_height, img_width, 3))
注意事项:这里是网络模型的第一层,因此要加上输入
池化层1:该池化层为2x2
layers.AveragePooling2D((2, 2))
卷积层2:32通道,3x3尺寸,步长1的卷积核
layers.Conv2D(32, (3, 3), activation='relu')
池化层2:该池化层为2x2
layers.AveragePooling2D((2, 2))
卷积层3:64通道,3x3尺寸,步长1的卷积核
layers.Conv2D(64, (3, 3), activation='relu')
过滤层(防止过拟合)详细解释参考本文开头的另外一篇基础介绍
layers.Dropout(0.3)
重点:
现在我们来分析一下图片中经过每层后数据的维度怎么来的
经过卷积层1之后,原数据28x28变为26x26是因为一个公式: (180-3)/stride+1=178,而3变为16是因为当前卷积核通道数为16.
经过池化层1之后,原数据178变为79是因为池化池的卷积核为2,所以79=178/2
经过卷积层2之后,原数据89变为87是因为一个公式: (89-3)/stride+1=87,而16变为32是因为当前卷积核通道数为32.
经过池化层2之后,原数据87变为43是因为池化池的卷积核为2,所以43=87/2
经过卷积层3之后,原数据43变为41是因为一个公式: (43-3)/stride+1=41,
droupout层是防止过拟合
经过flatten层之后,数据数量=41*41*64=107584
而后续全连接层的输出是根据全连接层代码设置。需要注意的是因为数据集是4种类型,因此最后为4
到此,我们便把网络模型设置的原因以及网络模型的输出结果进行了对应,我们可以看到网络模型的输出和我们分析的一致。
到此,网络模型我们变分析完了。
注意事项:我们发现这里的卷积层每一次的引入了激活函数,这是因为输入是彩色图像,引入激活函数可以更好的提取特征,而正是因为提取特征相对较多,我们又引入了防止过拟合。对于越复杂是数据越要引入激活函数,但是激活函数过多容易过拟合
2.4
该部分也同样重要,主要完成模型训练过程中的优化器,损失函数,准确率的设置。
我们结合本文来看。
opt = tf.keras.optimizers.Adam(learning_rate=0.001)
model.compile(optimizer=opt,
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
其中:对于这三个内容的含义可以参考我的文章开头的另外一篇基础博文进行了详细的介绍
2.5
该部分就是执行训练了,那么执行训练肯定需要设置训练集数据及其标签,测试集数据及其标签,训练的epoch
epochs = 10
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=epochs
)
2.6
当训练执行完毕,我们就可以拿一个测试集合中或者其他满足格式的数据进行验证了
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()
这里就是绘制训练集和测试集的准确率和损失函数图像
我们取测试集合的前4张图片进行验证(这里代码不在上面,是单独测试用的,我放到下面了)
for images, labels in val_ds.take(1):
for i in range(20):
ax = plt.subplot(5, 10, i + 1)
plt.imshow(images[i].numpy().astype("uint8"))
plt.title(class_names[labels[i]])
plt.axis("off")
plt.show()
#
#
pre = model.predict(val_ds) # 对所有测试图片进行预测
print("--")
print( pre[0]) # 输出第一张图片的预测结果
print( pre[1]) # 输出第一张图片的预测结果
print( pre[2]) # 输出第一张图片的预测结果
print( pre[3]) # 输出第一张图片的预测结果
print("--")
从结果中可以看出,前两张没问题,但是由于rain和cloudy值几乎差不多,所以判断失败