在机器学习中,选择合适的模型和参数对于构建一个有效的预测系统至关重要。交叉验证是一种常用的技术,用于评估模型的泛化能力。本文将探讨嵌套交叉验证和非嵌套交叉验证在模型选择和参数优化中的作用及其差异。
嵌套交叉验证(Nested Cross-Validation, NCV)是一种强大的技术,它不仅用于训练模型,还用于优化模型的超参数。通过这种方式,可以估计模型及其参数搜索的泛化误差。如果选择最大化非嵌套交叉验证的参数,模型可能会偏向于数据集,从而得到过于乐观的评分。
模型选择如果不使用嵌套交叉验证,就会使用相同的数据来调整模型参数和评估模型性能。这样,信息可能会“泄露”到模型中,导致数据过拟合。这种效应的程度主要取决于数据集的大小和模型的稳定性。
为了避免这个问题,嵌套交叉验证有效地使用了一系列的训练/验证/测试集分割。在内循环(这里由GridSearchCV执行)中,通过拟合每个训练集来近似最大化分数,然后在验证集上直接最大化选择(超)参数。在外循环(这里在cross_val_score中)中,通过在几个数据集分割上平均测试集分数来估计泛化误差。
以下示例使用具有非线性核的支持向量分类器,通过网格搜索构建具有优化超参数的模型。通过计算它们的分数差异来比较非嵌套和嵌套交叉验证策略的性能。
import numpy as np
from matplotlib import pyplot as plt
from sklearn.datasets import load_iris
from sklearn.model_selection import GridSearchCV, KFold, cross_val_score
from sklearn.svm import SVC
# 随机试验次数
NUM_TRIALS = 30
# 加载数据集
iris = load_iris()
X_iris = iris.data
y_iris = iris.target
# 设置要优化的参数可能值
p_grid = {
"C": [1, 10, 100],
"gamma": [0.01, 0.1]
}
# 使用"rbf"核的支持向量分类器
svm = SVC(kernel="rbf")
# 存储分数的数组
non_nested_scores = np.zeros(NUM_TRIALS)
nested_scores = np.zeros(NUM_TRIALS)
# 每次试验的循环
for i in range(NUM_TRIALS):
# 为内循环和外循环选择交叉验证技术,独立于数据集。
# 例如 "GroupKFold", "LeaveOneOut", "LeaveOneGroupOut" 等。
inner_cv = KFold(n_splits=4, shuffle=True, random_state=i)
outer_cv = KFold(n_splits=4, shuffle=True, random_state=i)
# 非嵌套参数搜索和评分
clf = GridSearchCV(estimator=svm, param_grid=p_grid, cv=outer_cv)
clf.fit(X_iris, y_iris)
non_nested_scores[i] = clf.best_score_
# 嵌套交叉验证与参数优化
clf = GridSearchCV(estimator=svm, param_grid=p_grid, cv=inner_cv)
nested_score = cross_val_score(clf, X=X_iris, y=y_iris, cv=outer_cv)
nested_scores[i] = nested_score.mean()
score_difference = non_nested_scores - nested_scores
print("Average difference of {:6f} with std. dev. of {:6f}.".format(score_difference.mean(), score_difference.std()))
# 在每次试验中绘制嵌套和非嵌套交叉验证的分数
plt.figure()
plt.subplot(211)
(non_nested_scores_line,) = plt.plot(non_nested_scores, color="r")
(nested_line,) = plt.plot(nested_scores, color="b")
plt.ylabel("score", fontsize="14")
plt.legend([non_nested_scores_line, nested_line], ["Non-Nested CV", "Nested CV"], bbox_to_anchor=(0, 0.4, 0.5, 0))
plt.title("Non-Nested and Nested Cross Validation on Iris Dataset", x=0.5, y=1.1, fontsize="15")
# 绘制差异的条形图。
plt.subplot(212)
difference_plot = plt.bar(range(NUM_TRIALS), score_difference)
plt.xlabel("Individual Trial #")
plt.legend([difference_plot], ["Non-Nested CV - Nested CV Score"], bbox_to_anchor=(0, 1, 0.8, 0))
plt.ylabel("score difference", fontsize="14")
plt.show()
通过上述代码示例,可以看到嵌套交叉验证在模型选择和参数优化中的重要性。它通过防止数据泄露和过拟合,提供了一个更准确的模型性能估计。此外,通过比较非嵌套和嵌套交叉验证策略的性能,可以更好地理解它们在实际应用中的优缺点。