展开

Cross-validation

最后发布时间 : 2023-04-22 21:44:00 浏览量 :

学习一个预测函数的参数并在相同的数据上进行测试是一个方法上的错误: 一个模型只是重复它刚刚看到的样本的标签就会得到一个完美的分数,但是不能预测任何有用的尚未看到的数据。这种情况叫做过拟合(overfitting)。为了避免这种情况,在进行(有监督的)机器学习实验时,通常的做法是将部分可用数据作为测试集 X _ test,y _ test。请注意,“实验”一词并不仅仅指学术用途,因为即使在商业环境中,机器学习通常也是从实验开始的。下面是模型训练中典型的交叉验证工作流程图。通过网格搜索技术可以确定最佳参数。

生信小木屋

在 scikit-learn 中,可以使用train _ test _ split辅助函数快速计算随机分割为训练集和测试集的结果。让我们加载iris数据集,以拟合它的线性支持向量机:

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn import datasets
from sklearn import svm
X, y = datasets.load_iris(return_X_y=True)
X.shape, y.shape
((150, 4), (150,))

我们现在可以快速抽样一个训练集,同时拿出40% 的数据来测试(评估)我们的分类器:

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=0)
X_train.shape, y_train.shape
((90, 4), (90,))
X_test.shape, y_test.shape
((60, 4), (60,))
clf = svm.SVC(kernel='linear', C=1).fit(X_train, y_train)
clf.score(X_test, y_test)
0.9666666666666667

当评估估计器的不同设置(“超参数”)时,例如必须为支持向量机手动设置的 C 设置,仍然存在测试集过拟合的风险,因为参数可以被调整,直到估计器执行最佳。这样,关于测试集的知识就可以“泄漏”到模型中,评估指标就不再报告泛化性能。为了解决这个问题,数据集的另一部分可以作为所谓的“验证集”: 训练在训练集上进行,之后在验证集上进行评估,当实验似乎成功时,可以在测试集上进行最终评估。

然而,通过将可用数据划分为三个集合,我们大大减少了可用于学习模型的样本数量,并且结果可以依赖于一对(训练,验证)集合的特定随机选择。

解决这个问题的方法是一个叫做交叉验证的程序(简称 CV)。测试集仍然应该等待最终的评估,但是在进行 CV 时不再需要验证集。在称为 k 倍 CV 的基本方法中,训练集被划分为 k 个较小的集(下面描述其他方法,但通常遵循相同的原则)。对于每个 k“折叠”都遵循以下步骤:

  • 使用折叠中的k-1个作为训练数据来训练模型;
  • 结果模型在数据的其余部分进行验证(例如,它被用作一个测试集来计算性能度量,例如准确性)。

然后,k 倍交叉验证报告的性能指标就是循环中计算的值的平均值。这种方法在计算上可能很昂贵,但是不会浪费太多的数据(在修复任意验证集时就是这种情况) ,这在诸如逆向推理等样本数量非常少的问题中是一个主要的优势。

生信小木屋

计算交叉验证的指标

使用交叉验证最简单的方法是对估计器和数据集调用cross _ val _ score函数。
以下示例说明如何透过分割数据、拟合模型及连续5次计算得分(每次不同的分割) ,估计iris数据集的线性内核支持向量机的准确度:

from sklearn.model_selection import cross_val_score
clf = svm.SVC(kernel='linear', C=1, random_state=42)
scores = cross_val_score(clf, X, y, cv=5)
scores
array([0.96666667, 1.        , 0.96666667, 0.96666667, 1.        ])

因此,平均分及标准差由以下方法计算:

print("%0.2f accuracy with a standard deviation of %0.2f" % (scores.mean(), scores.std()))
0.98 accuracy with a standard deviation of 0.02

默认情况下,在每个 CV 迭代中计算的得分是估计器的得分方法。可以通过使用scoring参数来改变这一点:

from sklearn import metrics
scores = cross_val_score(clf, X, y, cv=5, scoring='f1_macro')
scores
array([0.96658312, 1.        , 0.96658312, 0.96658312, 1.        ])

在iris数据集的情况下,样本是平衡的目标类,因此accuracyF1-score几乎相等。
当 cv 参数是一个整数时,cross _ val _ score 默认使用 KFoldStratifiedKFold策略,如果估计器派生自 ClassfierMixin,则使用后者。

还可以通过传递交叉验证迭代器来使用其他交叉验证策略,例如:

from sklearn.model_selection import ShuffleSplit
n_samples = X.shape[0]
cv = ShuffleSplit(n_splits=5, test_size=0.3, random_state=0)
cross_val_score(clf, X, y, cv=cv)
array([0.97777778, 0.97777778, 1.        , 0.95555556, 1.        ])

另一种选择是使
用可迭代的产生(train,test)拆分作为索引数组,例如:

def custom_cv_2folds(X):
    n = X.shape[0]
    i = 1
    while i <= 2:
        idx = np.arange(n * (i - 1) / 2, n * i / 2, dtype=int)
        yield idx, idx
        i += 1

custom_cv = custom_cv_2folds(X)
cross_val_score(clf, X, y, cv=custom_cv)
array([1.        , 0.97333333])

保留数据的数据转换

正如测试一个预测器对于从训练、预处理(例如标准化、特征选择等)和类似的数据转换中提取出来的数据是重要的一样,类似地,应该从训练集中学习并应用于提取出来的数据进行预测:

from sklearn import preprocessing
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=0)
scaler = preprocessing.StandardScaler().fit(X_train)
X_train_transformed = scaler.transform(X_train)
clf = svm.SVC(C=1).fit(X_train_transformed, y_train)
X_test_transformed = scaler.transform(X_test)
clf.score(X_test_transformed, y_test)
0.9333333333333333

管道使得构建估计器更加容易,在交叉验证下提供这种行为:

from sklearn.pipeline import make_pipeline
clf = make_pipeline(preprocessing.StandardScaler(), svm.SVC(C=1))
cross_val_score(clf, X, y, cv=cv)
array([0.97777778, 0.93333333, 0.95555556, 0.93333333, 0.97777778])

交叉验证函数和多度量评价

Cross _ valid函数与 cross _ val _ score 有两个不同之处:

  • 它允许指定多个评估指标。
  • 除了测试分数之外,它还返回一个包含适合时间、得分时间(以及可选的训练分数和适合的估计值)的结果。
    对于单一度量计算,其中计分参数是字符串、可调用或无,键为-[‘ test _ score’、‘ fit _ time’、‘ score _ time’]
    对于多重度量评估,返回值是一个包含以下键的 dict-[‘ test _ < scoer1 _ name >’,‘ test _ < scoer2 _ name >’,‘ test _ < scoer... >’,‘ fit _ time’,‘ score _ time’]
    Return _ train _ score 默认设置为 False 以保存计算时间。要评估训练集上的分数,还需要将其设置为 True。
    您也可以通过设置 return _ Estiator = True来保留每个训练集上适合的估计值。
    多个指标可以指定为一个列表、元组或一组预定义的记分器名称:
from sklearn.model_selection import cross_validate
from sklearn.metrics import recall_score
scoring = ['precision_macro', 'recall_macro']
clf = svm.SVC(kernel='linear', C=1, random_state=0)
scores = cross_validate(clf, X, y, scoring=scoring)
sorted(scores.keys())
['fit_time', 'score_time', 'test_precision_macro', 'test_recall_macro']

通过交叉验证获取预测