我想在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获得了最佳超参数,你总可以在整个训练集上拟合模型时使用早期停止(当然前提是你确实有一个单独的验证集)。
深度神经网络领域仍然(非常)年轻,确实还没有建立其“最佳实践”指南;再加上得益于一个惊人的社区,各种工具都可以在开源实现中找到,你很容易发现自己处于一个(承认诱人的)位置,只是因为它们恰好可用而混合使用它们。我并不是说你在这里尝试做的就是这样——我只是敦促在结合可能并未设计为一起工作的想法时要更加谨慎…