zl程序教程

您现在的位置是:首页 >  云平台

当前栏目

深度学习 Day 2——如何利用CNN实现天气识别?

识别学习 实现 如何 利用 深度 CNN Day
2023-09-11 14:22:09 时间

深度学习 Day 2——如何利用CNN实现天气识别?

活动地址:CSDN21天学习挑战赛

一、前言

我的环境:

  • 电脑系统:Win 11

  • 语言环境:Python 3.10

  • 编译器:PyCharm,Jupyter notebook

  • 深度学习环境:TensorFlow2

在前面一篇博客中我们已经安装和配置好我们的深度学习环境了,也进行了一次官方示例的演示,接下来我们将学习关于卷积神经网络(CNN)的深度学习知识。

我是跟着CSDN上的k同学啊学习的有关深度学习方面的知识,他也是本次活动的带头人,我们一起跟着他好好学习关于深度学习方面的知识。

我们今天要学习有关天气识别的知识,是利用深度学习来实现的,他的原篇博客地址我放在这里:

https://mtyjkh.blog.csdn.net/article/details/117186183

废话不多说,我们开始今天的学习!

二、准备数据

在进行数据预处理之前我们需要先将数据导入到程序中。

1、设置GPU

如果你使用的是CPU可以忽略这一点,但是一般来说,研究深度学习和神经网络大都离不开GPU,在GPU的加持下,我们可以更快的获得模型训练的结果。

GPU的计算速度比CPU快得多。然而,情况并不总是如此。GPU相对于CPU的速度取决于执行的计算类型。最适合GPU的计算类型是可以并行完成的计算。我们在进行卷积计算的时候可以大大提高计算效率。因为深度学习和神经网络的每一个计算任务都是独立于其他计算的,任何计算都不依赖于任何其他计算的结果,可以采用高度并行的方式进行计算。而GPU相比于CPU拥有更多独立的大吞吐量计算通道,较少的控制单元使其不会受到计算以外的更多任务的干扰,拥有比CPU更纯粹的计算环境,所以深度学习和神经网络模型在GPU的加持下会更高效地完成计算任务。

大致就说这么多关于GPU和CPU在这方面的区别,想要了解更多关于这两个的区别与关系可以自行上网查阅,其中的逻辑和结构比较的复杂。

说了这么多,我们来进行代码的GPU设置:

import tensorflow as tf

gpus = tf.config.list_physical_devices("GPU")

if gpus:
    gpu0 = gpus[0]                                        #如果有多个GPU,仅使用第0个GPU
    tf.config.experimental.set_memory_growth(gpu0, True)  #设置GPU显存用量按需使用
    tf.config.set_visible_devices([gpu0], "GPU")

2、导入数据集

设置好GPU之后,我们开始导入我们需要的数据集:

import matplotlib.pyplot as plt

# 设置随机种子尽可能使结果可以重现
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:\深度学习\数据\weather_photos" # 数据集路径
data_dir = pathlib.Path(data_dir)

在这里使用随机种子,我认为应该是为了能够完全复现作者的代码结果,随机种子的选择能够减少一定程度上算法结果的随机性,也就是更接近于原始作者的结果,即产生随机种子意味着每次运行实验,产生的随机数都是相同的,但是在大多数情况下,即使设定了随机种子,也仍然没有办法完全复现原作者的结果。

3、查看数据集

原作者提供的数据集有三个类的文件夹:

在这里插入图片描述

我们计算一下一共共有多少张照片

image_count = len(list(data_dir.glob('*/*.jpg')))
print("图片总数为:", image_count)

运行出来显示照片一共有1125张照片供我们训练。

图片总数为: 1125

我们导入PIL库,这个库是用来为我们的Python解释器添加图像处理功能,比如打开某个文件夹里面的某张照片:

import PIL

roses = list(data_dir.glob('sunrise/*.jpg'))
PIL.Image.open(str(roses[0])) # 这里是打开sunrise文件夹中第一张照片

在这里插入图片描述

顺便提一下,这个库是Python的第三方库,PIL支持Python2,但是不支持Python3,我们需要下载的是pillow,这个库下载之后我们导包的时候还是需要用PIL来导入,用pillow或者Pillow都是不行的。

三、数据预处理

1、加载数据

在这里我们需要使用一个函数方法:image_dataset_from_directory

这个函数的原型是:

tf.keras.preprocessing.image_dataset_from_directory(
    directory,
    labels="inferred",
    label_mode="int",
    class_names=None,
    color_mode="rgb",
    batch_size=32,
    image_size=(256, 256),
    shuffle=True,
    seed=None,
    validation_split=None,
    subset=None,
    interpolation="bilinear",
    follow_links=False,
)

如果你的目录结构是:

main_directory/
...class_a/
......a_image_1.jpg
......a_image_2.jpg
...class_b/
......b_image_1.jpg
......b_image_2.jpg

然后调用image_dataset_from_directory(main_directory, labels=‘inferred’) 将返回 a ,它会tf.data.Dataset从子目录class_a和中生成批量图像class_b,以及标签 0 和 1(0 对应于class_a1,1 对应于class_b)。

支持的图像格式:jpeg、png、bmp、gif。动画 gif 被截断到第一帧。

这里是它的相关参数介绍和说明:

在这里插入图片描述

这个函数的官网介绍地址我放在这里:

https://tensorflow.google.cn/api_docs/python/tf/keras/utils/image_dataset_from_directory

想要了解更多的可以到官网去查阅。

好,现在我们使用image_dataset_from_directory方法将我们本地电脑里面的照片数据加载到tf.data.Dataset中去。

先定义我们需要加载的照片属性:

batch_size = 32
img_height = 180
img_width = 180

然后开始加载:

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)

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)

运行的结果是:

Found 1125 files belonging to 4 classes.
Using 900 files for training.

Found 1125 files belonging to 4 classes.
Using 225 files for validation.

然后再通过class_names输出数据集的标签,让标签按照字母顺序对应目录名称,这样更有利于管理。

class_names = train_ds.class_names
print(class_names)

运行的结果是:

['cloudy', 'rain', 'shine', 'sunrise']

2、可视化数据

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")

在这里figure方法中参数figsize是指图像的宽和高,单位是英尺,它还有很多其他的参数:

参数说明
num图像编码或者名称,数字是编码,字符串是名称
figsize宽和高,单位是英尺
dpi指定绘图对象的分辨率,即每英寸多少个像素,缺省值为80
facecolor背景颜色
edgecolor边框颜色
frameon是否显示边框

subplot方法中也有很多参数:

参数说明
nrows行数
ncols列数
sharex和谁共享x轴
sharey和谁共享y轴
subplot_kw关键字字典
**fig_kw其他关键字

3、再次检查数据

通过for循环来检查数据的参数:

for image_batch, labels_batch in train_ds:
    print(image_batch.shape)
    print(labels_batch.shape)
    break

再次检查的结果是:

(32, 180, 180, 3)
(32,)

这里image_batch是形状的张量,括号内的数字表示这是一组形状为180×180×3的32张图像,而label_batch表示这些标签对应32张图像。

4、配置数据集

配置数据集我们需要用到三个方法:

方法说明
shuffle()将序列的所有元素随机排序
prefetch()预取数据,用于加速运行
cache()将数据集缓存到内存中,加速运行

注意的是,shuffle()方法是不能直接访问的,我们需要使用随机数模块,然后通过随机数模块静态对象调用这个方法,我们前面定义过所有可以直接使用。

from tensorflow.python.data.ops.dataset_ops import AUTOTUNE
# AUTOTUNE = tf.data.AUTOTUNE

train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

在这里有个问题,原作者使用的是带#号的方法,但是我这样运行是会报错的,所以我改用了我自己的方法就成功了,具体什么原因目前我也不知道,如果有人知道原因的话可以在下面评论或者私信告诉我。

四、构建CNN网络

CNN网络是卷神经网络的别名。而卷神经网络最擅长的就是图片的处理,它是受到人类视觉神经系统的启发。

CNN有两大特点:

  1. 能够有效的将大数据量的图片降维成小数据量
  2. 能够有效的保留图片特征,符合图片处理的原则

大致就说这么多,后续我还会写一篇博客来稍微详细的说一下这些,因为我也是深度学习这方面的小白,刚接触不久,等我后续查阅资料之后再把学习总结写出来。

在这里,CNN输入的是张量形式的图像的高度,图像的宽度和图像的颜色信息,不需要输入图像的批量大小,而图像颜色信息是输入的RGB的三个颜色通道。

在这里我们需要用到卷积核的计算,而卷积核就是图像处理时,给定输入图像,输入图像中一个小区域中像素加权平均后成为输出图像中的每个对应像素,其中权值由一个函数定义,这个函数称为卷积核,后面我们再做更多的说明。

另外我们还需要添加模型的泛化能力,加上Dropout层并使用平均池化层

Dropout可以作为训练深度神经网络的一种trick供选择。在每个训练批次中,通过忽略一半的特征检测器(让一半的隐层节点值为0),可以明显地减少过拟合现象。

Dropout层 tf.keras.layers.Dropout() 的函数原型是:

tf.keras.layers.Dropout(
    rate, noise_shape=None, seed=None, **kwargs
)

这是它的相关参数说明:

在这里插入图片描述

说完这些我们,我们来把代码写出来:

num_classes = 4

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()  # 打印网络结构

这段代码运行出来的结果是:

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
rescaling (Rescaling)        (None, 180, 180, 3)       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 178, 178, 16)      448       
_________________________________________________________________
average_pooling2d (AveragePo (None, 89, 89, 16)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 87, 87, 32)        4640      
_________________________________________________________________
average_pooling2d_1 (Average (None, 43, 43, 32)        0         
_________________________________________________________________
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
_________________________________________________________________

可以看到总参数和可训练参数一样,并没有不可训练参数。

五、模型编译

我们在进行训练模型之前还需要设置优化器,优化器可以通过算法帮助模型在训练过程中,如何更快更好的将参数调整到位,而tensorflow有很多的优化器,在这里我们使用的是AdamOptimizer优化器,它是利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。它对学习率有个约束,使得每一次学习率都有个确定范围,因此参数比较平稳。

# 设置优化器
opt = tf.keras.optimizers.Adam(learning_rate=0.001)

model.compile(optimizer=opt,
           loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
           metrics=['accuracy'])

说明一下其中几个参数:

  • optimizer:优化器,决定模型如何根据其看到的数据和自身的损失函数进行更新
  • loss:损失函数,用于衡量模型在训练期间的准确率
  • metrics:指标,用于监控训练和测试步骤

六、训练模型

训练模型代码如下:

epochs = 10

history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=epochs
)

一个epoch是指把所有训练数据完整的跑一遍

model.fit()方法用于执行训练过程,它的方法使用如下:

model.fit( 训练集的输入特征,
                 训练集的标签,  
                 batch_size,  #每一个batch的大小
                 epochs,   #迭代次数
                 validation_data = (测试集的输入特征,测试集的标签),
                 validation_split = 从测试集中划分多少比例给训练集,
                 validation_freq = 测试的epoch间隔数)

上面训练模型代码运行的结果是:

Epoch 1/10
29/29 [==============================] - 7s 229ms/step - loss: 1.0138 - accuracy: 0.6278 - val_loss: 0.6860 - val_accuracy: 0.7244
Epoch 2/10
29/29 [==============================] - 6s 206ms/step - loss: 0.5177 - accuracy: 0.8256 - val_loss: 0.5455 - val_accuracy: 0.8000
Epoch 3/10
29/29 [==============================] - 6s 199ms/step - loss: 0.3470 - accuracy: 0.8711 - val_loss: 0.7673 - val_accuracy: 0.7333
Epoch 4/10
29/29 [==============================] - 6s 207ms/step - loss: 0.2896 - accuracy: 0.9122 - val_loss: 0.5628 - val_accuracy: 0.7867
Epoch 5/10
29/29 [==============================] - 6s 205ms/step - loss: 0.2009 - accuracy: 0.9267 - val_loss: 0.3247 - val_accuracy: 0.8844
Epoch 6/10
29/29 [==============================] - 6s 212ms/step - loss: 0.1638 - accuracy: 0.9411 - val_loss: 0.4505 - val_accuracy: 0.8311
Epoch 7/10
29/29 [==============================] - 6s 200ms/step - loss: 0.1082 - accuracy: 0.9644 - val_loss: 0.3575 - val_accuracy: 0.8800
Epoch 8/10
29/29 [==============================] - 6s 204ms/step - loss: 0.0675 - accuracy: 0.9756 - val_loss: 0.5717 - val_accuracy: 0.8044
Epoch 9/10
29/29 [==============================] - 6s 203ms/step - loss: 0.0630 - accuracy: 0.9756 - val_loss: 0.4456 - val_accuracy: 0.8889
Epoch 10/10
29/29 [==============================] - 6s 200ms/step - loss: 0.1048 - accuracy: 0.9600 - val_loss: 0.5101 - val_accuracy: 0.8622

七、模型评估

在这里我们通过Python可视化数据制图来进行模型的评估工作。

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()

它运行的结果是:

在这里插入图片描述

第一个图作用是培训和验证准确性,第二个图作用是训练和验证损失。在这里和原作者的图大体上一致。

八、最后我想说

整个过程已经结束了,整个过程和代码我都是跟着K同学啊学习的,这些东西对于现阶段的我来说很难去理解并学会,博客虽然写完了但不代表我就会了就可以结束了,后面的时间我还需要花费大量的时间去更深层次的理解这些代码的含义以及作用,学习的道路总是永无止境的,后面我会花时间整理一篇我在其中遇见的问题并如何去解决的博客,里面还会加上代码里面出现过的模型,方法什么的更加详细的说明。

总而言之,干计算机这个行业,你就要做好时刻都要去学习的准备,要跟紧时代发展和计算机行业的更新迭代,目前处于大学的我们更需要去汲取新时代的各种知识,在踏入社会之前让自己变得更加有能力。

好了,感谢你们的支持,希望你们能和我一样学习知识,将自己武装起来,另外我也希望得到你们的点赞收藏转发三连,你们的鼓励将是我创作最大动力,谢谢!!!
在这里插入图片描述