- 本文为365天深度学习训练营 中的学习记录博客
- 原作者:K同学啊
导入数据集,数据集(K同学啊)
from tensorflow import keras
from tensorflow.keras import layers, models
import os, PIL, pathlib
import matplotlib.pyplot as plt
import tensorflow as tf
data_dir = "/content/drive/MyDrive/Training_Camp_DL/TensorFlow/CNN/45-data"
data_dir = pathlib.Path(data_dir)
这段代码将字符串类型的 data_dir 转换为了 pathlib.Path 类型的对象。pathlib 是 Python3.4 中新增的模块,用于处理文件路径。
通过 Path 对象,可以方便地操作文件和目录,如创建、删除、移动、复制等。
在这里,我们使用 pathlib.Path() 函数将 data_dir 转换为路径对象,这样可以更加方便地进行文件路径的操作和读写等操作。
image_count = len(list(data_dir.glob("*/*.jpg")))
image_count
获取指定目录下所有子文件夹中 jpg 格式的文件数量,并将其存储在变量 image_count 中。
具体来说,data_dir 是一个路径变量,表示需要计算的目标文件夹的路径。glob() 方法可以返回匹配指定模式(通配符)的文件列表,该方法的参数 “/.jpg” 表示匹配所有子文件夹下以 .jpg 结尾的文件。
list() 方法将 glob() 方法返回的生成器转换为列表,方便进行数量统计。最后,len() 方法计算列表中元素的数量,就得到了指定目录下 jpg 格式文件的总数。
所以,这行代码的作用就是计算指定目录下 jpg 格式文件的数量。
Monkeypox = list(data_dir.glob("Monkeypox/*.jpg"))
PIL.Image.open(str(Monkeypox[0]))
Monkeypox = list(data_dir.glob(“Monkeypox/*.jpg”)) 这行代码是使用 glob() 方法查找指定目录中所有名为 “Monkeypox” 的子文件夹,
然后在每个子文件夹中搜索所有的以 .jpg 结尾的文件。最终将搜索到的所有文件的路径以列表的形式存储在 Monkeypox 变量中。
接下来的 PIL.Image.open(str(Monkeypox[0])) 代码行则是打开 Monkeypox 列表中的第一个 jpg 文件,并将其转换为一个 PIL Image 对象。
因为 Monkeypox 列表中的元素都是文件路径(Path 对象),所以需要用 str() 函数将该路径对象转换为字符串类型,作为 PIL.Image.open() 方法的参数传入,然后方法会读取该路径指向的图像文件,并返回一个 PIL Image 对象。
这样,通过两行代码,我们就可以从指定目录下的所有 “Monkeypox” 子文件夹中获取第一张 jpg 格式的图片,并将其以 PIL Image 对象的形式加载到内存中,方便进行处理和操作。
batch_size = 32
img_height = 224
img_width = 224
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
)
这段代码使用 TensorFlow 的 keras.preprocessing.image_dataset_from_directory() 函数从指定的目录中读取图像数据集,并将其划分为训练集和验证集。
其中,data_dir 指定数据集目录的路径,validation_split 表示从数据集中划分出多少比例的数据作为验证集,
subset 参数指定为 “training” 则表示从剩下的数据中选择 80% 作为训练集,其余 20% 作为验证集。
seed 是一个随机种子,用于生成可重复的随机数。
image_size 参数指定输出图像的大小,batch_size 表示每批次加载的图像数量。
该函数返回一个 tf.data.Dataset 对象,代表了整个数据集(包含训练集和验证集)。可以使用 train_ds 和 val_ds 两个对象分别表示训练集和验证集。
在深度学习模型训练中,通常需要进行数据预处理和增强,例如对图像进行缩放、归一化、旋转、翻转等操作,
这些步骤可以通过传递给 keras.preprocessing.image_dataset_from_directory() 函数的各种参数来控制。这样可以方便地进行数据管道配置,使得训练过程更加高效、方便、快速。
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
)
这段代码和上一段代码类似,使用 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 对象,可以使用这段代码来创建一个验证数据集对象,用于模型训练和评估,从而提高模型性能。
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 的元素个数设置输出层的神经元数量,并将每个类别与一个唯一的整数标签相关联。
plt.figure(figsize = (20, 10))
for images, labels in train_ds.take(1):
for i in range(20):
plt.subplot(5, 10, i + 1)
plt.imshow(images[i].numpy().astype("uint8"))
plt.title(class_names[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() 函数之后调用,以便在显示图像时去掉多余的细节信息,仅仅显示图像本身。
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).prefetch(buffer_size = AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size = AUTOTUNE)
AUTOTUNE 是 TensorFlow 的一个常量,它的值取决于当前硬件配置和运行环境。
它表示 TensorFlow 数据处理流程中可以自动选择最优化参数(例如 GPU 处理数量等)的范围,在不同的硬件配置下可能会有不同的取值。
在这个代码片段中,AUTOTUNE 的作用是为数据集的预处理过程提供了一个启发式的缓冲大小,以便更好地平衡内存使用和计算速度。
train_ds.cache() 和 val_ds.cache() 函数是 Tensorflow 的数据转换函数,它们的作用是将数据集中的元素缓存到内存或者磁盘中,以便后续访问时能够更快地读取数据。
使用缓存可以避免由于磁盘 I/O 等因素导致数据读取速度变慢的问题,从而加速训练或评估过程。
train_ds.shuffle(1000) 函数是 Tensorflow 的数据转换函数,它的作用是将输入数据集中的元素随机打乱顺序。
这样做的目的是防止模型过拟合,并促进模型对不同数据的学习能力。其中,1000 表示用于对数据集进行重排的元素数量,其具体取值可以根据数据集大小进行调整。
train_ds.prefetch(buffer_size = AUTOTUNE) 和 val_ds.prefetch(buffer_size = AUTOTUNE) 函数是 Tensorflow 的数据转换函数,
它们的作用是将输入数据集中的元素通过 AUTOTUNE 参数定义的缓冲大小进行预加载。
具体来说,这个函数可以在当前训练或评估任务执行期间异步读取和处理数据集中的下一批样本,从而减少模型训练或评估过程中的等待时间和延迟。
因此,这段代码的作用是对训练集和验证集进行预处理,并将它们缓存到内存或磁盘中,以便更快地访问数据。
然后,它对训练集进行了一次随机打乱操作,并将缓存结果设为可以提前预加载的形式,以便在训练时能够快速地读取和处理数据。
最后,它将缓存结果也设置为可以提前预加载的形式,以加速评估过程。
num_classes = 2
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)),
layers.AveragePooling2D((2, 2)),
layers.Conv2D(32, (3, 3), activation = 'relu'),
layers.AveragePooling2D((2, 2)),
layers.Dropout(0.3),
layers.Conv2D(64, (3, 3), activation = 'relu'),
layers.Dropout(0.3),
layers.Flatten(),
layers.Dense(128, activation = 'relu'),
layers.Dense(num_classes)
])
model.summary()
layers.experimental.preprocessing.Rescaling(1./255, input_shape=(img_height, img_width, 3)) 是 TensorFlow 中的一个预处理层,
用于将图像数据缩放至 [0, 1] 范围内,在神经网络的训练过程中稳定模型训练过程。
在这个代码片段中,1./255 表示将输入数据除以 255,这样就把原来范围在 [0, 255] 的像素值缩放到 [0, 1] 的范围内。
这个操作可以使得神经网络的输入数据归一化,从而更好地适应不同的计算机视觉任务,例如图像分类、目标检测、语义分割等。
input_shape=(img_height, img_width, 3) 参数表示输入图像的形状,其中 img_height 和 img_width 分别表示图像的高度和宽度,而 3 表示通道数。
由于这个预处理层是作为神经网络模型的第一层使用的,因此需要指定输入数据的形状。
因此,这行代码的作用是将输入图像的像素值除以 255,从而将像素范围缩放到 [0, 1],并将其作为神经网络模型的第一层,
以便在训练过程中更好地归一化输入数据,并提高模型的鲁棒性和泛化性能。
opt = tf.keras.optimizers.Adam(learning_rate = 1e-4)
model.compile(
optimizer = opt,
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits = True),
metrics = ['accuracy']
)
opt = tf.keras.optimizers.Adam(learning_rate = 1e-4) 是定义了一个优化器,并设置了学习率。
在这里,我们使用了 Adam 优化器,它能够根据梯度自适应地调整学习率,从而加速模型的训练过程。
from_logits=True:表示我们要对模型的输出 logits 进行稀疏分类交叉熵损失计算。
如果将其设置为 False,则会对经过 softmax 函数处理后的输出进行计算。在这里,我们将其设置为 True,因为我们的模型最后一层没有加 softmax 函数。
tf.keras.losses.SparseCategoricalCrossentropy():这是 TensorFlow 中的稀疏分类交叉熵损失函数,它适用于分类问题中标签为整数的情况。
from tensorflow.keras.callbacks import ModelCheckpoint
epochs = 50
checkpointer = ModelCheckpoint(
'best_model.h5',
monitor = 'val_accuracy',
verbose = 1,
save_best_only = True,
save_weights_only = True
)
ModelCheckpoint 是 Keras 中的一个回调函数(Callback),用于在训练过程中保存模型的权重。
具体来说,对于每一轮训练后,该回调函数都可以按照一定的条件将这一轮训练得到的最佳模型权重保存下来。
使用 ModelCheckpoint 可以避免在训练过程中出现意外情况导致模型信息的丢失,同时也可以方便地对比不同阶段训练得到的模型效果,进行模型的选择和调整。
具体来说,这段代码中的参数设置如下:
best_model.h5:保存模型权重的文件路径和名称。
monitor=‘val_accuracy’:表示在验证集上监测模型的准确率并作为指标,以便在每个 epoch 结束时评估模型效果。当发现模型在验证集上的准确率有提高时,就会保存当前的模型权重。
verbose=1:表示每个 epoch 结束时,在控制台输出一条信息说明模型权重是否被更新了。
save_best_only=True:表示只有在模型效果提高时才保存模型权重。如果设为 False,则每个 epoch 结束后都会保存模型权重,不管模型效果是否提高。
save_weights_only=True:表示只保存模型的权重,而不保存模型的结构和配置信息。这样可以方便地加载已经训练好的模型权重,并重新构建模型。
综合起来,这段代码的作用是在训练过程中不断保存最佳的模型权重,以便后续的模型加载和使用。
history = model.fit(
train_ds,
validation_data = val_ds,
epochs = epochs,
callbacks = [checkpointer]
)
这段代码使用了 Keras 的 fit 方法来训练模型,并将 checkpointer 回调函数作为参数传入。
具体来说,这段代码中的参数设置如下:
train_ds:表示训练集数据的生成器或 Numpy 数组。
validation_data = val_ds:表示验证集数据的生成器或 Numpy 数组。在每个 epoch 结束时,Keras 会使用验证集数据来评估模型的性能,以便及时调整模型的参数和结构。
epochs = epochs:表示训练的 epoch 数量,即训练集数据将被遍历几次。
callbacks = [checkpointer]:表示在训练过程中需要调用的回调函数列表,其中包括我们定义的 checkpointer 回调函数。
在模型训练过程中,每个 epoch 结束后都会调用 checkpointer 回调函数,根据指定的条件保存模型权重。
综合起来,这段代码的作用是利用训练集和验证集数据对模型进行训练,同时利用 checkpointer 回调函数动态地保存模型的最佳权重,以便后续的模型加载和使用.
loss = history.history['loss']
val_loss = history.history['val_loss']
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
epochs_range = range(len(loss))
plt.figure(figsize = (12, 4))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label = "Training Acc")
plt.plot(epochs_range, val_acc, label = "Validation Acc")
plt.legend(loc = 'lower right')
plt.title("Training And Validation Acc")
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()
model.load_weights('best_model.h5')
#这段代码用于加载之前训练中保存的最佳模型权重。'best_model.h5' 指的是之前保存的模型权重文件路径和名称。
#这样可以避免从头开始训练模型,直接使用已经训练好的最佳模型进行预测的工作。
from PIL import Image
import numpy as np
img = Image.open("45-data/Monkeypox/M01_02_06.jpg") #使用 PIL 库中的 Image.open() 方法打开一张待预测的图片。
image = tf.image.resize(img, [img_height, img_width])
#这个函数调整输入图像的大小以符合模型的要求。
#在这个例子中,使用 TensorFlow 的 tf.image.resize() 函数将图像缩放为指定大小,其中 img_height 和 img_width 是指定的图像高度和宽度。
img_array = tf.expand_dims(image, 0)
'''
这个函数将输入图像转换为形状为 (1, height, width, channels) 的四维数组,
其中 height 和 width 是图像的高度和宽度,channels 是图像的通道数(例如 RGB 图像有 3 个通道)。
这里使用 TensorFlow 的 tf.expand_dims() 函数来扩展图像数组的维度,以匹配模型的输入格式。
具体来说:
image 是一个二维图片张量,它的形状是 (height, width, channels)。其中 height 和 width 分别为图片的高度和宽度,channels 为图片的颜色通道数。
0 是一个整数值,它指定在哪个维度上扩展此张量,这里表示在最前面(第一个)的维度上扩展。
因此,函数的作用是将输入张量 image 在最前面添加一个额外的维度(batch_size),生成一个四维张量。
tf.expand_dims(input, axis)
其中 input 表示要扩展的输入张量,axis 表示要在哪个维度上进行扩展。在这个例子中,input 是变量 image,axis 是 0。
'''
predictions = model.predict(img_array)
#这个函数用于对输入图像进行分类预测。它使用已经训练好的模型来对输入数据进行推断,并输出每个类别的概率分布。
print("预测结果为:", class_names[np.argmax(predictions)])
'''
将模型输出的概率分布转换为最终预测结果。
具体来说,使用 np.argmax() 函数找到概率最大的类别索引,然后使用该索引在 class_names 列表中查找相应的类别名称,并输出预测结果。
'''