活动地址:CSDN21天学习挑战赛
目录
>- 本文为[365天深度学习训练营](https://mp.weixin.qq.com/s/k-vYaC8l7uxX51WoypLkTw) 中的学习记录博客
>- 参考文章地址:(深度学习100例 | 第24天-卷积神经网络(Xception):动物识别_K同学啊的博客-CSDN博客_深度学习动物识别)
本文开发环境:tensorflowgpu2.5,经过验证,2.4也可以运行,2.2反正不行
我这个人对于任何代码,我都会先去跑通之和才会去观看内容,哈哈哈,所以第一步我们先不管37=21,直接把博主的代码复制黏贴一份运行结果。(PS:做了一些修改,因为原文是jupyter,而我在pycharm)
- import tensorflow as tf
-
- gpus = tf.config.list_physical_devices("GPU")
-
- if gpus:
- tf.config.experimental.set_memory_growth(gpus[0], True) #设置GPU显存用量按需使用
- tf.config.set_visible_devices([gpus[0]],"GPU")
-
- # 打印显卡信息,确认GPU可用
- print(gpus)
-
- import matplotlib.pyplot as plt
- # 支持中文
- plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
- plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
- import os,PIL
- # 设置随机种子尽可能使结果可以重现
- import numpy as np
- np.random.seed(1)
- # 设置随机种子尽可能使结果可以重现
- import tensorflow as tf
- tf.random.set_seed(1)
- import pathlib
-
- data_dir = "./data"
-
- data_dir = pathlib.Path(data_dir)
-
-
- image_count = len(list(data_dir.glob('*/*')))
-
- print("图片总数为:",image_count)
-
- batch_size = 2
- img_height = 299
- img_width = 299
-
- """
- 关于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=12,
- 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=12,
- image_size=(img_height, img_width),
- batch_size=batch_size)
-
- class_names = train_ds.class_names
- print(class_names)
-
- for image_batch, labels_batch in train_ds:
- print(image_batch.shape)
- print(labels_batch.shape)
- break
-
- AUTOTUNE = tf.data.AUTOTUNE
-
- train_ds = (
- train_ds.cache()
- .shuffle(1000)
- # .map(train_preprocessing) # 这里可以设置预处理函数
- # .batch(batch_size) # 在image_dataset_from_directory处已经设置了batch_size
- .prefetch(buffer_size=AUTOTUNE)
- )
-
- val_ds = (
- val_ds.cache()
- .shuffle(1000)
- # .map(val_preprocessing) # 这里可以设置预处理函数
- # .batch(batch_size) # 在image_dataset_from_directory处已经设置了batch_size
- .prefetch(buffer_size=AUTOTUNE)
- )
-
- # ====================================#
- # Xception的网络部分
- # ====================================#
- from tensorflow.keras.preprocessing import image
-
- from tensorflow.keras.models import Model
- from tensorflow.keras import layers
- from tensorflow.keras.layers import Dense, Input, BatchNormalization, Activation, Conv2D, SeparableConv2D, MaxPooling2D
- from tensorflow.keras.layers import GlobalAveragePooling2D, GlobalMaxPooling2D
- from tensorflow.keras import backend as K
- from tensorflow.keras.applications.imagenet_utils import decode_predictions
-
-
- def Xception(input_shape=[299, 299, 3], classes=1000):
- img_input = Input(shape=input_shape)
-
- # =================#
- # Entry flow
- # =================#
- # block1
- # 299,299,3 -> 149,149,64
- x = Conv2D(32, (3, 3), strides=(2, 2), use_bias=False, name='block1_conv1')(img_input)
- x = BatchNormalization(name='block1_conv1_bn')(x)
- x = Activation('relu', name='block1_conv1_act')(x)
- x = Conv2D(64, (3, 3), use_bias=False, name='block1_conv2')(x)
- x = BatchNormalization(name='block1_conv2_bn')(x)
- x = Activation('relu', name='block1_conv2_act')(x)
-
- # block2
- # 149,149,64 -> 75,75,128
- residual = Conv2D(128, (1, 1), strides=(2, 2), padding='same', use_bias=False)(x)
- residual = BatchNormalization()(residual)
-
- x = SeparableConv2D(128, (3, 3), padding='same', use_bias=False, name='block2_sepconv1')(x)
- x = BatchNormalization(name='block2_sepconv1_bn')(x)
- x = Activation('relu', name='block2_sepconv2_act')(x)
- x = SeparableConv2D(128, (3, 3), padding='same', use_bias=False, name='block2_sepconv2')(x)
- x = BatchNormalization(name='block2_sepconv2_bn')(x)
-
- x = MaxPooling2D((3, 3), strides=(2, 2), padding='same', name='block2_pool')(x)
- x = layers.add([x, residual])
-
- # block3
- # 75,75,128 -> 38,38,256
- residual = Conv2D(256, (1, 1), strides=(2, 2), padding='same', use_bias=False)(x)
- residual = BatchNormalization()(residual)
-
- x = Activation('relu', name='block3_sepconv1_act')(x)
- x = SeparableConv2D(256, (3, 3), padding='same', use_bias=False, name='block3_sepconv1')(x)
- x = BatchNormalization(name='block3_sepconv1_bn')(x)
- x = Activation('relu', name='block3_sepconv2_act')(x)
- x = SeparableConv2D(256, (3, 3), padding='same', use_bias=False, name='block3_sepconv2')(x)
- x = BatchNormalization(name='block3_sepconv2_bn')(x)
-
- x = MaxPooling2D((3, 3), strides=(2, 2), padding='same', name='block3_pool')(x)
- x = layers.add([x, residual])
-
- # block4
- # 38,38,256 -> 19,19,728
- residual = Conv2D(728, (1, 1), strides=(2, 2), padding='same', use_bias=False)(x)
- residual = BatchNormalization()(residual)
-
- x = Activation('relu', name='block4_sepconv1_act')(x)
- x = SeparableConv2D(728, (3, 3), padding='same', use_bias=False, name='block4_sepconv1')(x)
- x = BatchNormalization(name='block4_sepconv1_bn')(x)
- x = Activation('relu', name='block4_sepconv2_act')(x)
- x = SeparableConv2D(728, (3, 3), padding='same', use_bias=False, name='block4_sepconv2')(x)
- x = BatchNormalization(name='block4_sepconv2_bn')(x)
-
- x = MaxPooling2D((3, 3), strides=(2, 2), padding='same', name='block4_pool')(x)
- x = layers.add([x, residual])
-
- # =================#
- # Middle flow
- # =================#
- # block5--block12
- # 19,19,728 -> 19,19,728
- for i in range(8):
- residual = x
- prefix = 'block' + str(i + 5)
-
- x = Activation('relu', name=prefix + '_sepconv1_act')(x)
- x = SeparableConv2D(728, (3, 3), padding='same', use_bias=False, name=prefix + '_sepconv1')(x)
- x = BatchNormalization(name=prefix + '_sepconv1_bn')(x)
- x = Activation('relu', name=prefix + '_sepconv2_act')(x)
- x = SeparableConv2D(728, (3, 3), padding='same', use_bias=False, name=prefix + '_sepconv2')(x)
- x = BatchNormalization(name=prefix + '_sepconv2_bn')(x)
- x = Activation('relu', name=prefix + '_sepconv3_act')(x)
- x = SeparableConv2D(728, (3, 3), padding='same', use_bias=False, name=prefix + '_sepconv3')(x)
- x = BatchNormalization(name=prefix + '_sepconv3_bn')(x)
-
- x = layers.add([x, residual])
-
- # =================#
- # Exit flow
- # =================#
- # block13
- # 19,19,728 -> 10,10,1024
- residual = Conv2D(1024, (1, 1), strides=(2, 2),
- padding='same', use_bias=False)(x)
- residual = BatchNormalization()(residual)
-
- x = Activation('relu', name='block13_sepconv1_act')(x)
- x = SeparableConv2D(728, (3, 3), padding='same', use_bias=False, name='block13_sepconv1')(x)
- x = BatchNormalization(name='block13_sepconv1_bn')(x)
- x = Activation('relu', name='block13_sepconv2_act')(x)
- x = SeparableConv2D(1024, (3, 3), padding='same', use_bias=False, name='block13_sepconv2')(x)
- x = BatchNormalization(name='block13_sepconv2_bn')(x)
-
- x = MaxPooling2D((3, 3), strides=(2, 2), padding='same', name='block13_pool')(x)
- x = layers.add([x, residual])
-
- # block14
- # 10,10,1024 -> 10,10,2048
- x = SeparableConv2D(1536, (3, 3), padding='same', use_bias=False, name='block14_sepconv1')(x)
- x = BatchNormalization(name='block14_sepconv1_bn')(x)
- x = Activation('relu', name='block14_sepconv1_act')(x)
-
- x = SeparableConv2D(2048, (3, 3), padding='same', use_bias=False, name='block14_sepconv2')(x)
- x = BatchNormalization(name='block14_sepconv2_bn')(x)
- x = Activation('relu', name='block14_sepconv2_act')(x)
-
- x = GlobalAveragePooling2D(name='avg_pool')(x)
- x = Dense(classes, activation='softmax', name='predictions')(x)
-
- inputs = img_input
-
- model = Model(inputs, x, name='xception')
-
- return model
-
- model = Xception()
- # 打印模型信息
- model.summary()
-
- # 设置初始学习率
- initial_learning_rate = 1e-4
-
- lr_schedule = tf.keras.optimizers.schedules.ExponentialDecay(
- initial_learning_rate,
- decay_steps=300, # 敲黑板!!!这里是指 steps,不是指epochs
- decay_rate=0.96, # lr经过一次衰减就会变成 decay_rate*lr
- staircase=True)
-
- # 将指数衰减学习率送入优化器
- optimizer = tf.keras.optimizers.Adam(learning_rate=lr_schedule)
-
- model.compile(optimizer=optimizer,
- loss ='sparse_categorical_crossentropy',
- metrics =['accuracy'])
-
- epochs = 15
-
- 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()
-
- from sklearn.metrics import confusion_matrix
- import seaborn as sns
- import pandas as pd
-
-
- # 定义一个绘制混淆矩阵图的函数
- def plot_cm(labels, predictions):
- # 生成混淆矩阵
- conf_numpy = confusion_matrix(labels, predictions)
- # 将矩阵转化为 DataFrame
- conf_df = pd.DataFrame(conf_numpy, index=class_names, columns=class_names)
-
- plt.figure(figsize=(8, 7))
-
- sns.heatmap(conf_df, annot=True, fmt="d", cmap="BuPu")
-
- plt.title('混淆矩阵', fontsize=15)
- plt.ylabel('真实值', fontsize=14)
- plt.xlabel('预测值', fontsize=14)
-
- val_pre = []
- val_label = []
-
- for images, labels in val_ds:#这里可以取部分验证数据(.take(1))生成混淆矩阵
- for image, label in zip(images, labels):
- # 需要给图片增加一个维度
- img_array = tf.expand_dims(image, 0)
- # 使用模型预测图片中的人物
- prediction = model.predict(img_array)
-
- val_pre.append(class_names[np.argmax(prediction)])
- val_label.append(class_names[label])
- plot_cm(val_label, val_pre)
-
- # 保存模型
- model.save('model/24_model.h5')
点击pycharm即可运行出最后的预测结果!
这里的结果是指网络模型的保存和损失函数这些的输出,对于模型的预测结果我们将在最后显示。

神经网络的整个过程我分为如下六部分,而我们也会对这六部分进行逐部分分析。那么这6部分分别是:
六步法:
1->import
2->train test(指定训练集的输入特征和标签)
3->class MyModel(model) model=Mymodel(搭建网络结构,逐层描述网络)
4->model.compile(选择哪种优化器,损失函数)
5->model.fit(执行训练过程,输入训练集和测试集的特征+标签,batch,迭代次数)
6->验证
导入:这里很容易理解,也就是导入本次实验内容所需要的各种库。在本案例中主要包括以下部分:

蓝框1:
设置电脑gpu工作,如果你的电脑没有gpu就不设置,或者你的gpu显存不够,训练时出问题了,那么就设在为cpu模式
蓝框2:
设置中文显示
对于这里的话我们可以直接复制黏贴,当需要一些其他函数时,只需要添加对应的库文件即可。
设置训练集和测试集:对于神经网络的训练包括了两种数据集合,一个是训练集,一个是测试集。其中训练集数据较多,测试集较少,因为训练一个模型数据越多相对的模型更准确。
本文中利用的数据集在文章下方,该数据集是一个动物的彩色图像数据集合
本文中数据集处理较为复杂,包括了随机数,显示图片,数据集设置等,我们将分别对代码进行解释。

蓝框1:
如上为随机数种子的设置,神经网络的方法起始我感觉就是扩展变量法,所以为了保证初始情况都一样,因此设置随机数种子。
蓝框2:
本文数据集和之前数字识别和服装识别利用的minist数据集不一样,是存在与本地文件夹中,因此这里数据集的调用方法也不一样。
这里利用了文件夹函数,首先找到数据集的路径,然后通过函数设置路径文件夹,相当于图中的data_dir就是数据集了

这个看起来就很熟悉,这是设置图片的尺寸以及batch_size。

蓝框1 :
这里就是设置训练集了。利用了image_dataset_from_directory(),很明显可以看出来,这是一个图像数据来自文件夹函数。而这个文件夹就是data_dir。
该函数中的变量设置的意思如下:

蓝框2:
这里是吧训练集的标签打印出来,以及设置将要打印出来的图片的尺寸

蓝框1:
这里是吧训练集的数量,尺寸,通道数,batchsize再次打印出来
蓝框2:
该部分主要是加速电脑训练的。也可以使用另外的写法,可以参考我的天气识别博客
到这里,数据集的设置就结束了。我们可以发现相对于来说,代码多了很多,而这是因为数据集来源不一样,以及对于数据显示我们多显示了一下。但核心内容还是设置数据集
网络模型搭建:这里也是神经网络的重点了!废话不多说,直接开始!

重点:
现在我们来分析一下图片中经过每层后数据的维度怎么来的
经过卷积层1之后,原数据299x299变为149x149是因为一个公式: (299-3)/stride+1=149,而3变为32是因为当前卷积核通道数为32.
其他的卷积层都是按照这个方式来计算的。如果你想看详细的计算过程,可以参考我的天气识别模型,那里模型简单一点,我把整个过程都分析了。
实现天气彩色图像识别_重邮研究森的博客-CSDN博客
https://blog.csdn.net/m0_60524373/article/details/126153389
到此,网络模型我们变分析完了。
注意事项:我们发现这里的卷积层每一次的引入了激活函数,这是因为输入是彩色图像,引入激活函数可以更好的提取特征,而正是因为提取特征相对较多,我们又引入了防止过拟合。对于越复杂是数据越要引入激活函数,但是激活函数过多容易过拟合
该部分也同样重要,主要完成模型训练过程中的优化器,损失函数,准确率的设置。
我们结合本文来看。

其中:对于这三个内容的含义可以参考我的文章开头的另外一篇基础博文进行了详细的介绍
该部分就是执行训练了,那么执行训练肯定需要设置训练集数据及其标签,测试集数据及其标签,训练的epoch

当训练执行完毕,我们就可以拿一个测试集合中或者其他满足格式的数据进行验证了

这里就是绘制训练集和测试集的准确率和损失函数图像

今天引入一个新东西,可以方便我们进行查看测试集合。名字叫做混淆矩阵!

话不多说,先上图。下图就是混淆矩阵画出来的预测值

从上图中我们可以看出,预测和真实值清晰明了的放出来了,但是可惜我们这个本来模型准确率不敢苟同,所以效果差,但这不影响我们简单的使用,代码我放在下面了。注意上面还需要添加一段数据集设置的。
- # 加载模型
- model = tf.keras.models.load_model('model/24_model.h5')
-
-
- from sklearn.metrics import confusion_matrix
- import seaborn as sns
- import pandas as pd
-
-
- # 定义一个绘制混淆矩阵图的函数
- def plot_cm(labels, predictions):
- # 生成混淆矩阵
- conf_numpy = confusion_matrix(labels, predictions)
- # 将矩阵转化为 DataFrame
- conf_df = pd.DataFrame(conf_numpy, index=class_names, columns=class_names)
-
- plt.figure(figsize=(8, 7))
-
- sns.heatmap(conf_df, annot=True, fmt="d", cmap="BuPu")
-
- plt.title('混淆矩阵', fontsize=15)
- plt.ylabel('真实值', fontsize=14)
- plt.xlabel('预测值', fontsize=14)
- plt.show()
-
- val_pre = []
- val_label = []
-
- for images, labels in val_ds:#这里可以取部分验证数据(.take(1))生成混淆矩阵
- for image, label in zip(images, labels):
- # 需要给图片增加一个维度
- img_array = tf.expand_dims(image, 0)
- # 使用模型预测图片中的人物
- prediction = model.predict(img_array)
-
- val_pre.append(class_names[np.argmax(prediction)])
- val_label.append(class_names[label])
-
- plot_cm(val_label, val_pre)
- plt.show()