1、绪论
1.1 图像分类的定义
图像分类是计算机视觉领域中的一项基本任务,其定义是将输入图像分配给预定义类别中的一个或多个。具体来说,图像分类系统接受一个图像作为输入,并输出一个或多个类别标签,这些标签描述了图像中的内容。
在图像分类中,通常使用有监督学习方法,这意味着训练数据集中的每个图像都已经被手动标记或注释了正确的类别。训练过程涉及学习从图像中提取特征(这些特征可能是颜色、形状、纹理、空间关系等),并基于这些特征来预测图像所属的类别。
图像分类在许多实际应用中发挥着重要作用:
-
自动标签系统:在社交媒体平台上,图像分类可以帮助自动为上传的照片添加标签或描述。
-
安全监控:在视频监控系统中,图像分类可以用于检测异常事件,如火灾、盗窃或交通违规。
-
医疗诊断:在医学领域,图像分类可以用于分析X光片、MRI扫描或病理切片,以辅助医生进行疾病诊断。
-
零售分析:在零售店中,通过图像分类可以识别顾客正在查看的商品,从而优化商品摆放和库存管理。
-
自动驾驶:在自动驾驶汽车中,图像分类可以帮助识别行人、车辆、交通标志和道路类型,以确保汽车安全行驶。
为了实现图像分类,通常会使用深度学习技术,特别是卷积神经网络(Convolutional Neural Networks, CNNs)。CNNs 能够从原始像素中自动学习层次化的特征表示,这些特征表示对于图像分类任务非常有效。通过在大规模数据集上训练CNNs,可以获得具有出色分类性能的模型。
1.2 使用Keras进行图像分类概述
Keras是一个高级神经网络库,作为TensorFlow的一部分,它提供了简单易用的接口,可以快速构建、训练和评估神经网络模型。在图像分类识别领域,Keras凭借其直观易用的API和强大的功能,成为了研究人员和开发者们的首选工具。
1.2.1 Keras图像分类的优势
Keras图像分类的优势
一、用户友好和易用性:
- Keras是一个高级神经网络API,它允许用户通过简单的Python代码来定义和训练深度学习模型。这使得即使对于初学者来说,也能快速上手并构建复杂的图像分类模型。
二、模块化设计:
- Keras的模块化设计允许用户将不同的神经网络层、优化器、损失函数等组件像搭积木一样组合在一起,从而构建出适合特定任务的模型。这种灵活性使得Keras非常适合于图像分类任务。
三、丰富的预训练模型:
- Keras提供了许多预训练的神经网络模型,如VGG、ResNet、Inception等。这些模型在大型数据集(如ImageNet)上进行了训练,并已经学习到了丰富的图像特征。用户可以直接使用这些预训练模型进行特征提取或微调,从而加速模型的训练过程并提高性能。
四、强大的GPU支持:
- Keras支持使用GPU进行加速计算,这使得在训练大规模图像分类模型时能够显著提高训练速度。GPU的并行计算能力使得模型能够在更短的时间内完成训练,从而加速实验迭代和模型优化。
五、可视化与调试:
- Keras提供了丰富的调试工具和可视化选项,如模型摘要、张量板(TensorBoard)集成等。这些工具可以帮助用户更好地理解模型的结构、参数和训练过程,从而更容易地发现问题并进行调试。
六、可扩展性:
- Keras是建立在TensorFlow或Theano等深度学习框架之上的,这意味着它继承了这些框架的强大功能和可扩展性。用户可以根据需要自定义神经网络层、损失函数等,以适应更复杂的图像分类任务。
七、社区支持和资源:
- Keras拥有一个庞大的用户社区和丰富的在线资源,包括教程、示例代码、模型库等。这些资源可以帮助用户更快地学习和掌握Keras的使用技巧,并解决在实际应用中遇到的问题。
Keras在图像分类任务中具有用户友好、模块化设计、丰富的预训练模型、强大的GPU支持、可视化与调试、可扩展性以及社区支持和资源等优势。这些优势使得Keras成为图像分类任务中广泛使用的深度学习框架之一。
1.2.2 图像分类识别流程
一、数据准备
- 收集足够数量的标记图像数据集,这些图像应包含多个类别,并且每个图像都应有明确的标签表示其所属类别。
- 将数据集划分为训练集、验证集和测试集。训练集用于模型训练,验证集用于调整超参数和防止过拟合,测试集用于评估模型的性能。
- 对图像进行预处理,包括调整图像大小以适应模型的输入要求、标准化或归一化像素值等,以便模型能够更好地学习图像特征。
二、构建模型
- 使用Keras构建一个卷积神经网络(CNN)模型。CNN是图像分类任务中常用的模型结构,它能够通过卷积层提取图像中的局部特征,并通过池化层降低特征图的维度,从而减少计算量。
- 可以选择使用Keras提供的预训练模型,如VGG16、ResNet等。这些模型在大型数据集(如ImageNet)上进行了预训练,具有良好的特征提取能力,可以作为特征提取器或进行微调以适应新的任务。
三、编译模型
- 设置损失函数,对于多分类问题,通常使用交叉熵损失函数。
- 选择优化器,如SGD、Adam、RMSprop等,用于更新模型的权重以最小化损失函数。
- 设置评估指标,如准确率、精确度、召回率、F1分数等,用于在训练过程中监控模型的性能。
四、训练模型
- 使用训练集数据对模型进行训练。在训练过程中,模型会不断学习从输入图像中提取特征并输出类别预测的能力。
- 监控训练过程中的损失值和评估指标,了解模型的训练效果。
- 使用验证集数据对模型进行验证,并根据验证结果调整超参数或优化模型结构。
五、评估模型
- 使用测试集数据对训练好的模型进行评估,计算模型的准确率、精确度、召回率等指标,以评估模型的泛化能力。
- 分析评估结果,了解模型在不同类别上的表现,并根据需要进行改进。
六、预测新图像
- 对新的图像进行预处理,使其与训练数据具有相同的格式和预处理方式。
- 使用训练好的模型对新的图像进行分类预测,并输出预测的类别标签或概率分布。
Keras在图像分类识别领域具有广泛的应用,其简单易用的API和强大的功能使得研究人员和开发者们能够快速地构建和训练模型。通过合理地设计模型结构、设置超参数和进行数据预处理,可以获得更好的图像分类效果。
2、数据准备
本文的例子展示了如何从磁盘上的JPEG图像文件开始进行图像分类,而不利用预训练的权重或预先构建的Keras应用程序模型。我们将在Kaggle的猫和狗二元分类数据集上演示这个工作流程。
我们使用image_dataset_from_directory
实用工具来生成数据集,并使用Keras的图像预处理层来进行图像标准化和数据增强。
2.1 设置
import os
import numpy as np
import keras
from keras import layers
from tensorflow import data as tf_data
import matplotlib.pyplot as plt
2.2 下载数据
猫和狗的图片请从以下网址进行下载并进行解压缩
https://download.microsoft.com/download/3/E/1/3E1C3F21-ECDB-4869-8368-6DEBA77B919F/kagglecatsanddogs_5340.zip
解压缩后,在PetImages文件夹下面分别有dog和cat两个子文件夹。
2.3过滤损坏的图像
在处理大量真实世界的图像数据时,损坏的图像是常见的。让我们过滤掉那些在文件头中没有包含字符串 “JFIF” 的错误编码图像。
num_skipped = 0
for folder_name in ("Cat", "Dog"):
folder_path = os.path.join("PetImages", folder_name)
for fname in os.listdir(folder_path):
fpath = os.path.join(folder_path, fname)
try:
fobj = open(fpath, "rb")
is_jfif = b"JFIF" in fobj.peek(10)
finally:
fobj.close()
if not is_jfif:
num_skipped += 1
# Delete corrupted image
os.remove(fpath)
print(f"Deleted {num_skipped} images.")
2.4 生成数据集
image_size = (180, 180)
batch_size = 128
train_ds, val_ds = keras.utils.image_dataset_from_directory(
"PetImages",
validation_split=0.2,
subset="both",
seed=1337,
image_size=image_size,
batch_size=batch_size,
)
2.5 可视化数据
以下是训练数据集中的前9张图像。
plt.figure(figsize=(10, 10))
for images, labels in train_ds.take(1):
for i in range(9):
ax = plt.subplot(3, 3, i + 1)
plt.imshow(np.array(images[i]).astype("uint8"))
plt.title(int(labels[i]))
plt.axis("off")
2.6 使用图像数据增强
当你没有大量的图像数据集时,通过应用随机但真实的变换(如随机水平翻转或小范围随机旋转)来人为地引入样本多样性是一个很好的做法。这有助于让模型接触到训练数据的不同方面,同时减缓过拟合。
data_augmentation_layers = [
layers.RandomFlip("horizontal"),
layers.RandomRotation(0.1),
]
def data_augmentation(images):
for layer in data_augmentation_layers:
images = layer(images)
return images
让我们通过反复对数据集中的前几幅图像应用数据增强来可视化增强后的样本是什么样的:
plt.figure(figsize=(10, 10))
for images, _ in train_ds.take(1):
for i in range(9):
augmented_images = data_augmentation(images)
ax = plt.subplot(3, 3, i + 1)
plt.imshow(np.array(augmented_images[0]).astype("uint8"))
plt.axis("off")
2.7数据标准化
我们的图像已经是标准大小(180x180),因为它们由数据集作为连续的float32批次提供。但是,它们的RGB通道值在[0, 255]范围内。这对于神经网络来说并不是理想的;通常,你应该使你的输入值变小。在这里,我们将在模型的开始处使用一个Rescaling层来将值标准化到[0, 1]范围内。
3、数据预处理
预处理数据有两种选择,程序员可以使用data_augmentation
预处理器有两种方式之一
3.1 使数据预处理成为模型的一部分
inputs = keras.Input(shape=input_shape)
x = data_augmentation(inputs)
x = layers.Rescaling(1./255)(x)
... # Rest of the model
使用这种选项,程序员的数据增强将在设备上发生,与模型的其余部分同步执行,这意味着它将受益于GPU加速。
请注意,数据增强在测试时是不活动的,因此输入样本仅在fit()
期间进行增强,而不是在调用evaluate()
或predict()
时。
如果程序员是在GPU上训练,这可能是一个好的选择。
3.2 将数据增强应用于数据集
augmented_train_ds = train_ds.map(
lambda x, y: (data_augmentation(x, training=True), y))
使用这种选项,程序员的数据增强将在CPU上异步发生,并在进入模型之前进行缓冲。
如果是在CPU上训练,这是更好的选择,因为它使数据增强异步进行且不会阻塞。
在我们的例子中,我们将选择第二种选项。如果程序员不确定该选择哪种,第二种选项(异步预处理)总是一个稳妥的选择。
3.3 为性能配置数据集
让我们对训练数据集应用数据增强,并确保使用缓冲预取,以便我们可以从磁盘上读取数据而不会使I/O成为阻塞操作:
# Apply `data_augmentation` to the training images.
train_ds = train_ds.map(
lambda img, label: (data_augmentation(img), label),
num_parallel_calls=tf_data.AUTOTUNE,
)
# Prefetching samples in GPU memory helps maximize GPU utilization.
train_ds = train_ds.prefetch(tf_data.AUTOTUNE)
val_ds = val_ds.prefetch(tf_data.AUTOTUNE)
4、构建模型
我们将构建一个Xception网络的小版本。我们并没有特别地尝试优化架构;如果你想系统地搜索最佳模型配置,可以考虑使用KerasTuner。
请注意:
- 我们以数据增强预处理器开始模型,接着是一个Rescaling层。
- 我们在最终的分类层之前包括一个Dropout层。
def make_model(input_shape, num_classes):
inputs = keras.Input(shape=input_shape)
# Entry block
x = layers.Rescaling(1.0 / 255)(inputs)
x = layers.Conv2D(128, 3, strides=2, padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.Activation("relu")(x)
previous_block_activation = x # Set aside residual
for size in [256, 512, 728]:
x = layers.Activation("relu")(x)
x = layers.SeparableConv2D(size, 3, padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.Activation("relu")(x)
x = layers.SeparableConv2D(size, 3, padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.MaxPooling2D(3, strides=2, padding="same")(x)
# Project residual
residual = layers.Conv2D(size, 1, strides=2, padding="same")(
previous_block_activation
)
x = layers.add([x, residual]) # Add back residual
previous_block_activation = x # Set aside next residual
x = layers.SeparableConv2D(1024, 3, padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.Activation("relu")(x)
x = layers.GlobalAveragePooling2D()(x)
if num_classes == 2:
units = 1
else:
units = num_classes
x = layers.Dropout(0.25)(x)
# We specify activation=None so as to return logits
outputs = layers.Dense(units, activation=None)(x)
return keras.Model(inputs, outputs)
model = make_model(input_shape=image_size + (3,), num_classes=2)
keras.utils.plot_model(model, show_shapes=True)
5、训练模型
epochs = 25
callbacks = [
keras.callbacks.ModelCheckpoint("save_at_{epoch}.keras"),
]
model.compile(
optimizer=keras.optimizers.Adam(3e-4),
loss=keras.losses.BinaryCrossentropy(from_logits=True),
metrics=[keras.metrics.BinaryAccuracy(name="acc")],
)
model.fit(
train_ds,
epochs=epochs,
callbacks=callbacks,
validation_data=val_ds,
)
Epoch 1/25
...
Epoch 25/25
147/147 ━━━━━━━━━━━━━━━━━━━━ 53s 354ms/step - acc: 0.9638 - loss: 0.0903 - val_acc: 0.9382 - val_loss: 0.1542
<keras.src.callbacks.history.History at 0x7f41003c24a0>
经过在完整数据集上训练25个周期后,我们得到了超过90%的验证准确率(实际上,在验证性能开始下降之前,你可以训练50个或更多的周期)。
这是一个很好的结果,表明你的模型已经能够很好地学习到数据的特征,并在未见过的数据上表现出色。然而,需要注意的是,随着训练周期的增加,模型可能会开始过拟合训练数据,导致在验证集上的性能下降。因此,在训练过程中监控验证集的性能是非常重要的。
如果你想要进一步提高模型的性能,你可以尝试以下一些方法:
- 数据增强:通过增加数据增强的复杂性和多样性,你可以帮助模型更好地泛化到未见过的数据。
- 模型微调:解冻预训练模型的一部分或全部层,并允许它们在训练过程中进行微调。这可以帮助模型学习到与你的特定任务更相关的特征。
- 正则化:除了Dropout层之外,你还可以尝试使用其他正则化技术,如L1或L2权重正则化,来防止过拟合。
- 使用更复杂的模型:虽然Xception是一个强大的模型,但根据你的数据和任务,可能还有其他更适合的模型架构。
- 优化器和学习率调度:尝试使用不同的优化器(如RMSprop、Adadelta等)和学习率调度策略(如指数衰减、余弦衰减等),以找到最适合你的训练任务的配置。
- 早期停止:在验证性能开始下降时停止训练,以防止过拟合。你可以使用Keras的
EarlyStopping
回调来实现这一点。
记住,每个数据集和任务都是独特的,所以可能需要一些实验来找到最佳的模型配置和训练策略。
6、在新数据上运行推理
请注意,在推理时间,数据增强和dropout层都是不活动的。
在训练完模型后,当你想要对新的、未见过的数据进行预测时,你需要确保数据预处理步骤与训练时一致(例如,图像大小、归一化等),但不需要应用数据增强(如旋转、缩放等)。同样,dropout层在推理时也不会被激活,因为它主要用于在训练期间防止过拟合。
img = keras.utils.load_img("PetImages/Cat/6779.jpg", target_size=image_size)
plt.imshow(img)
img_array = keras.utils.img_to_array(img)
img_array = keras.ops.expand_dims(img_array, 0) # Create batch axis
predictions = model.predict(img_array)
score = float(keras.ops.sigmoid(predictions[0][0]))
print(f"This image is {100 * (1 - score):.2f}% cat and {100 * score:.2f}% dog.")
1/1 ━━━━━━━━━━━━━━━━━━━━ 2s 2s/step
This image is 94.30% cat and 5.70% dog.
7、总结
今天我们对图像分类任务进行了深入的讨论,主要围绕构建和训练一个基于Xception网络架构的模型,以及在新数据上进行推理的过程。以下是本次讨论的总结:
7.1 模型构建与训练
-
选择网络架构:我们选择了Xception作为基础模型,因为它在图像分类任务中表现出色,并且具有高效的计算性能。
-
数据预处理:在训练之前,对图像数据进行预处理是非常重要的步骤。这通常包括图像大小调整、归一化以及可能的数据增强(如旋转、缩放等)。数据增强可以增加模型的泛化能力,防止过拟合。
-
模型微调:我们讨论了使用预训练的Xception模型,并可能通过微调其部分或全部层来适应特定的图像分类任务。微调可以帮助模型学习到与任务更相关的特征。
-
训练过程:在训练过程中,我们监控了验证集的性能,以确保模型没有过度拟合训练数据。通过调整学习率、使用不同的优化器以及应用正则化技术(如Dropout),我们尝试优化模型的性能。
-
训练周期:经过25个周期的训练,我们达到了超过90%的验证准确率。这表明模型已经能够很好地学习到数据的特征,并在未见过的数据上表现出色。然而,我们也提到了随着训练周期的增加,模型可能会开始过拟合训练数据。
7.2推理过程
-
数据预处理:在推理时,我们确保对新数据进行与训练时相同的预处理步骤,包括图像大小调整、归一化等。然而,我们不再应用数据增强。
-
运行推理:使用训练好的模型对新数据进行预测。对于图像分类任务,模型将输出每个类别的概率值。
-
解释预测结果:根据模型的输出,我们可以解释预测结果。对于二元分类任务,可以使用简单的阈值判断来确定预测类别。对于多类分类任务,可以选择概率最高的类别作为预测结果。
7.3总结与展望
本次讨论强调了构建和训练图像分类模型的关键步骤,以及在新数据上进行推理的过程。我们提到了选择合适的网络架构、进行适当的数据预处理、微调模型以及监控验证集性能的重要性。此外,我们还讨论了如何解释预测结果并可能进一步优化模型性能的方法。
展望未来,我们可以进一步探索不同的网络架构和训练策略,以提高图像分类模型的性能。同时,随着深度学习技术的不断发展,我们可以期待在图像分类领域取得更多的突破和进展。