RandomizedSearchCV+KerasClassifier训练机制与用法解析
Keras 结合 RandomizedSearchCV 做K折交叉验证的问题解答
背景
你持有一份训练数据集,计划通过K折交叉验证训练神经网络,采用keras-sklearn接口封装模型,核心实现代码如下:
# 模型初始化 model = KerasClassifier(build_fn=get_model_, epochs=120, batch_size=32, verbose=1) # 超参数搜索空间 params = dict({'l2':[0.1,0.3,0.5,0.8], 'dropout_rate':[0.1,0.3,0.5,0.8], 'batch_size':[16,32,64,128], 'learning_rate':[0.001, 0.01, 0.05, 0.1]}) def trainer(model, X, y, folds, params, verbose=None): from keras.wrappers.scikit_learn import KerasClassifier from tensorflow.keras.optimizers import Adam from sklearn.model_selection import GridSearchCV, RandomizedSearchCV v = verbose if verbose else 0 clf = RandomizedSearchCV(model, param_distributions = params, n_jobs = 1, scoring="roc_auc", cv = folds, verbose = v) grid_result = clf.fit(X, y) # 结果输出 print('- '*40) print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_)) print('- '*40) # 启动训练 trainer(model, X_train, y_train, folds, params, verbose=1)
其中get_model_为返回已编译tf.keras模型的自定义函数。
核心运行逻辑说明
默认流程下的运行规则
你对流程的基础推测符合默认逻辑:每一组待测试的超参数组合,会执行K轮独立训练,每轮取K-1折数据作为训练集训练指定epochs轮数,训练完成后在剩余1折上计算测试得分。关于你提到的几个核心机制问题,明确如下:
- 该流程本身不直接防控模型训练过程的过拟合:K折交叉验证的核心作用是给出超参数组合的泛化性能无偏估计,避免在固定验证集上反复调参导致的超参数层面过拟合(即参数适配验证集特殊分布、换数据效果骤降的问题),训练过程的过拟合仍需通过L2正则、Dropout、早停等机制防控。
- 原生Keras每轮epoch结束后的验证集评估逻辑,默认在当前流程中不生效:调用
clf.fit(X,y)时未传入验证集拆分规则,KerasClassifier会将传入的全部数据作为训练集跑满设定的epochs,不会自动拆分验证折做epoch级评估,自然不会输出每轮的验证折得分。 - 回调函数支持正常传入,不存在配置冲突:初始化KerasClassifier时可直接传入
callbacks参数,RandomizedSearchCV不会修改回调逻辑,回调会在每折训练过程中正常生效。如果要监测val_prc指标,需要在每折训练时手动从训练折中拆分出验证子集传入fit方法——没有验证集数据的前提下,所有val_前缀的指标都无法计算。你可以通过自定义KerasClassifier子类重写fit方法,实现每折训练时自动拆分验证集的逻辑。
具体问题解答
1. 当前RandomizedSearchCV的使用是否正确?为什么始终提示Fitting 5 folds for each of 10 candidates, totalling 50 fits
你的写法整体符合接口调用规范,出现该提示是RandomizedSearchCV的默认机制导致的:
- 和GridSearchCV遍历所有参数组合的逻辑不同,RandomizedSearchCV默认的参数采样次数
n_iter=10,即无论参数空间总共有多少种组合,默认只会随机采样10组不同的参数组合做测试。 - 当你传入的
folds为5折拆分规则时,总拟合次数就是10组参数 * 5折 = 50次,和每个参数设置多少个可选值无关。如果需要增加测试的参数组合数量,初始化RandomizedSearchCV时手动设置n_iter为目标数值即可。
2. 数据不平衡、样本量不足场景下,模型得分异常偏低、loss偏高的原因
该问题通常由以下几类原因导致:
- 超参数搜索空间设置不合理:你设置的学习率最大值为0.1、L2正则系数最大值为0.8,这两个参数的大值选项极易导致训练异常:0.1的学习率对于绝大多数神经网络任务来说过大,很容易跨过损失最低点导致训练发散;0.8的L2惩罚强度过高,会严重限制模型参数更新,直接导致模型欠拟合。
- 样本不平衡适配缺失:你设置的评估指标为
roc_auc,在样本极度不平衡场景下,如果未设置类别权重、未做采样处理,模型会直接偏向预测多数类,导致AUC得分极低;如果编译模型时使用普通交叉熵损失,也会加剧模型对多数类的偏向。 - 无早停机制:当前配置下模型会固定跑满120个epochs,没有验证集触发早停逻辑,要么学习率过大导致训练发散loss飙升,要么正则强度过高导致模型无法学习有效特征,都会出现loss高、得分低的现象。
- 数据预处理缺失:神经网络对输入特征尺度敏感,如果特征未做标准化/归一化处理,会直接导致收敛速度慢、训练效果差。
3. 当前代码的完整执行流程
在不修改现有代码的前提下,程序会严格按照以下顺序执行:
- RandomizedSearchCV初始化完成后,从你定义的params参数空间中,随机采样10组不同的超参数组合(包含l2、dropout_rate、batch_size、learning_rate四个参数的随机取值)。
- 对每一组采样到的超参数组合:
- 按照传入的folds规则,将X_train、y_train拆分为K个互斥的数据折(默认5折)。
- 依次遍历每一折:将当前折作为验证折,剩余K-1折作为训练折;调用
get_model_函数,传入当前组超参数,生成一个全新的、权重随机初始化的编译完成的Keras模型。 - 用当前训练折数据,按照当前组参数指定的batch_size连续训练120个epochs,训练全程不做epoch级验证,仅输出训练集上的训练进度和指标。
- 训练完成后,在当前验证折上做预测,计算roc_auc得分并记录,之后丢弃当前训练好的模型,继续处理下一折。
- 计算K折的平均roc_auc得分,作为该组超参数的最终性能得分。
- 10组超参数全部完成K折验证后,选出平均roc_auc得分最高的一组作为最优参数,对应得分记录为最优得分。
- 搜索流程结束后,RandomizedSearchCV会自动用全部X_train、y_train数据,以最优参数重新训练一个模型,作为
clf对象的最终可用模型,该重训练过程同样会跑满120个epochs,无epoch级验证。 - 按照代码逻辑打印最优得分和最优参数。
内容的提问来源于stack exchange,提问作者David Harar
相关产品推荐
相关产品推荐

