深度学习图像分类模型的K折交叉验证

在构建深度学习模型时,希望模型既稳健又准确。为此,需要一种评估技术来检查模型是否正常工作。K折交叉验证是帮助评估模型的一种技术。本文将从理论部分开始,然后转向代码及其解释。编码部分将完全依赖于Python。虽然这里讨论的方法也可以应用于一些机器学习问题,但只以深度学习问题为例。如果希望了解如何将其应用于机器学习,请在评论区告诉。

目录

  1. 引言
  2. 为什么需要K折交叉验证
  3. 什么是K折交叉验证
  4. 如何用代码实现K折交叉验证
  5. 结论

让首先尝试理解在这里做什么。在创建深度学习模型时,希望模型既稳健又准确。为此,需要一些评估技术,即某种方式来检查模型是否正常工作。K折交叉验证是帮助评估模型的一种技术。可能已经多次看到K折交叉验证的使用,但在这里,不仅将使用这种技术来评估,还将用它来计算模型的结果。在使用这种技术之前,首先需要理解为什么它如此重要,以及为什么它比其他验证技术更好。

为什么需要K折交叉验证

通常,使用留出法,将数据分成两部分:训练和测试,并检查在训练数据上训练的模型是否在测试数据上表现良好,使用一些错误指标。但能依赖这种方法吗?答案是不可以。这种方式的评估高度依赖于训练集中也在测试集中的数据点,因此评估高度依赖于数据分割的方法。让看看K折交叉验证如何比传统的留出法更好。

什么是K折交叉验证

K折交叉验证是一种验证技术,将数据分成k个子集,重复k次留出法,每个k个子集用作测试集,其他k-1个子集用于训练。然后计算所有这些k次试验的平均误差,这比标准留出法更可靠。有了这种技术,不必担心数据是如何实际分割的。

如何用代码实现K折交叉验证

要将K折交叉验证与深度学习模型一起使用,需要满足一些先决条件,如下所示。

数据集:,包含6个类别,需要DL模型进行分类。主文件夹包含2个子文件夹,即train和test,以及一个CSV文件。子文件夹包含图像,CSV包含有关训练和测试数据的所有信息。更多信息可以在下载页面上找到。可以查看那里。

对于测试,将仅使用训练数据,即只会评估训练准确性。

库:pandas, Keras, sklearn。

不会从头开始编写K折交叉验证,因为sklearn已经提供了实现。将只使用那个。

from sklearn.model_selection import KFold kf = KFold(n_splits=5, shuffle=False, random_state=None)

让深入看看参数。所以n_splits意味着想要创建的数据子集的数量。shuffle:是否在将数据分割成批次之前对数据进行洗牌。

现在让看看将要使用的sklearn中定义的K折的修改。

from sklearn.model_selection import StratifiedKFold skf = StratifiedKFold(n_splits=5, shuffle=False, random_state=None)

分层K折:分层和普通K折之间的主要区别在于分割方式,即分层K折保证每个分割将有一定百分比的每个类别,这试图最小化每个类别对结果的影响。当拥有不平衡的数据集时,最好使用这种技术。

让创建一个自定义模型进行比较:

def get_model(IMG_SIZE): base_model = applications.ResNet50(weights='imagenet', include_top=False, input_shape=(IMG_SIZE, IMG_SIZE, 3)) add_model = Sequential() add_model.add(Flatten(input_shape=base_model.output_shape[1:])) add_model.add(Dropout(0.3)) add_model.add(Dense(64, activation='relu')) add_model.add(Dropout(0.4)) add_model.add(Dense(6, activation='softmax')) model = Model(inputs=base_model.input, outputs=add_model(base_model.output)) model.compile(loss='categorical_crossentropy', optimizer=optimizers.SGD(lr=1e-4, momentum=0.9), metrics=['accuracy']) return model

在上面的模型中,使用了修改后的Resnet-50,其实现已经在Keras中定义。上述模型没有优化,仅用于示例解释。

IMG_SIZE = 128 BATCH_SIZE = 16 EPOCHS = 1 N_SPLIT = 7

在上面的代码中,初始化了n_splits为7,以便在取平均值时至少有一个类别重复。有6个类别,所以如果采取小于7的分割,可能会遇到每次都有一个不同的预测的情况。因此,最后将无法找到哪个类别在大多数情况下被预测,模型将只给出随机结果。

主代码:

train_datagen = ImageDataGenerator(rescale = 1./255, shear_range = 0.2, zoom_range = 0.2, horizontal_flip = True) validation_datagen = ImageDataGenerator(rescale = 1./255) kfold = StratifiedKFold(n_splits=N_SPLIT, shuffle=True, random_state=42) j = 0 for train_idx, val_idx in list(kfold.split(train_x, train_y)): x_train_df = df.iloc[train_idx] x_valid_df = df.iloc[val_idx] j += 1 training_set = train_datagen.flow_from_dataframe(dataframe=x_train_df, directory=TRAIN_PATH, x_col="Image", y_col="Class", class_mode="categorical", target_size=(IMG_SIZE, IMG_SIZE), batch_size=BATCH_SIZE) validation_set = validation_datagen.flow_from_dataframe(dataframe=x_valid_df, directory=TRAIN_PATH, x_col="Image", y_col="Class", class_mode="categorical", target_size=(IMG_SIZE, IMG_SIZE), batch_size=BATCH_SIZE) model_test = get_model(IMG_SIZE) history = model_test.fit_generator(training_set, validation_data=validation_set, epochs=EPOCHS, steps_per_epoch=x_train_df.shape[0] // BATCH_SIZE) test_generator = ImageDataGenerator(rescale = 1./255) test_set = test_generator.flow_from_dataframe(dataframe=train, directory=TRAIN_PATH, x_col="Image", y_col=None, class_mode=None, target_size=(IMG_SIZE, IMG_SIZE)) pred = model_test.predict_generator(test_set, len(train) // BATCH_SIZE) predicted_class_indices = np.argmax(pred, axis=1) data_kfold[j] = predicted_class_indices gc.collect()

在上述代码中,只是重复了7次留出法,并将输出存储在data_kfold中。所以基本上data_kfold将如下所示,将拥有:

所以当看到不同训练数据时,得到不同的结果,就像在上面的例子中,对于6468的图像,可以看到相同的模型一次预测它是2,一次预测它是1。但大多数时候预测值是4。那么认为应该是什么答案呢?由于可以看到4比其他任何类别都多,所以将选择4。是的!这正是在以下代码中所做的。

labels = (training_set.class_indices) labels2 = dict((v, k) for k, v in labels.items()) import collections for i in range(len(data_kfold)): co = collections.Counter(data_kfold.loc[i]) co = sorted(co.items(), key=lambda x: x[1], reverse=True) ans.Class.loc[i] = labels2[co[0][0]]

在上述代码中,将最预测标签的最大出现次数存储到数据持有者“ans”中。labels2只不过是从整数到字符串的映射,因为原始数据包含以字符串形式的目标类别,所以需要它。

哇,完成了教程。但是结果呢?让比较平均值和标准留出法的训练准确性。

留出法的准确性:0.32168805070335443

K折方法的准确性:0.4274230947596228

这些是获得的结果。当对K折取平均值时,以及当应用留出法时。所以可以清楚地说,K折的性能要好得多。

代码可以从Kaggle和Github获得:

Kaggle:

Github:

通过上述结果,可以清楚地看到,K折平均的准确性比标准留出法要好得多,即使是一个epoch。可以训练更多的epoch,观察变化。不仅准确性高,还可以说最终模型将比普通模型更稳健。

当然,可能已经意识到,当有更多的数据和更多的类别时,这将需要更多的时间。所以这种方法只能应用于有有限的数据和有限的类别时。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485