0


CNN-运动鞋品牌识别

  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍦 参考文章地址: 365天深度学习训练营-第5周:运动鞋品牌识别
  • 🍖 作者:K同学啊

一、前期工作

1. 设置GPU

  1. from tensorflow import keras
  2. from tensorflow.keras import layers,models
  3. import os, PIL, pathlib
  4. import matplotlib.pyplot as plt
  5. import tensorflow as tf
  6. gpus = tf.config.list_physical_devices("GPU")
  7. if gpus:
  8. gpu0 = gpus[0] #如果有多个GPU,仅使用第0个GPU
  9. tf.config.experimental.set_memory_growth(gpu0, True) #设置GPU显存用量按需使用
  10. tf.config.set_visible_devices([gpu0],"GPU")
  11. gpus
  1. [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]

2. 导入并查看数据

  1. data_dir = pathlib.Path("./data/")
  2. image_count = len(list(data_dir.glob('*/*/*.jpg')))
  3. print("图片总数为:",image_count)
  1. 图片总数为: 578
  1. roses= list(data_dir.glob('train/nike/*.jpg'))
  2. PIL.Image.open(str(roses[0]))

二、数据预处理

1. 加载数据

使用image_dataset_from_directory方法将磁盘中的数据加载到tf.data.Dataset中

测试集与验证集的关系:

  1. 验证集并没有参与训练过程梯度下降过程的,狭义上来讲是没有参与模型的参数训练更新的。
  2. 但是广义上来讲,验证集存在的意义确实参与了一个“人工调参”的过程,我们根据每一个epoch训练之后模型在valid data上的表现来决定是否需要训练进行early stop,或者根据这个过程模型的性能变化来调整模型的超参数,如学习率,batch_size等等。
  3. 因此,我们也可以认为,验证集也参与了训练,但是并没有使得模型去overfit验证集
  1. batch_size = 32
  2. img_height = 224
  3. img_width = 224
  1. train_ds = tf.keras.preprocessing.image_dataset_from_directory(
  2. "./data/train/",
  3. seed=123,
  4. image_size=(img_height, img_width),
  5. batch_size=batch_size)
  1. Found 502 files belonging to 2 classes.
  1. val_ds = tf.keras.preprocessing.image_dataset_from_directory(
  2. "./data/test/",
  3. seed=123,
  4. image_size=(img_height, img_width),
  5. batch_size=batch_size)
  1. Found 76 files belonging to 2 classes.

我们可以通过class_names输出数据集的标签。标签将按字母顺序对应于目录名称。

  1. class_names = train_ds.class_names
  2. print(class_names)
  1. ['adidas', 'nike']

2. 可视化数据

  1. plt.figure(figsize=(20, 10))
  2. for images, labels in train_ds.take(1):
  3. for i inrange(20):
  4. ax = plt.subplot(5, 10, i + 1)
  5. plt.imshow(images[i].numpy().astype("uint8"))
  6. plt.title(class_names[labels[i]])
  7. plt.axis("off")

3. 再次检查数据

  1. for image_batch, labels_batch in train_ds:
  2. print(image_batch.shape)
  3. print(labels_batch.shape)
  4. break
  1. (32, 224, 224, 3)
  2. (32,)

4. 配置数据集

  • shuffle() :打乱数据,关于此函数的详细介绍可以参考:🔗365天深度学习训练营

  • prefetch() :预取数据,加速运行

  1. prefetch()

功能详细介绍:CPU 正在准备数据时,加速器处于空闲状态。相反,当加速器正在训练模型时,CPU 处于空闲状态。因此,训练所用的时间是 CPU 预处理时间和加速器训练时间的总和。

  1. prefetch()

将训练步骤的预处理和模型执行过程重叠到一起。当加速器正在执行第 N 个训练步时,CPU 正在准备第 N+1 步的数据。这样做不仅可以最大限度地缩短训练的单步用时(而不是总用时),而且可以缩短提取和转换数据所需的时间。如果不使用

  1. prefetch()

,CPU 和 GPU/TPU 在大部分时间都处于空闲状态:

使用

  1. prefetch()

可显著减少空闲时间:

  • cache() :将数据集缓存到内存当中,加速运行
  1. AUTOTUNE = tf.data.AUTOTUNE
  2. train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
  3. val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

三、构建CNN网络

卷积神经网络(CNN)的输入是张量 (Tensor) 形式的 (image_height, image_width, color_channels),包含了图像高度、宽度及颜色信息。不需要输入batch size。color_channels 为 (R,G,B) 分别对应 RGB 的三个颜色通道(color channel)。在此示例中,我们的 CNN 输入的形状是 (224, 224, 3)即彩色图像。我们需要在声明第一层时将形状赋值给参数input_shape。

  1. num_classes = 2
  2. model = models.Sequential([
  3. layers.experimental.preprocessing.Rescaling(1./255, input_shape=(img_height, img_width, 3)),
  4. layers.Conv2D(16, (3, 3), activation='relu', input_shape=(img_height, img_width, 3)), # 卷积层1,卷积核3*3
  5. layers.AveragePooling2D((2, 2)), # 池化层1,2*2采样
  6. layers.Conv2D(32, (3, 3), activation='relu'), # 卷积层2,卷积核3*3
  7. layers.MaxPooling2D((2, 2)), # 池化层2,2*2采样
  8. layers.Dropout(0.3),
  9. layers.Conv2D(64, (3, 3), activation='relu'), # 卷积层3,卷积核3*3
  10. layers.Dropout(0.3),
  11. layers.Flatten(), # Flatten层,连接卷积层与全连接层
  12. layers.Dense(128, activation='relu'), # 全连接层,特征进一步提取
  13. layers.Dense(num_classes) # 输出层,输出预期结果
  14. ])
  15. model.summary() # 打印网络结构
  1. Model: "sequential"
  2. _________________________________________________________________
  3. Layer (type) Output Shape Param #
  4. =================================================================
  5. rescaling (Rescaling) (None, 224, 224, 3) 0
  6. _________________________________________________________________
  7. conv2d (Conv2D) (None, 222, 222, 16) 448
  8. _________________________________________________________________
  9. average_pooling2d (AveragePo (None, 111, 111, 16) 0
  10. _________________________________________________________________
  11. conv2d_1 (Conv2D) (None, 109, 109, 32) 4640
  12. _________________________________________________________________
  13. max_pooling2d (MaxPooling2D) (None, 54, 54, 32) 0
  14. _________________________________________________________________
  15. dropout (Dropout) (None, 54, 54, 32) 0
  16. _________________________________________________________________
  17. conv2d_2 (Conv2D) (None, 52, 52, 64) 18496
  18. _________________________________________________________________
  19. dropout_1 (Dropout) (None, 52, 52, 64) 0
  20. _________________________________________________________________
  21. flatten (Flatten) (None, 173056) 0
  22. _________________________________________________________________
  23. dense (Dense) (None, 128) 22151296
  24. _________________________________________________________________
  25. dense_1 (Dense) (None, 2) 258
  26. =================================================================
  27. Total params: 22,175,138
  28. Trainable params: 22,175,138
  29. Non-trainable params: 0
  30. _________________________________________________________________

四、训练模型

在准备对模型进行训练之前,还需要再对其进行一些设置。以下内容是在模型的编译步骤中添加的:

  • 损失函数(loss):用于衡量模型在训练期间的准确率。
  • 优化器(optimizer):决定模型如何根据其看到的数据和自身的损失函数进行更新。
  • 指标(metrics):用于监控训练和测试步骤。以下示例使用了准确率,即被正确分类的图像的比率。

1. 设置动态学习率

  1. # 设置初始学习率
  2. initial_learning_rate = 0.0001
  3. lr_schedule = tf.keras.optimizers.schedules.ExponentialDecay(
  4. initial_learning_rate,
  5. decay_steps=50, # 敲黑板!!!这里是指 steps,不是指epochs
  6. decay_rate=0.98, # lr经过一次衰减就会变成 decay_rate*lr
  7. staircase=True)
  8. # 将指数衰减学习率送入优化器
  9. optimizer = tf.keras.optimizers.Adam(learning_rate=lr_schedule)
  10. model.compile(optimizer=optimizer,
  11. loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
  12. metrics=['accuracy'])

学习率大与学习率小的优缺点分析:

学习率大

● 优点: ○ 1、加快学习速率。 ○ 2、有助于跳出局部最优值。 ● 缺点: ○ 1、导致模型训练不收敛。 ○ 2、单单使用大学习率容易导致模型不精确。

学习率小

● 优点: ○ 1、有助于模型收敛、模型细化。 ○ 2、提高模型精度。 ● 缺点: ○ 1、很难跳出局部最优值。 ○ 2、收敛缓慢。

注意:这里设置的动态学习率为:指数衰减型(ExponentialDecay)。在每一个epoch开始前,学习率(learning_rate)都将会重置为初始学习率(initial_learning_rate),然后再重新开始衰减。计算公式如下:

learning_rate = initial_learning_rate * decay_rate ^ (step / decay_steps)

2. 早停与保存最佳模型参数

EarlyStopping()参数说明:

  • monitor: 被监测的数据。
  • min_delta: 在被监测的数据中被认为是提升的最小变化, 例如,小于 min_delta 的绝对变化会被认为没有提升。
  • patience: 没有进步的训练轮数,在这之后训练就会被停止。
  • verbose: 详细信息模式。
  • mode: {auto, min, max} 其中之一。 在 min 模式中, 当被监测的数据停止下降,训练就会停止;在 max 模式中,当被监测的数据停止上升,训练就会停止;在 auto 模式中,方向会自动从被监测的数据的名字中判断出来。
  • baseline: 要监控的数量的基准值。 如果模型没有显示基准的改善,训练将停止。
  • estore_best_weights: 是否从具有监测数量的最佳值的时期恢复模型权重。 如果为 False,则使用在训练的最后一步获得的模型权重。
  1. from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
  2. epochs = 60# 保存最佳模型参数
  3. checkpointer = ModelCheckpoint('best_model.h5',
  4. monitor='val_accuracy',
  5. verbose=1,
  6. save_best_only=True,
  7. save_weights_only=True)
  8. # 设置早停
  9. earlystopper = EarlyStopping(monitor='val_accuracy',
  10. min_delta=0.001,
  11. patience=20,
  12. verbose=1)

3. 模型训练

  1. history = model.fit(train_ds,
  2. validation_data=val_ds,
  3. epochs=epochs,
  4. callbacks=[checkpointer, earlystopper])
  1. Epoch 00057: val_accuracy did not improve from 0.88158
  2. Epoch 00057: early stopping

效果最好的一次,epoch走完了60次,val_accuracy为0.92,感觉选择一平均一最大池化比两平均效果好些。

五、模型评估

1. Loss与Accuracy图

  1. acc = history.history['accuracy']
  2. val_acc = history.history['val_accuracy']
  3. loss = history.history['loss']
  4. val_loss = history.history['val_loss']
  5. epochs_range = range(len(loss))
  6. plt.figure(figsize=(12, 4))
  7. plt.subplot(1, 2, 1)
  8. plt.plot(epochs_range, acc, label='Training Accuracy')
  9. plt.plot(epochs_range, val_acc, label='Validation Accuracy')
  10. plt.legend(loc='lower right')
  11. plt.title('Training and Validation Accuracy')
  12. plt.subplot(1, 2, 2)
  13. plt.plot(epochs_range, loss, label='Training Loss')
  14. plt.plot(epochs_range, val_loss, label='Validation Loss')
  15. plt.legend(loc='upper right')
  16. plt.title('Training and Validation Loss')
  17. plt.show()

  1. # 加载效果最好的模型权重
  2. model.load_weights('best_model.h5')
  3. from PIL import Image
  4. import numpy as np
  5. # img = Image.open("./45-data/Monkeypox/M06_01_04.jpg") #这里选择你需要预测的图片
  6. img = np.array(Image.open("./data/train/nike/1 (108).jpg")) #这里选择你需要预测的图片
  7. image = tf.image.resize(img, [img_height, img_width])
  8. img_array = tf.expand_dims(image, 0)
  9. predictions = model.predict(img_array) # 这里选用你已经训练好的模型print("预测结果为:",class_names[np.argmax(predictions)])
  1. 预测结果为: nike

一些收获与学习笔记:

1、对于卷积与池化

池化时,如果更注重整体、背景,可以选择平均池化,但会使得图片“模糊”;如果更注重纹理、线条边缘,可以选择最大池化,如本次运动鞋识别,最大池化效果好些。

一般而言,卷积层越多性能越好,但容易过拟合,池化层越多能回会降低模型预测的精准度。

即大的方向上,欠拟合加卷积,过拟合加池化。

**2、对于学习率 **

并非学得越精细越好,有时可能会陷入"极大值"区域而非"最大值"。

学习率大

  1. 优点: 1、加快学习速率。 2、有助于跳出局部最优值。 缺点: 1、导致模型训练不收敛。 2、单单使用大学习率容易导致模型不精确。

学习率小

  1. 优点: 1、有助于模型收敛、模型细化。 2、提高模型精度。 缺点: 1、很难跳出局部最优值。 2、收敛缓慢。

**3、batch_size **

Batch_size的作用:决定了下降的方向。

在神经网络训练时,如果数据集足够小,可将数据一次性全部喂给神经网络

但我们常常面临的是比较大的数据集,一次性喂给神经网络时,往往会出现内存/显存不足的现象。

此时,我们会把比较大的数据集,分批次喂给神经网络。

  • batch_size:表示一次性喂给神经网络多少数据。
  • batches:该值等于dataset除以batch_size。总的数据集是dataset,我们每次喂给神经网络batch_size个数据,一共要喂dataset/batch_size次,才可以把数据集全部处理一遍。
  • steps:该值等于batches。steps表示在一个epoch内,要迭代多少次才可以把所有的数据都训练一遍;显然,迭代次数等于dataset/batch_size。

在合理范围内,增大Batch_size的好处:

  • 提高了内存利用率以及大矩阵乘法的并行化效率;
  • 跑完一次epoch(全数据集)所需要的迭代次数减少,对相同的数据量,处理的速度比小的Batch_size要更快;
  • 在一定范围内,一般来说 Batch_Size 越大,其确定的下降方向越准,引起训练震荡越小。

盲目增大Batch_size,Batch_size过大的坏处:

  • 提高了内存利用率,但是内存容量可能撑不住;
  • 跑完一次epoch(全数据集)所需的迭代次数减少,要想达到相同的精度,其所花费的时间大大增加,从而对参数的修正也就显得更加缓慢;
  • Batch_Size 增大到一定程度,其确定的下降方向已经基本不再变化(会影响随机性的引入)。

一些经验之谈:

一般而言,根据GPU显存,设置为最大,而且一般要求是8的倍数(比如16,32,64),GPU内部的并行计算效率最高。
或者选择一部分数据,设置几个8的倍数的Batch_Size,看看loss的下降情况,再选用效果更好的值。

总结:

batch_size设的大一些,收敛得快,也就是需要训练的次数少,准确率上升的也很稳定,但是实际使用起来精度不高;
batch_size设的小一些,收敛得慢,可能准确率来回震荡,因此需要把基础学习速率降低一些,但是实际使用起来精度较高。

4、输入图片的大小

输入网络的图片大小要根据网络结构来确定。

主要看pool这个操作执行了几次,比如pool是2*2的,那么一次pool图像就缩小了一半。本实验执行了3次,就是2^3,那输入图片的尺寸就必须是2的3次方,8的倍数。

输入图片大小变小之后,batchsize可以调大一些。在不超内存的情况下,batch越大越好

5、others

  • 有时模型准确率高但损失函数反而偏大,可能是受到了少数极端错误分类样本的影响。
  • 对于训练集,在训练之前要进行shuffle操作,以确保模型的泛化能力。每一个epoch都需打乱数据的顺序,以使网络受到的调整更具有多样性。同时,我们会不断监督网络的训练效果。通常情况下,网络的性能提高速度会越来越慢,在几十到几百个epoch后网络的性能会趋于稳定,即性能基本不再提高.
  • 通过Dataset对象的cache()方法和prefetch()通过缓存到内存中加速模型运行。

本文转载自: https://blog.csdn.net/suic009/article/details/127007650
版权归原作者 老师我作业忘带了 所有, 如有侵权,请联系我们删除。

“CNN-运动鞋品牌识别”的评论:

还没有评论