基于ImageDataGenerator的CNN分类k折交叉验证实现正确性咨询
嘿,我看了你的代码,发现几个关键问题会导致你的k-fold交叉验证没正确跑起来,咱们一步步捋清楚:
核心问题分析
循环结构完全错位:你写的
for i in range(2)只创建了不同fold的生成器,但模型训练的model.fit_generator()在循环外面,这意味着你实际只训练了最后一次循环生成的train_generator和validation_generator(也就是fold1),前面的fold0完全没用到,这完全失去了k-fold交叉验证的意义。必须把整个训练流程(包括模型初始化/加载、训练、评估)都放进for循环里,每个fold独立执行一次完整的训练-验证流程。模型初始化逻辑有问题:当前代码尝试先加载一个预存的
saved.hdf5,但k-fold交叉验证中,每个fold的模型应该是独立初始化的(除非你是做模型微调且有特定需求)。如果第一个fold训练完覆盖了saved.hdf5,第二个fold加载的是第一个fold训练后的模型,这就不是独立的交叉验证了。正确的做法是,每个fold要么从头构建模型,要么加载同一个初始预训练模型(如果用迁移学习),而不是上一个fold训练后的模型。样本数变量未对应单个fold:你用的
nb_train_sample和nb_validation_sample看起来是全局的总样本数,但每个fold的训练/验证样本数应该是各自fold下的数量,否则steps_per_epoch和validation_steps会计算错误,导致训练时的步数不对。测试集预测未整合进交叉验证流程:你在循环里创建了
test_generator,但没有在每个fold训练完后对测试集进行预测并保存结果。k-fold交叉验证通常需要每个fold训练的模型都对测试集做预测,最后把所有fold的预测结果整合起来计算平均性能(比如平均准确率、混淆矩阵等)。
修正后的代码示例
我调整了你的代码结构,把核心流程放进循环,并且处理了每个fold的独立模型和结果保存:
import numpy as np # 定义每个fold的模型保存路径,避免互相覆盖 model_base_path = '../carPrediction/model/' # 用来保存每个fold的测试集预测结果 all_test_predictions = [] for i in range(2): print('===== Training fold', i, '=====') # 1. 创建当前fold的生成器 train_generator = train_datagen.flow_from_directory( TRAIN_CROPPED_PATH + f'fold{i}', target_size=(image_size, image_size), batch_size=batch_size, class_mode='categorical', seed=2019, color_mode='rgb' ) validation_generator = valid_datagen.flow_from_directory( VALID_CROPPED_PATH + f'fold{i}', target_size=(image_size,image_size), batch_size=batch_size, class_mode='categorical', seed=2019, color_mode='rgb' ) test_generator = test_datagen.flow_from_dataframe( dataframe=df_test, directory=TEST_CROPPED_PATH, x_col='img_file', y_col=None, target_size=(image_size,image_size), color_mode='rgb', class_mode=None, batch_size=batch_size, shuffle=False ) # 2. 初始化当前fold的模型:要么从头构建,要么加载初始预训练模型 # 这里假设你有一个build_model()函数用来创建你的CNN结构 # 如果是迁移学习,就加载预训练模型并修改顶层 model = build_model() # 替换成你实际的模型构建代码 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # 3. 设置当前fold的回调(注意模型路径要区分fold) patient = 2 fold_model_path = model_base_path + f'saved_fold{i}.hdf5' callbacks1 = [ EarlyStopping(monitor='val_loss', patience=patient, mode='min', verbose=1), ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=patient//2, min_lr=1e-5, verbose=1, mode='min'), ModelCheckpoint(filepath=fold_model_path, monitor='val_loss', verbose=1, save_best_only=True, mode='min'), ] # 4. 获取当前fold的样本数(从generator里取更准确) nb_train_sample = train_generator.samples nb_validation_sample = validation_generator.samples # 5. 训练当前fold的模型 history = model.fit_generator( train_generator, steps_per_epoch=nb_train_sample // batch_size, epochs=2, validation_data=validation_generator, validation_steps=nb_validation_sample // batch_size, verbose=1, callbacks=callbacks1 ) # 6. 用当前fold的最佳模型预测测试集 best_model = load_model(fold_model_path) test_predictions = best_model.predict(test_generator, verbose=1) all_test_predictions.append(test_predictions) # 7. 整合所有fold的预测结果,计算平均性能 mean_predictions = np.mean(all_test_predictions, axis=0) # 这里可以根据mean_predictions计算准确率、混淆矩阵等
额外注意点
- 如果你的k-fold是严格的分层抽样(保证每个fold里carA和carB的比例和整体一致),那你的文件夹结构是对的;如果不是,建议检查每个fold的类别分布是否均衡。
- 如果你想用同一个初始模型(比如预训练的ResNet)做微调,那每个fold都要加载同一个初始权重,而不是上一个fold训练后的权重。
fit_generator()在新版本Keras里已经被fit()替代了,如果你的TensorFlow/Keras版本较新,建议改用model.fit(train_generator, ...),用法基本一致。
内容的提问来源于stack exchange,提问作者user11240811




