深度学习-第T11周——优化器对比实验
- 深度学习-第T11周——优化器对比实验
- 一、前言
- 二、我的环境
- 三、前期工作
- 1、导入数据集
- 2、查看图片数目
- 3、查看数据
- 四、数据预处理
- 1、 加载数据
- 1、设置图片格式
- 2、划分训练集
- 3、划分验证集
- 4、查看标签
- 2、数据可视化
- 3、检查数据
- 4、配置数据集
- 五、搭建CNN网络
- 六、训练模型
- 七、模型评估
- 1、Loss和Acc图
- 2、模型评估
- 八、总结
深度学习-第T11周——优化器对比实验
一、前言
- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
二、我的环境
- 电脑系统:Windows 10
- 语言环境:Python 3.8.5
- 编译器:colab在线编译
- 深度学习环境:Tensorflow
三、前期工作
1、导入数据集
导入数据集,这里使用k同学的数据集,共17个分类。
import os, PIL, pathlib
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, models
import matplotlib.pyplot as pltdata_dir = "/content/drive/MyDrive/Training_Camp_DL/TensorFlow/CNN/48-data"
print(type(data_dir))
data_dir = pathlib.Path(data_dir)
print(type(data_dir))
这段代码将字符串类型的 data_dir 转换为了 pathlib.Path 类型的对象。pathlib 是 Python3.4 中新增的模块,用于处理文件路径。
通过 Path 对象,可以方便地操作文件和目录,如创建、删除、移动、复制等。
在这里,我们使用 pathlib.Path() 函数将 data_dir 转换为路径对象,这样可以更加方便地进行文件路径的操作和读写等操作。
2、查看图片数目
image_count = len(list(data_dir.glob("*/*.jpg")))
print("图片数目:", image_count)
获取指定目录下所有子文件夹中 jpg 格式的文件数量,并将其存储在变量 image_count 中。
data_dir 是一个路径变量,表示需要计算的目标文件夹的路径。
glob() 方法可以返回匹配指定模式(通配符)的文件列表,该方法的参数 “/.jpg” 表示匹配所有子文件夹下以 .jpg 结尾的文件。
list() 方法将 glob() 方法返回的生成器转换为列表,方便进行数量统计。最后,len() 方法计算列表中元素的数量,就得到了指定目录下 jpg 格式文件的总数。
所以,这行代码的作用就是计算指定目录下 jpg 格式文件的数量。
3、查看数据
roses = list(data_dir.glob("Jennifer Lawrence/*.jpg"))
print(type(roses), roses)
PIL.Image.open(str(roses[0]))
四、数据预处理
1、 加载数据
1、设置图片格式
batch_size = 16
img_height = 336
img_width = 336
2、划分训练集
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
)
这行代码使用 TensorFlow 读取指定路径下的图片文件,并生成一个 tf.data.Dataset 对象,用于模型的训练和评估。
具体来说,tf.keras.preprocessing.image_dataset_from_directory() 函数从指定目录中读取图像数据,并自动对其进行标准化和预处理。该函数有以下参数:
directory:指定图像文件所在目录的路径;
seed:用于随机划分数据集的种子;
image_size:指定图像缩放后的尺寸;
batch_size:指定批次中图像的数量。
通过这些参数,函数将指定目录中的图像按照指定大小预处理后,随机划分为训练集和验证集。最终,生成的 tf.data.Dataset 对象包含了划分好的数据集,可以用于后续的模型训练和验证。
需要注意的是,这里的 img_height 和 img_width 变量应该提前定义,并且应该与实际图像的尺寸相对应。同时,batch_size 也应该根据硬件设备的性能合理调整,以充分利用 GPU/CPU 的计算资源。
3、划分验证集
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
)
这段代码和上一段代码类似,使用 TensorFlow 的 keras.preprocessing.image_dataset_from_directory() 函数从指定的目录中读取图像数据集,并将其划分为训练集和验证集。
其中,data_dir 指定数据集目录的路径,validation_split 表示从数据集中划分出多少比例的数据作为验证集,subset 参数指定为 “validation” 则表示从数据集的 20% 中选择作为验证集,其余 80% 作为训练集。seed 是一个随机种子,用于生成可重复的随机数。image_size 参数指定输出图像的大小,batch_size 表示每批次加载的图像数量。
该函数返回一个 tf.data.Dataset 对象,代表了整个数据集(包含训练集和验证集)。可以使用 train_ds 和 val_ds 两个对象分别表示训练集和验证集。
不过两段代码的 subset 参数值不同,一个是 “training”,一个是 “validation”。
因此,在含有交叉验证或者验证集的深度学习训练过程中,需要定义两个数据集对象 train_ds 和 val_ds。我们已经定义了包含训练集和验证集的数据集对象 train_ds,可以省略这段代码,无需重复定义 val_ds 对象。只要确保最终的训练过程中,两个数据集对象都能够被正确地使用即可。
如果你没有定义 val_ds 对象,可以使用这段代码来创建一个验证数据集对象,用于模型训练和评估,从而提高模型性能。
4、查看标签
class_names = train_ds.class_names
class_names
train_ds.class_names 是一个属性,它是通过数据集对象 train_ds 中的类别信息自动生成的一个包含类别名称的列表。
在创建数据集对象 train_ds 时,你可以通过 class_names 参数手动指定类别名称,也可以根据图像文件夹的目录结构自动推断出来。
例如,假设你有一个包含猫和狗两种类别的图像数据集,其中猫类别的图像存储在 “cat” 文件夹中,狗类别的图像存储在 “dog” 文件夹中,
那么当你使用 keras.preprocessing.image_dataset_from_directory() 函数加载数据集时,会自动将 “cat” 和 “dog” 文件夹作为两个不同的类别,
并将它们的名称存储在 train_ds.class_names 属性中。
执行该代码后,你就可以在控制台或者输出窗口中看到包含数据集中所有类别名称的列表。这些名称通常是按照字母顺序排列的
因此,train_ds.class_names 属性可以让你方便地查看数据集中所有的类别名称,以便后续的模型训练、预测和评估等任务。
如果你要对数据集进行多类别分类,则需要根据 train_ds.class_names 的元素个数设置输出层的神经元数量,并将每个类别与一个唯一的整数标签相关联。
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[np.argmax(labels[i])]) #返回当前处理的图像所代表的类别的索引。plt.axis("off")
train_ds.take(1) 是一个方法调用,它返回一个数据集对象 train_ds 中的子集,其中包含了 take() 方法参数指定的数量的样本。
在这个例子中,take(1) 意味着我们从 train_ds 数据集中获取一批包含一个样本的数据块。
因此,for images, labels in train_ds.take(1): 的作用是遍历这个包含一个样本的数据块,并将其中的图像张量和标签张量依次赋值给变量 images 和 labels。具体来说,
它的执行过程如下:
从 train_ds 数据集中获取一批大小为 1 的数据块。
遍历这个数据块,每次获取一个图像张量和一个标签张量。
将当前图像张量赋值给变量 images,将当前标签张量赋值给变量 labels。
执行 for 循环中的代码块,即对当前图像张量和标签张量进行处理。
plt.imshow() 函数是 Matplotlib 库中用于显示图像的函数,它接受一个数组或张量作为输入,并在窗口中显示对应的图像。
在这个代码中,images[i] 表示从训练集中获取的第 i 个图像张量。由于 images 是一个包含多个图像的张量列表,因此使用 images[i] 可以获取其中的一个图像。
由于 imshow() 函数需要输入的数组或张量的类型是整型或浮点型,而从数据集中获取的图像张量通常是浮点型张量,因此需要将其转换为整型张量,以便进行显示。
这里使用了 .numpy().astype(“uint8”) 操作来将图像张量转换为整型张量(uint8 表示无符号8位整数),然后将结果传递给 plt.imshow() 函数进行显示。
因此,plt.imshow(images[i].numpy().astype(“uint8”)) 的作用是在 Matplotlib 窗口中显示训练集中的第 i 个图像。
你可以通过改变 i 的值来显示不同的图像,例如 i = 0 表示显示训练集中的第一张图像。
plt.axis(“off”) 是 Matplotlib 库中的一个函数调用,它用于控制图像显示时的坐标轴是否可见。
具体来说,当参数为 “off” 时,图像的坐标轴会被关闭,不会显示在图像周围。这个函数通常在 plt.imshow() 函数之后调用,以便在显示图像时去掉多余的细节信息,仅仅显示图像本身。
3、检查数据
for image_batch, labels_batch in train_ds:print(image_batch.shape)print(labels_batch.shape)break
4、配置数据集
##2.3、配置数据集
AUTOTUNE = tf.data.AUTOTUNE
"""
定义 AUTOTUNE 常量
这个常量的作用是指定 TensorFlow 数据管道读取数据时使用的线程个数,使得数据读取可以尽可能地并行化,提升数据读取效率。
具体来说,AUTOTUNE 的取值会根据系统资源和硬件配置等因素自动调节。"""def train_preprocessing(image, label):return (image / 255.0, label)"""这个函数的作用是对输入的图像数据进行预处理操作,其中 image 表示输入的原始图像数据,label 表示对应的标签信息。
函数体内的操作是把原始图像数据除以 255,使其数值归一化到 0 和 1 之间。
函数返回一个元组 (image / 255.0, label),其中第一个元素是经过处理后的图像数据,第二个元素是对应的标签信息。
"""#归一化处理train_ds = train_ds.cache().shuffle(1000).map(train_preprocessing).prefetch(buffer_size = AUTOTUNE)
val_ds = val_ds.cache().shuffle(1000).map(train_preprocessing).prefetch(buffer_size = AUTOTUNE)
AUTOTUNE 是 TensorFlow 的一个常量,它的值取决于当前硬件配置和运行环境。
它表示 TensorFlow 数据处理流程中可以自动选择最优化参数(例如 GPU 处理数量等)的范围,在不同的硬件配置下可能会有不同的取值。
在这个代码片段中,AUTOTUNE 的作用是为数据集的预处理过程提供了一个启发式的缓冲大小,以便更好地平衡内存使用和计算速度。
train_preprocessing 函数定义了对输入图像数据进行预处理的操作,其中 image 表示输入的原始图像数据,label 表示对应的标签信息。函数体内的操作是将原始图像数据除以 255,使其数值归一化到 0 和 1 之间。函数返回一个元组 (image / 255.0, label),其中第一个元素是经过处理后的图像数据,第二个元素是对应的标签信息。
接下来,训练数据集 train_ds 和验证数据集 val_ds 进行了一系列操作。首先使用 cache() 方法对数据集进行缓存,然后使用 shuffle(1000) 方法随机打乱数据集中的样本,接着使用 map(train_preprocessing) 方法对每个样本应用 train_preprocessing 函数进行预处理,最后使用 prefetch(buffer_size = AUTOTUNE) 方法预取数据以提高训练的效率。
五、搭建CNN网络
from tensorflow.keras.layers import BatchNormalization
#from tensorflow.keras.models import Modeldef create_model(optimizer = 'adam'):vgg16_base_model = tf.keras.applications.vgg16.VGG16(weights = 'imagenet', include_top = False, input_shape = (img_width, img_height, 3), pooling = 'avg')for layer in vgg16_base_model.layers:layer.trainable = FalseX = vgg16_base_model.outputX = Dense(170, activation = 'relu')(X)X = BatchNormalization()(X)X = Dropout(0.5)(X)output = Dense(len(class_names), activation = 'softmax')(X)vgg16_model = Model(inputs = vgg16_base_model.input, outputs = output)vgg16_model.compile(optimizer = optimizer,loss = 'sparse_categorical_crossentropy',metrics = ['accuracy'])return vgg16_modelmodel1 = create_model(optimizer = tf.keras.optimizers.Adam())
model2 = create_model(optimizer = tf.keras.optimizers.SGD())
model2.summary()
首先,从 tensorflow.keras.layers 模块导入了 BatchNormalization 层。注释掉的那行代码导入了 Model 类,但在代码中并没有使用到。
接下来,定义了一个名为 create_model 的函数,该函数接受一个参数 optimizer(默认为 ‘adam’)。函数内部首先使用 tf.keras.applications.vgg16.VGG16 加载了预训练的 VGG16 模型,其中 weights = ‘imagenet’ 表示使用 ImageNet 数据集的预训练权重,include_top = False 表示不包含顶层的全连接层,input_shape 设置输入图像的形状,pooling = ‘avg’ 表示使用平均池化。
然后,对 VGG16 模型的所有层进行循环遍历,将每一层的 trainable 属性设置为 False,即冻结预训练参数,使它们在训练过程中保持不变。
接下来,将 VGG16 模型的输出作为输入,并经过一系列的神经网络层进行特征提取和分类。首先是全连接层 Dense(170, activation = ‘relu’),然后是批归一化层 BatchNormalization(),最后是丢弃层 Dropout(0.5),用于防止过拟合。
最后一层是输出层 Dense(len(class_names), activation = ‘softmax’),其中 len(class_names) 是类别的数量,根据具体情况进行设置,激活函数使用 softmax。
接下来,使用 Model 函数创建了一个新的模型 vgg16_model,指定了输入和输出。这里的 vgg16_base_model.input 表示 VGG16 模型的输入,output 表示上述神经网络层的输出。
然后,使用 compile 方法编译了模型,指定了优化器、损失函数和评估指标。优化器根据传入的参数进行选择,如果没有指定,默认为 Adam 优化器。损失函数使用稀疏分类交叉熵,评估指标为准确率。
最后,函数返回创建的模型。
接下来,通过调用 create_model 函数分别创建了两个模型 model1 和 model2,分别使用了 Adam 优化器和 SGD 优化器。最后调用 model2.summary() 打印了 model2 的模型摘要信息。
六、训练模型
NO_EPOCHS = 50
history_model1 = model1.fit(train_ds, epochs = NO_EPOCHS, verbose = 1, validation_data = val_ds)
history_model2 = model2.fit(train_ds, epochs = NO_EPOCHS, verbose = 1, validation_data = val_ds)
使用 fit 方法训练了 model1 和 model2 模型,并将训练过程中的指标记录在 history_model1 和 history_model2 中。
参数 train_ds 是训练数据集,epochs 指定训练轮数,verbose 控制是否打印训练过程中的日志信息(这里设置为 1,表示打印日志),validation_data 是验证数据集,用于在每个训练轮结束后评估模型性能。
七、模型评估
1、Loss和Acc图
from matplotlib.ticker import MultipleLocator
plt.rcParams['savefig.dpi'] = 300 #图片像素
plt.rcParams['figure.dpi'] = 300 #分辨率acc1 = history_model1.history['accuracy']
acc2 = history_model2.history['accuracy']
val_acc1 = history_model1.history['val_accuracy']
val_acc2 = history_model2.history['val_accuracy']loss1 = history_model1.history['loss']
loss2 = history_model2.history['loss']
val_loss1 = history_model1.history['val_loss']
val_loss2 = history_model2.history['val_loss']epochs_range = range(len(acc1))
plt.figure(figsize = (16, 4))
plt.subplot(1, 2, 1)plt.plot(epochs_range, acc1, label = 'Training Accuracy-Adam')
plt.plot(epochs_range, acc2, label = 'Training Accuracy-SGD')
plt.plot(epochs_range, val_acc1, label = 'Validation Accuracy-Adam')
plt.plot(epochs_range, val_acc2, label = 'Validation Accuracy-SGD')
plt.legend(loc = 'lower right')
plt.title('Training and Validation Accuracy')
ax = plt.gca()
ax.xaxis.set_major_locator(MultipleLocator(1))plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss1, label = 'Training Loss-Adam')
plt.plot(epochs_range, loss2, label = 'Training Loss-SGD')
plt.plot(epochs_range, val_loss1, label = 'Validation Loss-Adam')
plt.plot(epochs_range, val_loss2, label = 'Validation Loss-SGD')
plt.legend(loc = 'upper right')
plt.title('Training and Validation Loss')
ax = plt.gca()
ax.xaxis.set_major_locator(MultipleLocator(1))plt.show()
2、模型评估
def test_accuracy_report(model):score = model.evaluate(val_ds, verbose = 0)print('Loss function; %s, accuracy:' % score[0], score[1])print('model1:')
test_accuracy_report(model1)
print('model2:')
test_accuracy_report(model2)
'''
八、总结
通过本次实验,学会了进行对比实验,可以比较不同优化器在训练过程中的性能表现,可视化训练过程的损失曲线和准确率等指标。这是一项非常重要的技能,在研究论文中,我们可以通过这些优化方法可以提高工作量。