交叉验证是一种评估机器学习模型性能的方法,用于衡量机器学习模型的泛化能力(即在未见数据上的表现)。
它通过将数据集分成多个部分,交替使用不同的部分作为训练集和测试集,从而充分利用数据、避免过拟合或欠拟合,并更准确地评估模型的泛化能力。
核心思想
- 数据划分:将数据集划分为训练集和测试集。
- 多次训练与验证:模型在不同的划分上进行训练和测试,以尽可能全面地利用数据。
- 综合评估:取所有测试结果的平均值,作为模型性能的评估指标。
交叉验证解决了因单次划分数据集可能导致模型评估结果不稳定的问题,使得模型评估更加稳健和准确。
常见的交叉验证方法
1.K折交叉验证
将数据集分为 k 个相等大小的子集。每次用其中 k-1 个子集作为训练集,剩下的一个子集作为测试集,重复 k 次,每次选择不同的子集作为测试集。
最终的评估指标为 k 次实验结果的平均值。
优点
- 能够有效利用数据集,每个样本都有机会作为训练集和测试集。
- 评估结果稳定且可靠。
缺点
- 计算开销较大,因为需要进行 K 次训练,时间复杂度较高。
复制
from sklearn.model_selection import KFold from sklearn.metrics import accuracy_score from sklearn.datasets import load_iris from sklearn.ensemble import RandomForestClassifier import numpy as np data = load_iris() X, y = data.data, data.target kf = KFold(n_splits=5, shuffle=True, random_state=42) model = RandomForestClassifier(random_state=42) scores = [] for train_index, test_index in kf.split(X): X_train, X_test = X[train_index], X[test_index] y_train, y_test = y[train_index], y[test_index] model.fit(X_train, y_train) y_pred = model.predict(X_test) scores.append(accuracy_score(y_test, y_pred)) print(f"K-Fold Cross-Validation Scores: {scores}") print(f"Mean Accuracy: {np.mean(scores)}")
2.留一交叉验证
留一交叉验证是一种特殊的 K折交叉验证方法,其中 K等于数据集的样本数(即每个子集只包含一个数据点)。
这种方法每次选择一个样本作为测试集,其余所有样本作为训练集。
优点
- 每个样本都会参与训练和测试,因此可以最大限度地利用数据。
- 在小数据集上特别有用,能够提供更精确的性能评估。
缺点
- 计算成本非常高,尤其是当数据集规模较大时,因为需要进行多次训练和测试。
- 高方差:对单个数据点的敏感度较高,可能导致不稳定的评估结果。
复制
from sklearn.model_selection import LeaveOneOut loo = LeaveOneOut() scores = [] for train_index, test_index in loo.split(X): X_train, X_test = X[train_index], X[test_index] y_train, y_test = y[train_index], y[test_index] model.fit(X_train, y_train) y_pred = model.predict(X_test) scores.append(accuracy_score(y_test, y_pred)) print(f"LOOCV Mean Accuracy: {np.mean(scores)}")
3.分层K折交叉验证
与普通的 K 折交叉验证不同,分层 K 折交叉验证会确保每个折叠中的类别比例与整体数据集中的类别比例相似。这对于类别不平衡的数据集尤其重要。
优点
- 保证每个折叠中的类别分布和整体数据集相似,从而使得评估结果更加稳定和可靠。
- 特别适用于类别不平衡的问题。
缺点
- 和普通的K折交叉验证一样,仍然需要K次训练和测试,因此计算开销较大。
复制
from sklearn.model_selection import StratifiedKFold skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = [] for train_index, test_index in skf.split(X, y): X_train, X_test = X[train_index], X[test_index] y_train, y_test = y[train_index], y[test_index] model.fit(X_train, y_train) y_pred = model.predict(X_test) scores.append(accuracy_score(y_test, y_pred)) print(f"Stratified K-Fold Scores: {scores}") print(f"Mean Accuracy: {np.mean(scores)}")
4.重复 K 折交叉验证
重复 K 折交叉验证是一种通过多次执行 K 折交叉验证来进一步提高模型评估稳定性的方法。
例如,重复 3 次的 5 折交叉验证意味着将数据集随机划分 5 次,进行 3 轮交叉验证,总共进行 15 次训练和测试。
优点:
- 提供了更稳定的评估结果,减少了由于单次 K 折交叉验证的随机性带来的结果波动。
缺点:
- 计算开销较大,因为需要进行多次 K 折交叉验证。
- 适用于需要非常稳定评估结果的情况。
复制
from sklearn.model_selection import RepeatedKFold from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import load_iris import numpy as np # 加载数据 data = load_iris() X, y = data.data, data.target model = RandomForestClassifier(n_estimators=100) rkf = RepeatedKFold(n_splits=5, n_repeats=3, random_state=42) scores = [] for train_index, test_index in rkf.split(X, y): X_train, X_test = X[train_index], X[test_index] y_train, y_test = y[train_index], y[test_index] model.fit(X_train, y_train) score = model.score(X_test, y_test) scores.append(score) print(f"每次折叠的得分: {scores}") print(f"平均得分: {np.mean(scores)}")
5.时间序列交叉验证
时间序列数据的特点是数据点之间有序,且过去的数据对未来的预测有重要意义。
因此,普通的K折交叉验证可能不适用于时间序列问题。
时间序列交叉验证在划分训练集和测试集时考虑了时间顺序,即每次选择一个时间段作为测试集,前面的数据作为训练集。
优点:
- 保持时间序列的因果关系。
- 避免数据泄漏。
缺点:
- 训练集大小逐渐增大,计算成本可能较高。
复制
from sklearn.model_selection import TimeSeriesSplit tscv = TimeSeriesSplit(n_splits=5) scores = [] for train_index, test_index in tscv.split(X): X_train, X_test = X[train_index], X[test_index] y_train, y_test = y[train_index], y[test_index] model.fit(X_train, y_train) y_pred = model.predict(X_test) scores.append(accuracy_score(y_test, y_pred)) print(f"Time Series Cross-Validation Scores: {scores}") print(f"Mean Accuracy: {np.mean(scores)}")
6.嵌套交叉验证
嵌套交叉验证主要用于模型选择和性能评估。外层交叉验证用于评估模型性能,内层交叉验证用于选择最佳的超参数。
优点:
- 避免数据泄漏问题。
- 提供更可靠的模型性能评估。
缺点:
- 计算成本非常高。
复制
from sklearn.model_selection import cross_val_score, GridSearchCV, StratifiedKFold from sklearn.svm import SVC from sklearn.datasets import load_iris import numpy as np # 加载数据 data = load_iris() X, y = data.data, data.target # 设置外层交叉验证 outer_cv = StratifiedKFold(n_splits=5) # 设置内层交叉验证和超参数搜索 param_grid = {'C': [0.1, 1, 10], 'kernel': ['linear', 'rbf']} inner_cv = StratifiedKFold(n_splits=3) grid_search = GridSearchCV(SVC(), param_grid, cv=inner_cv) # 使用外层交叉验证进行评估,内层交叉验证用于超参数调优 nested_score = cross_val_score(grid_search, X, y, cv=outer_cv) # 输出结果 print(f"每次折叠的得分: {nested_score}") print(f"平均得分: {nested_score.mean()}")