机器学习 - 交叉验证

交叉验证

在调整模型时,我们的目标是提高模型在未见数据上的整体性能。超参数调整可以使测试集上的性能得到显著提高。然而,针对测试集优化参数会导致信息泄露,从而使模型在未见数据上的表现更差。为了纠正这一点,我们可以执行交叉验证。

为了更好地理解交叉验证(CV),我们将在鸢尾花数据集上执行不同的方法。首先,让我们加载并分离数据。

from sklearn import datasets

X, y = datasets.load_iris(return_X_y=True)

交叉验证有很多方法,我们将从 k 折交叉验证开始。

K 折

用于模型训练的数据被分割成 k 个较小的集合,用于验证模型。然后,模型在 k-1 个训练集上进行训练。剩余的折则用作验证集来评估模型。

由于我们将尝试对鸢尾花的不同物种进行分类,我们需要导入一个分类器模型,在本练习中,我们将使用 DecisionTreeClassifier。我们还需要从 sklearn 导入 CV 模块。

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import KFold, cross_val_score

加载数据后,我们现在可以创建并拟合一个模型进行评估。

clf = DecisionTreeClassifier(random_state=42)

现在让我们评估我们的模型,看看它在每个 k 折上的表现如何。

k_folds = KFold(n_splits = 5)

scores = cross_val_score(clf, X, y, cv = k_folds)

查看所有折的平均得分也是一个好习惯,以了解 CV 的整体表现。

实例

运行 k 折 CV:

from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import KFold, cross_val_score

X, y = datasets.load_iris(return_X_y=True)

clf = DecisionTreeClassifier(random_state=42)

k_folds = KFold(n_splits = 5)

scores = cross_val_score(clf, X, y, cv = k_folds)

print("交叉验证得分:", scores)
print("平均 CV 得分:", scores.mean())
print("用于平均的 CV 得分数量:", len(scores))

亲自试一试

分层 K 折

在类别不平衡的情况下,我们需要在训练和验证集中考虑这种不平衡。为此,我们可以对目标类别进行分层,这意味着两组都将拥有所有类别的相同比例。

实例

from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import StratifiedKFold, cross_val_score

X, y = datasets.load_iris(return_X_y=True)

clf = DecisionTreeClassifier(random_state=42)

sk_folds = StratifiedKFold(n_splits = 5)

scores = cross_val_score(clf, X, y, cv = sk_folds)

print("交叉验证得分:", scores)
print("平均 CV 得分:", scores.mean())
print("用于平均的 CV 得分数量:", len(scores))

亲自试一试

虽然折的数量相同,但在确保分层类别时,平均 CV 得分从基本的 k 折增加。

留一法(Leave-One-Out, LOO)

与 k 折不同,留一法(LeaveOneOut)使用 1 个观测值进行验证,并使用 n-1 个观测值进行训练。这种方法是一种详尽无遗的技术。

实例

运行 LOO CV:

from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import LeaveOneOut, cross_val_score

X, y = datasets.load_iris(return_X_y=True)

clf = DecisionTreeClassifier(random_state=42)

loo = LeaveOneOut()

scores = cross_val_score(clf, X, y, cv = loo)

print("交叉验证得分:", scores)
print("平均 CV 得分:", scores.mean())
print("用于平均的 CV 得分数量:", len(scores))

亲自试一试

我们可以观察到,执行的交叉验证得分数量与数据集中的观测值数量相等。在这种情况下,鸢尾花数据集中有 150 个观测值。

平均 CV 得分为 94%。

留 P 法(Leave-P-Out, LPO)

留 P 法有与留一法的一个细微差别,在于我们可以选择在验证集中使用的 p 的数量。

实例

运行 LPO CV:

from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import LeavePOut, cross_val_score

X, y = datasets.load_iris(return_X_y=True)

clf = DecisionTreeClassifier(random_state=42)

lpo = LeavePOut(p=2)

scores = cross_val_score(clf, X, y, cv = lpo)

print("交叉验证得分:", scores)
print("平均 CV 得分:", scores.mean())
print("用于平均的 CV 得分数量:", len(scores))

亲自试一试

正如我们所见,这是一个详尽无遗的方法,即使 p = 2,也计算了比留一法更多的得分,但达到了大致相同的平均 CV 得分。

随机拆分

与 KFold 不同,ShuffleSplit 留出一定百分比的数据,不用于训练或验证集。为此,我们必须决定训练和测试集的大小,以及拆分的数量。

实例

运行随机拆分 CV:

from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import ShuffleSplit, cross_val_score

X, y = datasets.load_iris(return_X_y=True)

clf = DecisionTreeClassifier(random_state=42)

ss = ShuffleSplit(train_size=0.6, test_size=0.3, n_splits = 5)

scores = cross_val_score(clf, X, y, cv = ss)

print("交叉验证得分:", scores)
print("平均 CV 得分:", scores.mean())
print("用于平均的 CV 得分数量:", len(scores))

亲自试一试

结束说明

这些只是可以应用于模型的几种 CV 方法。还有更多交叉验证类,大多数模型都有自己的类。请查看 sklearn 的交叉验证以获取更多 CV 选项。