使用 Keras 和 sklearn GridSearchCV 交叉验证提前停止

Posted

技术标签:

【中文标题】使用 Keras 和 sklearn GridSearchCV 交叉验证提前停止【英文标题】:Early stopping with Keras and sklearn GridSearchCV cross-validation 【发布时间】:2021-05-08 09:08:04 【问题描述】:

我希望通过 Keras 和 sklean 的 GridSearchCV 实现提前停止。

下面的工作代码示例是从How to Grid Search Hyperparameters for Deep Learning Models in Python With Keras 修改的。数据集可能是downloaded from here。

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

请注意,我们可以将val_acc 替换为val_loss

问题:如何使用GridSearchCV k-fold 算法生成的交叉验证数据集,而不是浪费 10% 的训练数据用于提前停止验证集?

# Use scikit-learn to grid search the learning rate and momentum
import numpy
from sklearn.model_selection import GridSearchCV
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier
from keras.optimizers import SGD

# Function to create model, required for KerasClassifier
def create_model(learn_rate=0.01, momentum=0):
    # create model
    model = Sequential()
    model.add(Dense(12, input_dim=8, activation='relu'))
    model.add(Dense(1, activation='sigmoid'))
    # Compile model
    optimizer = SGD(lr=learn_rate, momentum=momentum)
    model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy'])
    return model

# Early stopping
from keras.callbacks import EarlyStopping
stopper = EarlyStopping(monitor='val_acc', patience=3, verbose=1)

# fix random seed for reproducibility
seed = 7
numpy.random.seed(seed)
# load dataset
dataset = numpy.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# split into input (X) and output (Y) variables
X = dataset[:,0:8]
Y = dataset[:,8]
# create model
model = KerasClassifier(
    build_fn=create_model,
    epochs=100, batch_size=10,
    validation_split=0.1, # FIXME: Instead use GridSearchCV k-fold validation data.
    verbose=2)
# define the grid search parameters
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)

# Fitting parameters
fit_params = dict(callbacks=[stopper])
# Grid search.
grid_result = grid.fit(X, Y, **fit_params)

# summarize results
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))

【问题讨论】:

github.com/cerlymarco/keras-hypetune 【参考方案1】:

这里是如何只用一个拆分来做到这一点。

fit_params['cl__validation_data'] = (X_val, y_val)
X_final = np.concatenate((X_train, X_val))
y_final = np.concatenate((y_train, y_val))
splits = [(range(len(X_train)), range(len(X_train), len(X_final)))]

GridSearchCV(estimator=model, param_grid=param_grid, cv=splits)I

如果您想要更多拆分,您可以使用具有固定比率的'cl__validation_split' 并构造满足该条件的拆分。

可能太偏执了,但我不使用提前停止数据集作为验证数据集,因为它是间接用于创建模型的。

我还认为,如果您在最终模型中使用提前停止,那么在进行超参数搜索时也应该这样做。

【讨论】:

【参考方案2】:

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

在急于解决实施问题之前,花一些时间思考方法和任务本身总是一个好习惯;可以说,将提前停止与交叉验证过程混合在一起不是一个好主意。

让我们举一个例子来突出这个论点。

假设您确实使用了 100 个 epoch 的提前停止和 5 倍交叉验证 (CV) 来选择超参数。还假设您最终得到了一个超参数集 X,它提供了最佳性能,例如 89.3% 的二元分类准确度。

现在假设您的第二好的超参数集 Y 提供了 89.2% 的准确度。仔细检查各个 CV 折叠,您会发现,对于您的最佳情况 X,5 个 CV 折叠中的 3 个用尽了最大 100 个时期,而在另外 2 个早期停止中,分别在 95 和 93 个时期中开始。

现在想象一下,检查你的第二好的集合 Y,你会再次看到 5 个 CV 折叠中的 3 个用尽了 100 个 epoch,而另外 2 个都在大约 80 个 epoch 处足够早地停止了。

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

可以说,您会发现自己处于不确定的境地;进一步的实验可能会揭示哪个实际上是最好的超参数集,当然前提是您首先会考虑查看结果的这些细节。不用说,如果所有这些都是通过回调自动完成的,那么您可能会错过最佳模型,尽管您实际上会尝试过


整个 CV 理念隐含地基于“所有其他人都相等”的论点(这在实践中当然不是真的,只能以最好的方式近似)。如果您认为 epoch 的数量应该是一个超参数,只需将其明确包含在您的 CV 中,而不是通过提前停止的后门插入,从而可能会影响整个过程(更不用说提前停止 自身有一个超参数patience)。

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


深度神经网络领域仍然(非常)年轻,确实还没有建立其“最佳实践”指南;再加上一个事实,多亏了一个了不起的社区,在开源实现中有各种可用的工具,你很容易发现自己陷入(诚然诱人的)混杂的位置,只是因为它们碰巧可用。我不一定说这就是你在这里尝试做的事情 - 我只是敦促在结合可能无法一起工作的想法时更加谨慎......

【讨论】:

晚上好@desertnaut。感谢您放弃周日晚上的一部分时间来回答我的问题。是的,我完全理解你的论点,确实是清晰而发人深省的建议。你让我免于在花园小路上奔跑,被太多工具集所诱惑。谢谢,贾斯汀。 我不同意desertnaut(但缺乏评论的声誉)。通过提前停止,对于一组 epoch 计数,您确实无法分辨出它们中的哪一个对找到的最佳超参数集有贡献。但这不是一开始的问题。该方法确实问的是“给定最大 n个时期并使用早期停止,最好的超参数是什么?”。是的,提前停止会引入更多超参数,您可能希望或可能不希望通过网格搜索进行优化,但对于模型中的任何超参数都是如此。事实上,我认为在网格搜索期间提前停止【参考方案3】:

[旧答案,在问题被编辑和澄清之前 - 请参阅上面更新和接受的答案]

我不确定我是否理解了您的确切问题(您的问题很不清楚,并且您包含许多不相关的细节,这在提出 SO 问题时绝不是好事 - 请参阅 here)。

您不必必须(实际上不应该)在您的 model = KerasClassifier() 函数调用中包含任何关于验证数据的参数(有趣的是,为什么您不觉得同样需要 training 数据也在这里)。您的grid.fit() 将负责训练验证折叠。因此,如果您希望保留示例中包含的超参数值,则此函数调用应该很简单

model = KerasClassifier(build_fn=create_model, 
                        epochs=100, batch_size=32,
                        shuffle=True,
                        verbose=1)

您可以看到一些关于使用 GridSearchCV 和 Keras here 的清晰且解释清楚的示例。

【讨论】:

感谢@desertnaut 让我的问题更清楚。我使用您指出的示例代码完全重新解决了这个问题。

以上是关于使用 Keras 和 sklearn GridSearchCV 交叉验证提前停止的主要内容,如果未能解决你的问题,请参考以下文章

F1 比在 keras 回调中使用 sklearn 的准确率更高。有问题?

在使用 sklearn 和 keras 构建 CNN 时需要帮助理解形状错误吗?

TensorFlow 低级模型(没有 Keras 和 Sklearn) - 每一步都获得损失 = 0 和准确度 = 100%

无法使用 Sklearn 和 Keras Wrappers 使 pipeline.fit() 工作

基于sklearn和keras的数据切分与交叉验证

Keras 神经网络和 SKlearn SVM.SVC