You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Xception自定义模型的5折交叉验证各数据拆分MAE获取及结果验证

问题分析与解答

首先直接给你答案:当前输出并不是5个数据拆分对应的平均绝对误差(MAE),而是每个拆分测试子集里单个样本的绝对误差组成的数组,而且你的交叉验证逻辑还存在几个关键问题,导致结果完全不符合预期。

核心问题梳理

1. 错误地仅用单个batch数据做交叉验证

你代码里的X_train, Y_train = next(train_gen)只获取了数据生成器的一个batch数据,而非整个训练集。KFold拆分的只是这一小批样本,完全失去了交叉验证“遍历全数据集拆分评估”的意义,得到的结果没有任何统计价值。

2. 混淆了MAE函数的行为

如果你的mean_absolute_error调用的是tf.keras.losses.mean_absolute_error,它会返回每个样本的绝对误差(形状为(样本数,)的张量);而如果是sklearn.metrics.mean_absolute_error,默认会返回整个测试集的平均误差(标量)。你当前的输出就是前者的结果,不是该拆分的平均MAE。

3. 交叉验证的验证集逻辑错误

循环里你用validation_data=(X_valid, y_valid),这是外部独立验证集,而非KFold拆分出的对应测试子集,违背了“每个拆分用自身测试集评估”的交叉验证原则。

正确实现5折交叉验证的步骤

针对Keras图像模型,正确做法是对整个数据集的索引拆分,为每个fold创建专属的训练/验证生成器,避免一次性加载全量数据(防止OOM)。

修正后的代码示例

import numpy as np
from sklearn.model_selection import KFold
from sklearn.metrics import mean_absolute_error
import tensorflow as tf
from tensorflow.keras.applications import Xception
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import GlobalMaxPooling2D, Flatten, Dense
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# 启用GPU内存增长(缓解OOM)
physical_devices = tf.config.list_physical_devices('GPU')
if physical_devices:
    tf.config.experimental.set_memory_growth(physical_devices[0], True)
print("Num GPUs Available: ", len(physical_devices))

# 全局参数
IMG_SIZE = (224, 224)  # 根据你的任务需求调整
boneage_div = 1.0  # 对应你数据预处理的zscore转换系数
batch_size = 32

# 准备训练集的完整索引(假设train_df是包含所有训练样本的DataFrame)
train_indices = np.arange(len(train_df))
n_splits = 5
kf = KFold(n_splits=n_splits, shuffle=True, random_state=42)

cv_mae_list = []

for fold, (train_idx, val_idx) in enumerate(kf.split(train_indices)):
    print(f"===== Fold {fold+1}/{n_splits} =====")
    
    # 为当前拆分创建独立的训练/验证DataFrame
    fold_train_df = train_df.iloc[train_idx].reset_index(drop=True)
    fold_val_df = train_df.iloc[val_idx].reset_index(drop=True)
    
    # 初始化数据生成器(可添加数据增强逻辑)
    core_idg = ImageDataGenerator(rescale=1./255)
    
    train_gen = core_idg.flow_from_dataframe(
        fold_train_df,
        path_col='path',
        y_col='boneage_zscore',
        target_size=IMG_SIZE,
        color_mode='rgb',
        batch_size=batch_size,
        shuffle=True,
        class_mode='raw'  # 回归任务需指定为raw
    )
    
    val_gen = core_idg.flow_from_dataframe(
        fold_val_df,
        path_col='path',
        y_col='boneage_zscore',
        target_size=IMG_SIZE,
        color_mode='rgb',
        batch_size=batch_size,
        shuffle=False,  # 验证集无需打乱,方便后续匹配标签
        class_mode='raw'
    )
    
    # 每个fold重新初始化模型(避免之前训练的权重干扰)
    base_model = Xception(input_shape=(*IMG_SIZE, 3), include_top=False, weights='imagenet')
    base_model.trainable = True  # 如需分层微调,可后续修改
    
    model = Sequential([
        base_model,
        GlobalMaxPooling2D(),
        Flatten(),
        Dense(16, activation='relu'),
        Dense(1, activation='linear')
    ])
    
    # 自定义月份MAE指标
    def mae_months(in_gt, in_pred):
        return tf.keras.metrics.mean_absolute_error(boneage_div * in_gt, boneage_div * in_pred)
    
    # 编译模型
    adam = Adam(learning_rate=0.0005)
    model.compile(loss='mse', optimizer=adam, metrics=[mae_months])
    
    # 训练模型
    history = model.fit(
        train_gen,
        validation_data=val_gen,
        epochs=10,  # 根据你的任务需求调整epochs
        verbose=1
    )
    
    # 在验证集上预测并计算MAE
    val_preds = model.predict(val_gen, verbose=1)
    val_true = fold_val_df['boneage_zscore'].values
    
    # 计算当前fold的平均MAE(转换为月份)
    fold_mae = mean_absolute_error(boneage_div * val_true, boneage_div * val_preds)
    cv_mae_list.append(fold_mae)
    print(f"Fold {fold+1} MAE (months): {fold_mae:.2f}")

# 输出5折交叉验证的最终结果
mean_cv_mae = np.mean(cv_mae_list)
std_cv_mae = np.std(cv_mae_list)
print(f"\n5-Fold Cross Validation Results:")
print(f"Mean MAE (months): {mean_cv_mae:.2f} ± {std_cv_mae:.2f}")

关键说明

  1. 按索引拆分数据集:通过拆分DataFrame索引为每个fold创建独立子集,确保交叉验证覆盖全量训练数据。
  2. 每个fold重新初始化模型:避免模型在之前fold的训练权重基础上继续训练,保证每个fold的结果独立可靠。
  3. 正确计算平均MAE:使用sklearn的mean_absolute_error计算整个验证集的平均误差,并转换为月份单位,得到每个fold的MAE值。
  4. 缓解OOM问题:用生成器分批加载数据,配合GPU内存增长设置,有效避免内存溢出。

内容的提问来源于stack exchange,提问作者Bathtub

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 19:02:31