趋近智
在上一节中,我们考察了多种评估模型表现的指标。然而,即使使用精密的指标,仅在一次训练-测试分割上评估模型有时也可能具有误导性。恰好落在测试集中的特定数据点会极大地影响计算出的表现得分。如果分割“幸运”,模型可能看起来比实际更好;“不幸运”的分割可能会不公正地惩罚一个好模型。我们需要一种更可靠的方式来评估模型在新数据上的泛化能力。交叉验证正是在此发挥作用。
交叉验证通过系统地使用数据的不同子集进行训练和测试,提供更稳定、更值得信赖的模型表现估计。我们不只进行一次分割,而是进行多次分割并平均结果。
设想您将数据按80%用于训练、20%用于测试进行分割。您训练模型,在测试集上评估,得到85%的准确率。您对这个数字有多大信心?如果您重新打乱数据并创建不同的80/20分割,您可能会得到82%或88%的准确率。这种变异性出现的原因是表现对测试集选取的特定样本很敏感。交叉验证方法旨在降低这种方差,让我们更好地了解模型在新数据上的平均表现。
最常用的交叉验证策略是K折交叉验证。其工作原理如下:
5折交叉验证的视觉表示。数据集被分成5个折叠。在每次迭代中,一个折叠用作测试集,而其他折叠构成训练集。表现将对所有迭代进行平均。
选择 K:
在scikit-learn中,您可以使用sklearn.model_selection模块中的cross_val_score函数或KFold类来实现K折交叉验证。
# 使用scikit-learn的例子
from sklearn.model_selection import KFold, cross_val_score
from sklearn.linear_model import LogisticRegression
import numpy as np
# 假设X(特征)和y(目标)是已定义的NumPy数组或Pandas DataFrame/Series
# 示例占位数据
X = np.random.rand(100, 10)
y = np.random.randint(0, 2, 100)
model = LogisticRegression()
# 定义K折策略(例如,5折)
# shuffle=True 确保在分割前随机打乱
# random_state 确保结果可复现
kfold = KFold(n_splits=5, shuffle=True, random_state=42)
# 执行交叉验证
# 'scoring' 可以是 'accuracy', 'roc_auc', 'f1' 等
scores = cross_val_score(model, X, y, cv=kfold, scoring='accuracy')
print(f"各折分数: {scores}")
print(f"平均交叉验证准确率: {np.mean(scores):.4f}")
print(f"交叉验证准确率的标准差: {np.std(scores):.4f}")
输出显示了5个折叠中每个折叠的准确率分数以及平均准确率,这比单次训练-测试分割提供了更好的估计。标准差则提示了分数在不同折叠间的变异性。
在处理分类问题时,特别是那些类别不平衡(一个类别的出现频率远高于其他类别)的问题,标准K折可能会导致某些折叠中的类别分布出现偏差。例如,一个折叠可能偶然地包含非常少甚至没有少数类样本。这会严重影响训练过程或导致误导性的评估分数。
分层K折通过确保每个折叠中各类别样本的百分比与完整数据集中保持一致来解决这个问题。如果您的数据集包含80%的A类和20%的B类,分层K折会确保每个折叠也包含(大约)80%的A类和20%的B类样本。
这是分类任务的首选方法,因为它确保模型在代表整体类别分布的折叠上进行训练和评估。
在scikit-learn中,您可以使用StratifiedKFold类,或者在执行分类时,直接将整数值传递给cross_val_score的cv参数;scikit-learn通常会为分类估计器默认使用分层K折。
# 使用scikit-learn进行分层K折的例子
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.ensemble import RandomForestClassifier
import numpy as np
# 假设X和y已定义,y可能不平衡
# 示例占位的不平衡数据
X = np.random.rand(100, 10)
y = np.array([0]*80 + [1]*20) # 80% 类别 0, 20% 类别 1
np.random.shuffle(y) # 打乱以混合类别
model = RandomForestClassifier(random_state=42)
# 定义分层K折策略(例如,5折)
stratified_kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# 使用分层K折执行交叉验证
# 对于分类,cross_val_score通常默认使用StratifiedKFold
# 如果传递了y,但明确指定它会更清晰。
scores = cross_val_score(model, X, y, cv=stratified_kfold, scoring='roc_auc') # 使用ROC AUC
print(f"各折分层分数: {scores}")
print(f"平均交叉验证ROC AUC: {np.mean(scores):.4f}")
print(f"交叉验证ROC AUC的标准差: {np.std(scores):.4f}")
虽然K折和分层K折是最常用的,但也存在其他策略:
ShuffleSplit和StratifiedShuffleSplit(后者保持类别比例)提供了在独立控制迭代次数和测试集大小方面的灵活性。交叉验证在超参数调优方法中发挥着重要作用,例如我们之前讨论过的网格搜索和随机搜索。当使用scikit-learn的GridSearchCV或RandomizedSearchCV等工具时,流程如下:
C=1.0,kernel='rbf')。这确保了超参数是基于泛化表现的稳定估计来选择的,而不是基于单次验证分割的表现。
通过采用交叉验证,我们从可能不可靠的单次分割评估转向我们的模型在新数据上可能表现的估计。这对于建立我们模型选择和超参数调优过程的信心是不可或缺的。
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造