基于CIFAR - 10数据集的模型训练与微调实验
1. 迁移学习与微调概述
在之前的实验中,尝试通过手头的数据集引入迁移学习的概念,但实验并不成功,因为所使用的数据集差异较大。不过,这一验证过程是有价值的。接下来,我们将介绍比迁移学习更进一步的技术——微调。
迁移学习是使用在一个数据集上训练好的模型的权重,应用到另一个(希望是非常相似的)数据集上,将输入映射到新的空间并在映射后的数据上训练模型。而微调则是在使用预训练模型权重的基础上,在使用新的、较小的数据集继续训练模型时,允许权重发生变化。
例如,我们可以使用在大型数据集(如ImageNet)上训练好的大模型的权重,对于不在ImageNet中的类别(如孔雀鱼、神仙鱼和灯鱼的照片)的小数据集,从预训练的大模型开始,使用小数据集进行微调,这样可以利用模型已经适应此类输入的优势,有望用小数据集得到一个不错的模型。
2. 实验目标与模型选择
本次实验使用CIFAR - 10数据集,目标是使用本章第一部分的深度架构训练一个模型,以区分狗和猫的图像。但我们的数据集较小,每个类别大约有500张图像。同时,我们还有一个更大的数据集,即CIFAR - 10中的所有车辆图像。
我们将使用这些数据训练以下模型:
1. 使用小型猫狗数据集的浅层架构模型。
2. 使用小型猫狗数据集的深层架构模型。
3. 在车辆数据上预训练并在小型猫狗数据集上微调的深层架构模型。对于最后一种情况,我们将使用不同的冻结权重组合训练几种变体。
3. 数据集构建
在进行微调之前,需要构建我们的数据集。我们将使用未增强的CIFAR - 10构建小型猫狗数据集,使用增强的CIFAR - 10构建车辆数据集。
3.1 小型猫狗数据集构建
以下是构建小型猫狗数据集的代码:
import numpy as np
x_train = np.load("cifar10_train_images.npy")[:,2:30,2:30,:]
y_train = np.load("cifar10_train_labels.npy")
x_test = np.load("cifar10_test_images.npy")[:,2:30,2:30,:]
y_test = np.load("cifar10_test_labels.npy")
xtrn = []; ytrn = []
xtst = []; ytst = []
for i in range(y_train.shape[0]):
if (y_train[i]==3):
xtrn.append(x_train[i])
ytrn.append(0)
if (y_train[i]==5):
xtrn.append(x_train[i])
ytrn.append(1)
for i in range(y_test.shape[0]):
if (y_test[i]==3):
xtst.append(x_test[i])
ytst.append(0)
if (y_test[i]==5):
xtst.append(x_test[i])
ytst.append(1)
np.save("cifar10_train_cat_dog_small_images.npy", np.array(xtrn)[:1000])
np.save("cifar10_train_cat_dog_small_labels.npy", np.array(ytrn)[:1000])
np.save("cifar10_test_cat_dog_small_images.npy", np.array(xtst)[:1000])
np.save("cifar10_test_cat_dog_small_labels.npy", np.array(ytst)[:1000])
我们加载完整的CIFAR - 10训练和测试数据,遍历每个样本。如果类别是3(猫)或5(狗),则将图像和标签添加到列表中,并重新编码类别标签,使0表示猫,1表示狗。添加完所有样本后,保留前1000个样本并保存到磁盘,作为小型猫狗训练和测试集。保留前1000个样本可以使数据集的类别接近50/50的划分。
加载CIFAR - 10图像后,使用
[:,2:30,2:30,:]
进行切片,这是因为增强版本的数据集包含图像的小位移,之前将图像大小从32×32缩小到了28×28,这里提取每个图像的中心28×28区域。第一维是训练或测试集中的图像数量,最后一维是通道数(RGB图像为3)。
3.2 车辆数据集构建
以下是构建车辆数据集的代码:
import numpy as np
x_train = np.load("cifar10_aug_train_images.npy")
y_train = np.load("cifar10_aug_train_labels.npy")
x_test = np.load("cifar10_aug_test_images.npy")
y_test = np.load("cifar10_test_labels.npy")
vehicles = [0,1,8,9]
xv_train = []; xv_test = []
yv_train = []; yv_test = []
for i in range(y_train.shape[0]):
if (y_train[i] in vehicles):
xv_train.append(x_train[i])
yv_train.append(vehicles.index(y_train[i]))
for i in range(y_test.shape[0]):
if (y_test[i] in vehicles):
xv_test.append(x_test[i])
yv_test.append(vehicles.index(y_test[i]))
np.save("cifar10_train_vehicles_images.npy", np.array(xv_train))
np.save("cifar10_train_vehicles_labels.npy", np.array(yv_train))
np.save("cifar10_test_vehicles_images.npy", np.array(xv_test))
np.save("cifar10_test_vehicles_labels.npy", np.array(yv_test))
我们使用增强版本的数据,遍历训练和测试集,将车辆类别(0、1、8、9)的样本添加到相应列表中,并重新编码类别标签。车辆训练集有200,000个样本,每个类别50,000个。
4. 模型训练
接下来,我们需要(1)在车辆数据集上训练深度模型;(2)使模型适应猫狗数据集并进行微调;(3)使用车辆模型的权重初始化深度模型进行训练。同时,为了对比,我们还将从头开始训练浅层和深层模型。
以下是车辆模型训练的相关代码:
import numpy as np
batch_size = 64
num_classes = 4
epochs = 12
img_rows, img_cols = 28,28
x_train = np.load("cifar10_train_vehicles_images.npy")
y_train = np.load("cifar10_train_vehicles_labels.npy")
x_test = np.load("cifar10_test_vehicles_images.npy")
y_test = np.load("cifar10_test_vehicles_labels.npy")
我们使用64的小批量大小,有四个类别(飞机、汽车、轮船、卡车),训练12个周期。训练完成后,将模型保存为
cifar10_cnn_vehicles_model.h5
,以便用于微调猫狗模型。在我们的CPU系统上训练这个模型需要几个小时,最终测试准确率为88.2%,对于我们的实验目的来说表现良好。
5. 模型微调准备
现在需要调整车辆模型以适应猫狗数据集并进行微调。具体来说,需要将期望四个类别的顶部softmax层替换为期望两个类别的层。同时,需要决定在训练过程中冻结哪些层的权重,哪些层的权重进行更新,这一步至关重要,不同的选择会影响微调结果。
在微调时,通常的做法是冻结较低层的权重,因为如果新数据与预训练数据相似,模型的较低层已经适应,不应改变它们,只允许高层层进行改变,因为它们需要学习新数据的表示。具体冻结哪些层取决于模型的大小和数据本身,需要进行实验。
如果使用随机梯度下降(SGD)进行微调,通常会将学习率降低一个因子(如10),因为模型已经接近误差函数的期望最小值,不需要大的步长来寻找。我们的实验将使用Adadelta,它会为我们调整学习率步长。
以下是微调车辆模型的代码:
import keras
from keras.models import load_model
from keras.layers import Dense
from keras import backend as K
import numpy as np
batch_size = 64
num_classes = 2
epochs = 36
img_rows, img_cols = 28,28
x_train = np.load("cifar10_train_cat_dog_small_images.npy")
y_train = np.load("cifar10_train_cat_dog_small_labels.npy")
x_test = np.load("cifar10_test_cat_dog_small_images.npy")
y_test = np.load("cifar10_test_cat_dog_small_labels.npy")
if K.image_data_format() == 'channels_first':
x_train = x_train.reshape(x_train.shape[0], 3, img_rows, img_cols)
x_test = x_test.reshape(x_test.shape[0], 3, img_rows, img_cols)
input_shape = (3, img_rows, img_cols)
else:
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 3)
x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 3)
input_shape = (img_rows, img_cols, 3)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
首先加载并预处理小型猫狗数据集,使用64的小批量大小,两个类别(0 = 猫,1 = 狗),训练36个周期。
以下是调整车辆模型以适应猫狗的代码:
model = load_model("cifar10_cnn_vehicles_model.h5")
model.layers.pop()
model.outputs = [model.layers[-1].output]
model.layers[-1].outbound_nodes = []
model.add(Dense(num_classes, name="softmax", activation='softmax'))
model.layers[0].trainable = False
model.layers[1].trainable = False
model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adadelta(),
metrics=['accuracy'])
加载模型后,使用Keras移除顶部层,修补模型使倒数第二层看起来像顶层,然后添加一个新的二分类softmax层。此示例设置为冻结前两个卷积层的权重,我们将测试前两个卷积层的所有可能组合。最后,编译更新后的模型并指定Adadelta优化器。
以下是训练和测试猫狗模型的代码:
score = model.evaluate(x_test[100:], y_test[100:], verbose=0)
print('Initial test loss:', score[0])
print('Initial test accuracy:', score[1])
history = model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
verbose=0,
validation_data=(x_test[:100], y_test[:100]))
score = model.evaluate(x_test[100:], y_test[100:], verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
model.save("cifar10_cnn_cat_dog_fine_tune_3_model.h5")
在调用
fit
之前,使用测试数据的后90%调用
evaluate
,这可以让我们了解使用车辆权重时猫狗模型的表现。然后调用
fit
并再次评估,最后保存模型和训练历史。
如果要冻结所有卷积层,可以使用以下代码:
for i in range(5):
model.layers[i].trainable = False
6. 模型测试结果
我们对每种可能的组合训练六次,以获取平均准确率的统计信息,考虑到初始化过程的随机性。未训练时,初始模型准确率在50% - 51%左右,因为这是一个二分类模型,未训练时相当于随机猜测。
以下是不同模型的测试集准确率:
| 模型 | Freeze Conv0 | Freeze Conv1 | 准确率 (%) |
| — | — | — | — |
| 浅层 | – | – | 64.375 ± 0.388 |
| 深层 | – | – | 61.142 ± 0.509 |
| 微调0 | False | False | 62.683 ± 3.689 |
| 微调1 | True | False | 69.142 ± 0.934 |
| 微调2 | False | True | 68.842 ± 0.715 |
| 微调3 | True | True | 70.050 ± 0.297 |
| 冻结所有 | – | – | 57.042 ± 0.518 |
从结果可以看出,使用小型猫狗数据集从头开始训练深层架构的效果不太好,准确率约为61%;浅层架构从头开始训练效果稍好,准确率约为64%。微调有帮助,但并非所有微调选项都同样有效,“微调0”和“冻结所有”甚至比从头开始训练的最佳结果还要差。
“微调3”模型表现最好,虽然这些模型之间的差异在统计上不显著,但冻结前两个卷积层可能是更好的选择。原因是这些最低层是在更大的车辆数据集上训练的,包含了如平移和旋转等标准增强,这些层学习到的内核是边缘和纹理检测器,由于猫狗数据集也来自CIFAR - 10,合理推测相同的内核在猫狗图像上也有用。
然而,当冻结深层架构的所有卷积层时,性能显著下降,这意味着高层卷积层不太适应猫狗的结构,因为高层层学习输入图像中的较大结构,而这些结构是区分猫狗的关键,如果不能修改这些层,它们就没有机会学习我们需要的特征。
这个微调示例展示了该技术在适用时的强大之处,但和机器学习中的大多数方法一样,对于其成功的原因和时机只有一些直觉。有时,在与目标数据集不太接近的数据集上训练的大模型进行微调,其性能可能并不比训练一个浅层模型更好。
综上所述,本次实验探索了卷积神经网络在CIFAR - 10数据集上的应用,介绍了迁移学习和微调这两种基本技术,并展示了如何在Keras中实现它们,这些技术在深度学习中非常重要,值得深入理解和掌握。
基于CIFAR - 10数据集的模型训练与微调实验
7. 结果分析与技术要点回顾
从上述实验结果来看,我们可以总结出一些关键的技术要点和经验教训。
首先,关于模型架构的选择。在小型猫狗数据集上,浅层架构从头开始训练的效果优于深层架构,这可能是因为深层架构在数据量较小时容易出现过拟合问题。而微调技术的引入,显著提升了模型的性能,尤其是“微调3”模型,冻结前两个卷积层的策略取得了相对较好的效果。
其次,冻结层的选择至关重要。较低层的卷积层通常学习到的是通用的特征,如边缘和纹理,对于相似数据集来说,这些特征已经在预训练阶段得到了很好的学习,因此冻结这些层可以避免过度调整,保持模型对基本特征的敏感性。而较高层的卷积层学习到的是更高级的、特定于数据集的特征,需要根据新数据进行调整。
再者,学习率的调整也是微调过程中的一个重要因素。虽然我们在实验中使用了Adadelta优化器,它可以自动调整学习率步长,但如果使用SGD进行微调,降低学习率可以使模型在接近最优解时更加稳定地收敛。
以下是整个实验流程的mermaid流程图:
graph LR
A[数据准备] --> B[构建猫狗数据集]
A --> C[构建车辆数据集]
B --> D[训练车辆模型]
C --> D
D --> E[调整车辆模型用于微调]
E --> F[微调猫狗模型]
F --> G[测试模型]
8. 技术应用与拓展
迁移学习和微调技术在实际应用中具有广泛的价值。例如,在医疗影像领域,由于获取大量标注数据往往非常困难,我们可以使用在大规模通用图像数据集(如ImageNet)上预训练的模型,然后在少量的医疗影像数据上进行微调,从而快速得到性能较好的模型。
在工业检测领域,对于不同类型的产品缺陷检测,我们可以先在一个通用的缺陷数据集上训练模型,然后针对特定产品的缺陷数据进行微调,这样可以节省大量的训练时间和计算资源。
另外,随着深度学习技术的不断发展,迁移学习和微调的应用场景也在不断拓展。例如,在自然语言处理领域,预训练的语言模型(如BERT)可以通过微调应用于各种下游任务,如文本分类、情感分析等。
9. 总结与展望
本次实验围绕CIFAR - 10数据集展开,全面探索了卷积神经网络的应用,包括不同架构的训练、迁移学习和微调技术的实现。通过实验,我们深入理解了这些技术的原理和应用方法,以及它们在不同场景下的性能表现。
迁移学习和微调技术为我们在数据有限的情况下训练高性能模型提供了有效的解决方案。然而,这些技术的应用并非一成不变,需要根据具体的数据集和任务需求进行调整和优化。例如,在选择预训练模型、冻结层的设置、学习率的调整等方面,都需要进行大量的实验和探索。
未来,随着数据集的不断丰富和模型架构的不断创新,迁移学习和微调技术有望在更多领域发挥重要作用。同时,我们也需要进一步研究这些技术的理论基础,提高其可解释性和稳定性,为深度学习的发展提供更坚实的支持。
以下是本次实验中涉及的关键技术和步骤的总结表格:
| 技术/步骤 | 描述 |
| — | — |
| 迁移学习 | 使用在一个数据集上训练好的模型的权重,应用到另一个数据集上 |
| 微调 | 在迁移学习的基础上,允许部分层的权重在新数据集上进行更新 |
| 数据集构建 | 从CIFAR - 10数据集中提取猫狗和车辆数据集 |
| 模型训练 | 在车辆数据集上训练深度模型,在猫狗数据集上训练浅层和深层模型 |
| 模型微调 | 调整车辆模型以适应猫狗数据集,选择冻结层并进行训练 |
| 模型测试 | 评估不同模型在猫狗数据集上的性能 |
总之,迁移学习和微调技术是深度学习中非常重要的工具,掌握这些技术可以帮助我们更高效地解决实际问题。在未来的研究和实践中,我们应该不断探索和优化这些技术,以适应不断变化的应用需求。
超级会员免费看

被折叠的 条评论
为什么被折叠?



