第T4周:猴痘病识别

一、前期工作

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来创建一个图像数据集。下面是对这段代码的详细讲解:

  1. tf.keras.preprocessing.image_dataset_from_directory: 这是TensorFlow中用于从目录加载图像数据的函数。它返回一个包含图像数据的数据集,可以用于训练和验证模型。

  2. data_dir: 这是一个字符串,表示包含图像数据的目录路径。这个目录应该包含两个子目录,一个用于训练数据,另一个用于验证数据。

  3. validation_split=0.2: 这个参数指定了将多少比例的数据作为验证集。在这个例子中,20%的数据将被用作验证集。

  4. shuffle=True: 这个参数指定是否在每个epoch开始时对数据进行洗牌。这有助于提高模型的泛化能力。

  5. subset="training": 这个参数指定要加载的子集。在这个例子中,我们只加载训练集。

  6. seed=123: 这个参数指定了随机数生成器的种子,以确保每次运行时都能得到相同的结果。

  7. image_size=(img_height, img_width): 这个参数指定了图像的大小。在这个例子中,图像的高度和宽度分别由img_heightimg_width变量指定。

  8. 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.可视化数据

代码知识点

  1. plt.figure(figsize=(20, 10)): 创建一个大小为20x10英寸的图形窗口。

  2. for images, labels in train_ds.take(1):: 从train_ds数据集中获取前1个批次的数据,每次迭代时,images变量将包含当前批次的图像数据,而labels变量将包含对应的标签数据。

  3. for i in range(20):: 外层循环控制批次的数量,内层循环控制子图的数量。这里设置的是每个批次显示20个子图。

  4. ax = plt.subplot(5, 10, i + 1): 创建一个子图对象ax,并使用plt.subplot()函数指定了子图的位置和布局。plt.subplot()函数的第一个参数是子图的总行数,第二个参数是子图的总列数,第三个参数是当前子图的索引(从1开始)。通过设置不同的行数和列数,可以控制子图的排列方式。

  5. plt.imshow(images[i].numpy().astype("uint8")): 使用plt.imshow()函数在当前子图上显示图像。images[i]表示当前批次中的第i个图像,.numpy()将其转换为NumPy数组,然后使用.astype("uint8")将数据类型转换为无符号8位整数。这样,图像将以正确的格式显示在子图中。

  6. plt.title(class_names[labels[i]]): 设置当前子图的标题。class_names是一个列表,其中包含了所有可能的类别名称。通过使用labels[i]作为索引,可以从列表中获取当前图像对应的类别名称,并将其设置为子图的标题。

  7. 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.配置数据集

代码知识点
这段代码是用于优化数据加载和预处理过程:

  1. AUTOTUNE = tf.data.AUTOTUNE: tf.data.AUTOTUNE是一个特殊的值,用于告诉TensorFlow在运行时自动调整数据加载和预处理的参数,以获得最佳性能。

  2. train_ds = train_ds.cache(): 这行代码将训练数据集(train_ds)缓存到内存中。缓存可以将数据集存储在内存中,以便更快地访问和处理数据。这对于大型数据集非常有用,因为它可以避免重复读取和预处理数据。

  3. train_ds = train_ds.shuffle(1000): 这行代码对训练数据集进行随机洗牌。洗牌操作会随机打乱数据集中的样本顺序,有助于提高模型的泛化能力。这里的1000表示洗牌缓冲区的大小,即每次从数据集中抽取多少个样本进行洗牌。

  4. train_ds = train_ds.prefetch(buffer_size=AUTOTUNE): 这行代码使用prefetch方法预取训练数据集的下一个批次。预取操作可以在数据实际被模型使用之前将其加载到内存中,从而减少等待时间并提高数据处理速度。buffer_size=AUTOTUNE表示使用前面定义的AUTOTUNE变量作为预取缓冲区的大小。

  5. val_ds = val_ds.cache(): 这行代码将验证数据集(val_ds)缓存到内存中。与训练数据集类似,缓存可以加速数据的访问和处理。

  6. 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网络

代码知识点

  1. num_classes = 2:定义了分类任务的类别数量,这里是二分类问题。

  2. model = models.Sequential([]):创建一个顺序模型,即模型的每一层按照添加的顺序依次堆叠起来。

  3. 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模型的。下面是逐行解释:

  1. opt = tf.keras.optimizers.Adam(learning_rate=1.3e-4)这一行代码创建了一个Adam优化器对象,并将其赋值给变量opt。Adam是一种常用的优化算法,它结合了RMSProp和Momentum的优点,能够自适应地调整学习率。在这里,学习率被设置为1.3e-4,即0.00013。
  2. model.compile(optimizer=opt , loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=['accuracy']) 这一行代码调用了模型对象的compile方法,用于配置模型的训练过程。其中,optimizer参数指定了使用的优化器对象为之前创建的optloss参数指定了损失函数,这里使用了稀疏分类交叉熵损失函数(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问题,找到全局最优解在合理的时间内几乎是不可能的。这时候,我们往往会倾向于接受局部最优解,因为局部最优解的质量不一定都是差的。

然而,如果算法的目标是找到全局最优解,那么陷入局部最优解就是不利的。此时,可以采取“跳出”或“重启”两种手段来应对。一是在当前解的基础上向其他方向搜索,二是无视当前解并在新的区域重新搜索。例如,爬山算法实现简单,但其主要的弱点就是可能会陷入局部最优解,而不能保证搜索到全局最优解。

五、训练模型

代码知识点
这段代码是用于训练一个深度学习模型的。下面是逐行解释:

  1. python from tensorflow.keras.callbacks import ModelCheckpoint
    这行代码导入了ModelCheckpoint类,它是Keras中用于在训练过程中保存模型权重和最佳性能的回调函数。
  2. python checkpointer = ModelCheckpoint(……) 这行代码创建了一个ModelCheckpoint对象,用于在训练过程中保存模型的最佳性能。具体来说,它指定了以下参数:
  • 'best_model.h5':保存模型的文件名;
  • monitor='val_accuracy':监控指标为验证集上的准确率;
  • verbose=1:在训练过程中输出详细信息;
  • save_best_only=True:仅保存具有最佳性能的模型;
  • save_weights_only=True:仅保存模型的权重,不保存整个模型结构。
  1. 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数组后就行了。

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值