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

神经网络训练学习曲线异常求助:5折交叉验证结果分析

调试提示与问题分析

我来帮你梳理下这个5折交叉验证里出现异常的可能原因和调试方向:

1. 交叉验证中模型未重新初始化(最核心问题)

你的cv函数里,每次折的训练都是复用同一个传入的model对象,没有在每个折开始时创建新的模型实例。这会导致:

  • 第一个折是从头训练,曲线表现正常;
  • 从第二个折开始,模型是在之前折训练好的权重基础上继续训练,而非针对当前折的数据集从头拟合;
  • 后续折的模型可能已经在前面的数据上收敛,出现loss极低、甚至val_loss低于train_loss的异常情况(比如验证集刚好是模型已经“熟悉”的数据分布)。

修复方法:
把模型初始化逻辑放到交叉验证循环里,确保每个折都用全新的模型:

def cv(X, y, model_func, input_dim, n_splits=5, epochs=5, batch_size=1024, random_state=42, verbose=0):
    kf = KFold(n_splits=n_splits, shuffle=False, random_state=random_state)
    histories = []
    for s in kf.split(X):
        # 每个折都创建新模型
        model = model_func(input_dim)
        X_train = X.iloc[s[0]].to_numpy()
        y_train = y.iloc[s[0]]['Target'].to_numpy()
        X_test = X.iloc[s[1]].to_numpy()
        y_test = y.iloc[s[1]]['Target'].to_numpy()
        h = model.fit(X_train, y_train, epochs=epochs, batch_size=batch_size, validation_data=(X_test, y_test), verbose=verbose)
        histories.append(h)
    return histories

调用时传入模型函数和输入维度:

histories = cv(X, y, model_8, input_dim=X.shape[1])

2. 损失函数与输出激活函数不匹配

你的模型输出用sigmoid(适合二分类任务),但编译时用的是mean_squared_error损失。虽然MSE并非完全不能用,但binary_crossentropy才是sigmoid输出的标准搭配,MSE可能导致训练不稳定、loss值异常偏低(比如Target是0/1时,MSE最小值为0,模型极易快速收敛到极低loss)。

修复方法:
修改模型编译的loss参数:

model.compile(loss='binary_crossentropy', optimizer='adam')

3. 未打乱数据的潜在风险

你设置了shuffle=False,数据会按原始顺序分割。如果你的数据是按Target值排序过的(比如前半全是0,后半全是1),后续折的训练/验证集分布会严重失衡,导致模型学习异常,出现val_loss低于train_loss的情况。

建议:
除非有特殊业务需求,交叉验证时应开启打乱:

kf = KFold(n_splits=n_splits, shuffle=True, random_state=random_state)

4. 绘图函数的索引可能存在错误

你的plots_函数里有一行hist = cv_model[1],但从cv函数的返回逻辑来看,models_cv_histories应该是每个模型的所有折的history列表,这里的索引[1]可能错误,导致你绘制的不是完整的折曲线,进而出现异常可视化结果。

修复方法:
修正绘图函数的遍历逻辑,确保正确取到所有折的history:

def plots_(models_cv_histories, n_splits, save=False):
    nb_models = len(models_cv_histories)
    fig, axes = plt.subplots(nrows=nb_models, ncols=n_splits, figsize=(12, 5))
    row_index = 0
    for cv_model in models_cv_histories:
        # 去掉错误的[1]索引,直接遍历当前模型的所有折history
        epochs = range(1, len(cv_model[0].epoch) + 1)
        col_index = 0
        for split_ in cv_model:
            loss = split_.history['loss']
            val_loss = split_.history['val_loss']
            model_name = split_.model.name
            if nb_models > 1:
                ax = axes[row_index][col_index]
            else:
                ax = axes[col_index]
            ax.set_title(model_name + ' split ' + str(col_index))
            ax.plot(epochs, loss, color="r", label="loss")
            ax.plot(epochs, val_loss, color="g", label="val_loss")
            ax.set_xlabel("epochs")
            ax.xaxis.set_major_locator(MaxNLocator(integer=True))
            ax.set_ylabel("loss")
            ax.legend(loc="upper right")
            col_index += 1
        row_index += 1
    fig.subplots_adjust()
    if save:
        plt.savefig("plots/test.png")
    fig.tight_layout()
    plt.show()

额外调试步骤

  • 检查Target列数据:确认是0/1二分类数据,无异常值,预处理是否正确;
  • 打印每个折的训练/验证集Target分布:用np.mean(y_train)和np.mean(y_test)查看正负样本比例,排查类别不平衡问题;
  • 尝试降低batch_size:1024的batch_size对小数据集来说可能过大,导致模型更新不充分;
  • 增加epochs:当前仅训练5个epoch,模型可能还未进入正常收敛阶段。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 08:17:42