使用Keras和sklearn的GridSearchCV进行交叉验证的早期停止

我想在Keras和sklearn的GridSearchCV中实现早期停止功能。

下面的工作代码示例是从如何使用Keras在Python中为深度学习模型进行网格搜索超参数修改而来。数据集可以从这里下载

修改部分增加了Keras的EarlyStopping回调类以防止过拟合。为了使其有效,需要monitor='val_acc'参数来监控验证准确率。为了使val_acc可用,KerasClassifier需要validation_split=0.1来生成验证准确率,否则EarlyStopping会引发RuntimeWarning: Early stopping requires val_acc available!。请注意FIXME:代码注释!

注意我们可以用val_loss替换val_acc

问题: 我如何使用GridSearchCV的k折算法生成的交叉验证数据集,而不是浪费10%的训练数据来创建一个早期停止验证集?

# 使用scikit-learn进行学习率和动量的网格搜索import numpyfrom sklearn.model_selection import GridSearchCVfrom keras.models import Sequentialfrom keras.layers import Densefrom keras.wrappers.scikit_learn import KerasClassifierfrom keras.optimizers import SGD# 用于创建模型的函数,这是KerasClassifier所需的def create_model(learn_rate=0.01, momentum=0):    # 创建模型    model = Sequential()    model.add(Dense(12, input_dim=8, activation='relu'))    model.add(Dense(1, activation='sigmoid'))    # 编译模型    optimizer = SGD(lr=learn_rate, momentum=momentum)    model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy'])    return model# 早期停止from keras.callbacks import EarlyStoppingstopper = EarlyStopping(monitor='val_acc', patience=3, verbose=1)# 为了重现性固定随机种子seed = 7numpy.random.seed(seed)# 加载数据集dataset = numpy.loadtxt("pima-indians-diabetes.csv", delimiter=",")# 将数据集分成输入(X)和输出(Y)变量X = dataset[:,0:8]Y = dataset[:,8]# 创建模型model = KerasClassifier(    build_fn=create_model,    epochs=100, batch_size=10,    validation_split=0.1, # FIXME: 改用GridSearchCV的k折验证数据。    verbose=2)# 定义网格搜索参数learn_rate = [0.01, 0.1]momentum = [0.2, 0.4]param_grid = dict(learn_rate=learn_rate, momentum=momentum)grid = GridSearchCV(estimator=model, param_grid=param_grid, verbose=2, n_jobs=1)# 拟合参数fit_params = dict(callbacks=[stopper])# 网格搜索.grid_result = grid.fit(X, Y, **fit_params)# 总结结果print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))means = grid_result.cv_results_['mean_test_score']stds = grid_result.cv_results_['std_test_score']params = grid_result.cv_results_['params']for mean, stdev, param in zip(means, stds, params):    print("%f (%f) with: %r" % (mean, stdev, param))

回答:

[问题编辑和澄清后的回答:]

在急于解决实现问题之前,花一些时间思考方法和任务本身总是好的做法;可以说,将早期停止与交叉验证程序混合使用不是一个好主意。

让我们举个例子来说明这个观点。

假设你确实使用了早期停止,设定100个epoch,并使用5折交叉验证(CV)来选择超参数。假设你最终找到了一个最佳性能的超参数集X,其二分类准确率为89.3%。

现在假设你的第二佳超参数集Y的准确率为89.2%。仔细检查各个CV折,你会发现,对于最佳情况X,有3个CV折用完了最大100个epoch,而在另外2个折中,早期停止在第95和93个epoch分别生效。

现在想象一下,检查你的第二佳集Y,你会发现同样有3个CV折用完了100个epoch,而另外2个折在约80个epoch时就提前停止了。

从这样的实验中你会得出什么结论呢?

可以说,你会发现自己处于一个不确定的情况;进一步的实验可能会揭示哪个实际上是最佳的超参数集,当然前提是你最初就考虑到了这些结果的细节。而且不用说,如果所有这些都是通过回调自动化的,你可能会错过最佳模型,尽管你实际上已经尝试过它。


整个CV的理念隐含地基于“其他条件相同”的论点(当然在实践中这永远不会是真的,只是在最好的情况下被近似)。如果你觉得epoch的数量应该是一个超参数,那就明确地将其包含在你的CV中,而不是通过早期停止的后门插入,这样可能会危害整个过程(更不用说早期停止本身就有一个超参数,patience)。

不混合使用这两个技术当然并不意味着你不能顺序使用它们:一旦你通过CV获得了最佳超参数,你总可以在整个训练集上拟合模型时使用早期停止(当然前提是你确实有一个单独的验证集)。


深度神经网络领域仍然(非常)年轻,确实还没有建立其“最佳实践”指南;再加上得益于一个惊人的社区,各种工具都可以在开源实现中找到,你很容易发现自己处于一个(承认诱人的)位置,只是因为它们恰好可用而混合使用它们。我并不是说你在这里尝试做的就是这样——我只是敦促在结合可能并未设计为一起工作的想法时要更加谨慎…

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注