Keras 中的交叉验证

Cross Validation in Keras

我正在 Keras 中实现多层感知器并使用 scikit-learn 执行交叉验证。为此,我受到问题 Cross Validation in Keras

中找到的代码的启发
from sklearn.cross_validation import StratifiedKFold

def load_data():
    # load your data using this function

def create model():
    # create your model using this function

def train_and_evaluate__model(model, data[train], labels[train], data[test], labels[test)):
    # fit and evaluate here.

if __name__ == "__main__":
    X, Y = load_model()
    kFold = StratifiedKFold(n_splits=10)
    for train, test in kFold.split(X, Y):
        model = None
        model = create_model()
        train_evaluate(model, X[train], Y[train], X[test], Y[test])

在我对神经网络的研究中,我了解到神经网络的知识表示在突触权重中,在网络跟踪过程中,更新权重从而降低网络错误率并提高其性能. (就我而言,我使用的是监督学习)

为了更好地训练和评估神经网络性能,一种常用的方法是交叉验证,即对数据集进行 returns 分区以训练和评估模型。

我的疑问是...

在此代码段中:

for train, test in kFold.split(X, Y):
    model = None
    model = create_model()
    train_evaluate(model, X[train], Y[train], X[test], Y[test])

我们为每个生成的分区定义、训练和评估新的神经网络?

如果我的目标是针对整个数据集微调网络,为什么定义单个神经网络并使用生成的分区对其进行训练是不正确的?

也就是为什么这段代码是这样的?

for train, test in kFold.split(X, Y):
    model = None
    model = create_model()
    train_evaluate(model, X[train], Y[train], X[test], Y[test])

不是这样吗?

model = None
model = create_model()
for train, test in kFold.split(X, Y):
    train_evaluate(model, X[train], Y[train], X[test], Y[test])

我对代码工作原理的理解有误吗?还是我的理论?

注释掉的函数使这一点不那么明显,但我们的想法是在迭代折叠时跟踪模型性能,并在最后提供那些较低级别的性能指标或平均全局性能。例如:

理想情况下,train_evaluate 函数会为每个拆分输出一些准确度分数,可以在最后合并。

def train_evaluate(model, x_train, y_train, x_test, y_test):
    model.fit(x_train, y_train)
    return model.score(x_test, y_test)

X, Y = load_model()
kFold = StratifiedKFold(n_splits=10)
scores = np.zeros(10)
idx = 0
for train, test in kFold.split(X, Y):
    model = create_model()
    scores[idx] = train_evaluate(model, X[train], Y[train], X[test], Y[test])
    idx += 1
print(scores)
print(scores.mean())

所以是的,你确实想为每一折创建一个新模型,因为本练习的目的是确定你的模型设计如何在所有数据段上执行,而不仅仅是一个特定的段可能或可能无法让模型表现良好。

这种方法在与超参数网格搜索一起应用时变得特别强大。在这种方法中,您使用交叉验证拆分训练具有不同超参数的模型,并跟踪拆分和整体的性能。最后,您将能够更好地了解哪些超参数可以使模型表现最佳。有关更深入的解释,请参阅 sklearn Model Selection 并特别注意交叉验证和网格搜索部分。

测试模型性能的主要思路是执行以下步骤:

  1. 在训练集上训练模型。
  2. 在训练过程中未使用的数据上评估您的模型,以模拟新的数据到达。

所以基本上 - 您最终应该测试模型的数据应该模仿您将从 client/application 获得的第一个数据部分,以应用您的模型。

这就是交叉验证如此强大的原因 - 它使整个数据集中的每个数据点都可以用作新数据的模拟。

现在 - 回答你的问题 - 每个交叉验证都应遵循以下模式:

for train, test in kFold.split(X, Y
     model = training_procedure(train, ...)
     score = evaluation_procedure(model, test, ...)

因为毕竟,您将首先训练您的模型,然后将其用于新数据。在您的第二种方法中-您不能将其视为对培训过程的模仿,因为例如在第二次折叠中,您的模型将从第一次折叠中保留信息 - 这不等同于您的训练过程。

当然 - 您可以应用使用 10 次连续训练的训练程序来微调网络。但这不是交叉验证 - 您需要使用上面的某种模式来评估此过程。

If my goal is to fine-tune the network for the entire dataset

不清楚您所说的 "fine-tune" 是什么意思,甚至不清楚您执行交叉验证 (CV) 的目的是什么;通常,CV 用于以下目的之一:

  • 模型选择(选择超参数的值)
  • 模型评估

由于您没有在代码中为超参数选择定义任何搜索网格,因此您似乎正在使用 CV 以获得模型的预期性能(误差、准确性等)。

无论如何,无论您出于何种原因使用 CV,第一个片段都是正确的;你的第二个片段

model = None
model = create_model()
for train, test in kFold.split(X, Y):
    train_evaluate(model, X[train], Y[train], X[test], Y[test])

将在不同的分区上 依次 训练您的模型(即在分区 #1 上训练,然后在分区 #2 上继续训练等),这实际上只是对您的整体进行训练数据集,肯定不是交叉验证...

也就是说, CV 之后的最后一步通常只是隐含的(并且经常被初学者错过)是,在您对所选的超参数感到满意之后 and/or 根据您的 CV 程序给出的模型性能,您返回并再次训练您的模型,这次使用 全部 可用数据。

如果您了解嵌套交叉验证,我想您的许多问题都会得到解答。这是 "fine tune" 模型超参数的好方法。这里有一个主题:

https://stats.stackexchange.com/questions/65128/nested-cross-validation-for-model-selection

要注意的最大问题是"peeking" 或循环逻辑。本质上 - 您要确保在训练期间可以看到 none 用于评估模型准确性的数据。

一个可能有问题的例子是,如果您正在 运行使用 PCA 或 ICA 之类的东西来提取特征。如果做这样的事情,你必须确保在你的训练集上运行PCA,然后将训练集的变换矩阵应用到测试集。

您可以将 Scikit-Learn API 的包装器与 Keras 模型一起使用。

给定输入 xy,这是一个重复 5 折交叉验证的示例:

from sklearn.model_selection import RepeatedKFold, cross_val_score
from tensorflow.keras.models import * 
from tensorflow.keras.layers import * 
from tensorflow.keras.wrappers.scikit_learn import KerasRegressor

def buildmodel():
    model= Sequential([
        Dense(10, activation="relu"),
        Dense(5, activation="relu"),
        Dense(1)
    ])
    model.compile(optimizer='adam', loss='mse', metrics=['mse'])
    return(model)

estimator= KerasRegressor(build_fn=buildmodel, epochs=100, batch_size=10, verbose=0)
kfold= RepeatedKFold(n_splits=5, n_repeats=100)
results= cross_val_score(estimator, x, y, cv=kfold, n_jobs=2)  # 2 cpus
results.mean()  # Mean MSE