- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊 | 接辅导、项目定制
一、前期工作
1.设置GPU
from tensorflow import keras
from tensorflow.keras import layers,models
import os, PIL, pathlib
import matplotlib.pyplot as plt
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")
gpus
2.导入数据
代码知识点
data_dir = pathlib.Path(data_dir)
这句代码使用了Python的pathlib
模块来创建一个Path
对象。pathlib
是Python 3.4及更高版本中引入的一个标准库,用于处理文件路径。
pathlib.Path(data_dir)
表示将字符串data_dir
作为参数传递给Path
类的构造函数,创建一个表示文件路径的对象。这个对象可以用于执行各种文件操作,如读取、写入、检查文件是否存在等。
通过使用pathlib.Path
对象,我们可以更方便地处理文件路径,而无需手动拼接字符串或使用其他底层的文件操作函数。这使得代码更加简洁和可读性更好。
data_dir = "./P4/"
data_dir = pathlib.Path(data_dir)
3.查看数据
代码知识点
image_count = len(list(data_dir.glob('*/*.jpg')))
这一句代码使用了Python的glob
模块来匹配指定目录下的所有以.jpg
为扩展名的文件,并将结果转换为列表。然后使用len
函数计算列表中元素的数量,即图片文件的数量,并将结果赋值给变量image_count
。
具体来说,data_dir.glob('*/*.jpg')
表示在data_dir
目录下查找所有子目录中的以.jpg
为扩展名的文件。list()
函数将匹配到的文件路径转换为列表,然后通过len()
函数计算列表的长度,即图片文件的数量。最后将结果赋值给变量image_count
。
image_count = len(list(data_dir.glob('*/*.jpg')))
print("图片总数为:",image_count)
输出结果
图片总数为: 2142
代码知识点
首先,data_dir.glob('Monkeypox/*.jpg')
使用glob
函数在指定的目录(即data_dir
)下查找所有以"Monkeypox"为前缀且以".jpg"为扩展名的文件,并将结果转换为列表形式赋值给变量Monkeypox
。
然后,PIL.Image.open(str(Monkeypox[123]))
使用Python Imaging Library (PIL)中的Image.open()
函数打开列表Monkeypox
中索引为123的元素,即第124个图片文件。由于Monkeypox
是一个列表,因此需要将其索引为123的元素转换为字符串类型,以便作为Image.open()
函数的参数。
最后,该代码将返回一个表示第124个图片文件的对象,可以对其进行进一步的操作或显示等操作。
Monkeypox = list(data_dir.glob('Monkeypox/*.jpg'))
PIL.Image.open(str(Monkeypox[123]))
输出结果
二、数据预处理
1.加载数据
代码知识点
调大batchsize会使得模型训练速度更快,但可能会降低模型的泛化能力。因为较大的batchsize意味着每次更新参数时使用的训练样本更多,这会导致模型对训练数据的噪声更加敏感,从而降低了模型在未见过的数据上的性能。
调小batchsize则会使得模型训练速度变慢,但可能会提高模型的泛化能力。较小的batchsize意味着每次更新参数时使用的训练样本较少,这会使模型对训练数据的细节更加敏感,从而提高了模型在未见过的数据上的性能。然而,过小的batchsize可能会导致训练不稳定或收敛速度较慢。
batch_size = 32
img_height = 224
img_width = 224
代码知识点
还可以参考K同学啊的这篇文章
这段代码是使用TensorFlow的Keras API来创建一个图像数据集。下面是对这段代码的详细讲解:
tf.keras.preprocessing.image_dataset_from_directory
: 这是TensorFlow中用于从目录加载图像数据的函数。它返回一个包含图像数据的数据集,可以用于训练和验证模型。
data_dir
: 这是一个字符串,表示包含图像数据的目录路径。这个目录应该包含两个子目录,一个用于训练数据,另一个用于验证数据。
validation_split=0.2
: 这个参数指定了将多少比例的数据作为验证集。在这个例子中,20%的数据将被用作验证集。
shuffle=True
: 这个参数指定是否在每个epoch开始时对数据进行洗牌。这有助于提高模型的泛化能力。
subset="training"
: 这个参数指定要加载的子集。在这个例子中,我们只加载训练集。
seed=123
: 这个参数指定了随机数生成器的种子,以确保每次运行时都能得到相同的结果。
image_size=(img_height, img_width)
: 这个参数指定了图像的大小。在这个例子中,图像的高度和宽度分别由img_height
和img_width
变量指定。
batch_size=batch_size
: 这个参数指定了每个批次中的样本数量。在这个例子中,批次大小由batch_size
变量指定。
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
data_dir,
validation_split=0.2,
shuffle=True,
subset="training",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size)
输出
Found 2142 files belonging to 2 classes.
Using 1714 files for training.
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
data_dir,
validation_split=0.2,
shuffle=True,
subset="validation",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size)
输出
Found 2142 files belonging to 2 classes.
Using 428 files for validation.
class_names = train_ds.class_names
print(class_names)
输出
[‘Monkeypox’, ‘Others’]
2.可视化数据
代码知识点
plt.figure(figsize=(20, 10))
: 创建一个大小为20x10英寸的图形窗口。
for images, labels in train_ds.take(1):
: 从train_ds
数据集中获取前1个批次的数据,每次迭代时,images
变量将包含当前批次的图像数据,而labels
变量将包含对应的标签数据。
for i in range(20):
: 外层循环控制批次的数量,内层循环控制子图的数量。这里设置的是每个批次显示20个子图。
ax = plt.subplot(5, 10, i + 1)
: 创建一个子图对象ax
,并使用plt.subplot()
函数指定了子图的位置和布局。plt.subplot()
函数的第一个参数是子图的总行数,第二个参数是子图的总列数,第三个参数是当前子图的索引(从1开始)。通过设置不同的行数和列数,可以控制子图的排列方式。
plt.imshow(images[i].numpy().astype("uint8"))
: 使用plt.imshow()
函数在当前子图上显示图像。images[i]
表示当前批次中的第i
个图像,.numpy()
将其转换为NumPy数组,然后使用.astype("uint8")
将数据类型转换为无符号8位整数。这样,图像将以正确的格式显示在子图中。
plt.title(class_names[labels[i]])
: 设置当前子图的标题。class_names
是一个列表,其中包含了所有可能的类别名称。通过使用labels[i]
作为索引,可以从列表中获取当前图像对应的类别名称,并将其设置为子图的标题。
plt.axis("off")
: 关闭坐标轴,使得图像只显示图像内容而不显示坐标轴。这样可以提高图像的可读性。
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")
输出
3.再次检查数据
for image_batch, labels_batch in train_ds:
print(image_batch.shape)
print(labels_batch.shape)
break #只处理一个批次的
输出
(32, 224, 224, 3)
(32,)
4.配置数据集
代码知识点
这段代码是用于优化数据加载和预处理过程:
AUTOTUNE = tf.data.AUTOTUNE
:tf.data.AUTOTUNE
是一个特殊的值,用于告诉TensorFlow在运行时自动调整数据加载和预处理的参数,以获得最佳性能。
train_ds = train_ds.cache()
: 这行代码将训练数据集(train_ds
)缓存到内存中。缓存可以将数据集存储在内存中,以便更快地访问和处理数据。这对于大型数据集非常有用,因为它可以避免重复读取和预处理数据。
train_ds = train_ds.shuffle(1000)
: 这行代码对训练数据集进行随机洗牌。洗牌操作会随机打乱数据集中的样本顺序,有助于提高模型的泛化能力。这里的1000
表示洗牌缓冲区的大小,即每次从数据集中抽取多少个样本进行洗牌。
train_ds = train_ds.prefetch(buffer_size=AUTOTUNE)
: 这行代码使用prefetch
方法预取训练数据集的下一个批次。预取操作可以在数据实际被模型使用之前将其加载到内存中,从而减少等待时间并提高数据处理速度。buffer_size=AUTOTUNE
表示使用前面定义的AUTOTUNE
变量作为预取缓冲区的大小。
val_ds = val_ds.cache()
: 这行代码将验证数据集(val_ds
)缓存到内存中。与训练数据集类似,缓存可以加速数据的访问和处理。
val_ds = val_ds.prefetch(buffer_size=AUTOTUNE)
: 这行代码使用prefetch
方法预取验证数据集的下一个批次。与训练数据集类似,这里也使用了buffer_size=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网络
代码知识点
num_classes = 2
:定义了分类任务的类别数量,这里是二分类问题。
model = models.Sequential([])
:创建一个顺序模型,即模型的每一层按照添加的顺序依次堆叠起来。
layers.experimental.preprocessing.Rescaling(1./255, input_shape=(img_height, img_width, 3))
:对输入图像进行预处理,将像素值缩放到0到1之间。
(1).
layers.experimental.preprocessing
:这是一个模块,包含了一些用于图像预处理的函数和类。在这个例子中,我们使用了其中的Rescaling
类来进行归一化处理。(2).
Rescaling
:这是一个类,用于对输入的数据进行归一化处理。归一化是将数据缩放到一个特定的范围(通常是0到1之间)的过程,这样可以使得模型更容易学习。(3).
(1./255, input_shape=(img_height, img_width, 3))
:这是Rescaling
类的构造函数,接受两个参数。第一个参数是一个浮点数,表示将输入数据乘以这个值进行归一化。在这个例子中,我们将输入数据乘以1/255,即将像素值从0-255的范围缩放到0-1的范围。第二个参数是一个元组,表示输入数据的维度。在这个例子中,我们假设输入数据的形状为(img_height, img_width, 3)
,即高度、宽度和通道数。
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()
输出
四、编译
代码知识点
这段代码是用于设置和编译一个TensorFlow模型的。下面是逐行解释:
opt = tf.keras.optimizers.Adam(learning_rate=1.3e-4)
这一行代码创建了一个Adam优化器对象,并将其赋值给变量opt
。Adam是一种常用的优化算法,它结合了RMSProp和Momentum的优点,能够自适应地调整学习率。在这里,学习率被设置为1.3e-4,即0.00013。model.compile(optimizer=opt , loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=['accuracy'])
这一行代码调用了模型对象的compile
方法,用于配置模型的训练过程。其中,optimizer
参数指定了使用的优化器对象为之前创建的opt
。loss
参数指定了损失函数,这里使用了稀疏分类交叉熵损失函数(SparseCategoricalCrossentropy
),并设置了from_logits=True
,表示模型输出的是未经过激活函数处理的原始分数。metrics
参数指定了评估指标,这里选择了准确率(accuracy
)。
opt = tf.keras.optimizers.Adam(learning_rate=1e-3)# 这里学习率设置得有一些偏高了
model.compile(optimizer=opt,
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
关于优化器
Adam优化器的优点是:
- 自适应学习率,能够根据参数的更新情况自动调整学习率;
- 自带动量,能够加速收敛;
- 自带偏差校正,能够解决梯度下降中的高频震荡问题。
Adam优化器的缺点是:
- 可能无法很好地处理超大规模的数据集,因为其在每个参数上都会计算指数移动平均,所以内存占用可能会很大;
- 由于其默认的学习率通常较小,因此训练时间可能会比较长。
SGD优化器的优点是:
- 简单易懂,实现起来非常方便;
- 可以很好地处理稀疏数据。
SGD优化器的缺点是:
- 学习率需要手动设置,且可能需要针对不同的问题进行细致的调整;
- 容易陷入
局部最优解
。
什么是局部最优解?
局部最优解是在某个特定搜索空间中,比其邻近解更优的解决方案。但若只着眼于局部,可能会忽视掉其他可能的全局最优解,导致结果并非真正最优化。在启发式算法中,局部最优值的陷入往往无法避免,因为这类算法本质上采用了一种贪心策略,这在客观上决定了不符合贪心规则的更好(或者最优)解会错过。陷入局部最优解并不全然是坏事。对于某些问题规模较大的情况,例如大规模NP-hard问题,找到全局最优解在合理的时间内几乎是不可能的。这时候,我们往往会倾向于接受局部最优解,因为局部最优解的质量不一定都是差的。
然而,如果算法的目标是找到全局最优解,那么陷入局部最优解就是不利的。此时,可以采取“跳出”或“重启”两种手段来应对。一是在当前解的基础上向其他方向搜索,二是无视当前解并在新的区域重新搜索。例如,爬山算法实现简单,但其主要的弱点就是可能会陷入局部最优解,而不能保证搜索到全局最优解。
五、训练模型
代码知识点
这段代码是用于训练一个深度学习模型的。下面是逐行解释:
python from tensorflow.keras.callbacks import ModelCheckpoint
这行代码导入了ModelCheckpoint
类,它是Keras中用于在训练过程中保存模型权重和最佳性能的回调函数。python checkpointer = ModelCheckpoint(……)
这行代码创建了一个ModelCheckpoint
对象,用于在训练过程中保存模型的最佳性能。具体来说,它指定了以下参数:
'best_model.h5'
:保存模型的文件名;monitor='val_accuracy'
:监控指标为验证集上的准确率;verbose=1
:在训练过程中输出详细信息;save_best_only=True
:仅保存具有最佳性能的模型;save_weights_only=True
:仅保存模型的权重,不保存整个模型结构。
python history = model.fit(train_ds, validation_data=val_ds, epochs=epochs, callbacks=[checkpointer])
这行代码使用fit
方法来训练模型。它传递了以下参数:
train_ds
:训练数据集;validation_data=val_ds
:验证数据集;epochs=epochs
:训练轮数;callbacks=[checkpointer]
:回调函数列表,包括之前创建的checkpointer
对象。这段代码的作用是在训练深度学习模型时,使用
ModelCheckpoint
回调函数在每个epoch结束时保存具有最佳验证准确率的模型权重。
from tensorflow.keras.callbacks import ModelCheckpoint
epochs = 30
checkpointer = ModelCheckpoint('best_model.h5',
monitor='val_accuracy'
# 当verbose=0时,表示不输出任何信息。这意味着在训练过程中,不会显示有关保存模型的任何消息或进度更新。
verbose=1,
save_best_only=True,
save_weights_only=True)
history = model.fit(train_ds,
validation_data=val_ds,
epochs=epochs,
callbacks=[checkpointer])
输出
六、模型评估
1.Loss and Acurracy图
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()
输出
2.指定图片进行预测
model.load_weights('best_model.h5')
from PIL import Image
import numpy as np
# 打开图像并转换为RGB模式
image = Image.open("./P4/Monkeypox/M06_01_04.jpg").convert("RGB")
# 将图像转换为NumPy数组
numpy_image = np.array(image)
# 将NumPy数组转换为Tensor
tensor_image = tf.convert_to_tensor(numpy_image, dtype=tf.float32)
img_array = tf.expand_dims(tensor_image, 0)
predictions = model.predict(img_array) # 这里选用你已经训练好的模型
print("预测结果为:",class_names[np.argmax(predictions)])
输出
1/1 [==============================] - 0s 134ms/step
预测结果为: Monkeypox
我最后一步在使用教案中的代码时出现了这个报错
Attempt to convert a value (<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=224x224 at 0x1FACC4153C0>) with an unsupported type (<class 'PIL.JpegImagePlugin.JpegImageFile'>) to a Tensor.
搜索出来说是
这个报错是因为你试图将一个不支持的类型(PIL.JpegImagePlugin.JpegImageFile)转换为Tensor。要解决这个问题,你需要先将图像转换为NumPy数组,然后再将其转换为Tensor。
然后后来转化成numpy数组后就行了。