机器学习进阶(14):交叉验证

张开发
2026/4/7 20:26:18 15 分钟阅读

分享文章

机器学习进阶(14):交叉验证
第 14 篇交叉验证到底在解决什么问题——为什么切一次验证集有时还不够前面我们已经讲过训练集、验证集、测试集也多次提到过一个词交叉验证。很多人第一次接触交叉验证时都会有点疑惑不是已经有训练集和验证集了吗为什么还要再搞一套验证方式难道一次划分还不够吗这个问题特别值得单独讲。因为交叉验证看起来只是一个“技术细节”但它解决的其实是一个非常现实的问题如果你只切一次验证集你看到的结果可能只是那一次切分碰巧的结果。也就是说模型好不好有时候并不只取决于模型本身还取决于你那次恰好怎么分了数据。交叉验证就是为了尽量减少这种偶然性。1. 先想一个很真实的问题你切的这次验证集真的有代表性吗假设你现在有一份不算特别大的数据集1000 条样本。你按 8:2 切分800 条做训练集200 条做验证集然后训练了一个模型验证集准确率 88%。这看上去没什么问题。但你有没有想过这 200 条样本恰好是你这次分到的。如果换另一批 200 条来做验证结果还会是 88% 吗不一定。有些切分可能刚好验证集比较容易结果就偏高。有些切分可能刚好验证集比较难结果就偏低。尤其当数据量不大或者样本分布本身不太均匀时这种波动会更明显。所以只切一次验证集最大的问题不是“不能用”而是结果可能不够稳。2. 交叉验证本质上是在重复做“轮流验证”交叉验证最常见的一种形式叫K 折交叉验证K-Fold Cross Validation。它的做法很简单把数据分成 K 份每次拿其中 1 份做验证集剩下 K-1 份做训练集这样轮流做 K 次最后把 K 次结果取平均比如 5 折交叉验证第 1 次第 1 份做验证其余做训练第 2 次第 2 份做验证其余做训练…第 5 次第 5 份做验证其余做训练这样每个样本都轮流当过一次验证样本也都在其它轮次里当过训练样本。所以交叉验证其实没有那么玄乎。它做的事情就是不要只信一次切分而是多换几次验证集看看模型表现稳不稳。3. 为什么这么做更可靠如果你只切一次验证集结果受那次切分影响很大。但如果你切 5 次、10 次再把结果平均一下偶然性就会被削弱很多。举个很直白的类比如果你只让一个学生做一套卷子可能刚好那套题他擅长。但如果你让他做 5 套不同的卷子再看平均成绩这个结果通常会更靠谱。交叉验证就是这个思路。它不追求“某一次分得特别漂亮”而是更在意模型在不同切分下整体表现是不是稳定。4. 交叉验证特别适合什么情况交叉验证最有价值的场景通常有两个。第一数据量不大如果数据本来就不多你随便切一块出来做验证集训练集就更少了。这样会带来两个问题模型训练样本不够验证结果也不稳定交叉验证可以让数据被更充分地利用。因为每个样本都不只是“永远待在训练集里”或者“永远待在验证集里”而是轮流参与不同角色。第二你在做模型选择或调参比如你在比较KNN 的 K 取多少更合适随机森林是 100 棵树还是 300 棵树SVM 的 C 和 gamma 怎么配这时候如果只靠一次验证集很容易因为那次切分的偶然性选到一个“看起来好其实不一定真稳”的参数组合。交叉验证就能让这种选择更可靠一点。5. K 折中的 K 怎么选最常见的 K 值一般是5 折10 折为什么是这两个因为它们通常在“计算成本”和“结果稳定性”之间比较平衡。5 折训练 5 次计算量适中已经能显著减少偶然性。很多场景下够用了。10 折比 5 折更稳一些但计算也更慢一点。在数据量不大、你又比较在意评估稳定性时经常会用。当然K 也不是越大越好。K 太大比如接近留一法每次只留一个样本做验证计算量会明显上去而且结果方差也不一定总是更理想。所以大多数时候5 或 10 已经很常见了。6. 分层交叉验证为什么分类任务里特别常用如果你的任务是分类而且类别分布不平衡那普通 K 折有时还不够。比如一份数据里90% 是负类10% 是正类如果你纯随机去分折有可能某一折里正类特别少甚至比例偏得厉害。这会让每一折的验证结果波动很大。这时候就经常用Stratified K-Fold分层 K 折它的作用就是尽量保证每一折里的类别比例都和整体数据差不多。这样评估会稳很多。所以在分类任务里尤其是样本不平衡时分层交叉验证比普通 K 折更常见。7. 交叉验证到底是怎么配合调参一起用的这也是很多人一开始会卡住的地方。比如你想调随机森林的这些参数n_estimatorsmax_depthmin_samples_leaf你当然可以试一组参数看一次验证集结果再试下一组但更稳一点的做法是每一组参数都跑一次交叉验证。也就是说不是只看某一次切分下分数高不高而是看这组参数在多次切分下平均表现怎么样这样选出来的参数一般会更靠谱。这也是为什么GridSearchCV、RandomizedSearchCV这些工具内部通常就是拿交叉验证来帮你比较参数。8. 用代码看一个最基础的交叉验证例子先看最简单的写法importnumpyasnpfromsklearn.model_selectionimportcross_val_scorefromsklearn.ensembleimportRandomForestClassifier Xnp.array([[2,50],[3,55],[4,60],[5,65],[6,70],[7,75],[8,80],[9,85],[1,45],[10,90]])ynp.array([0,0,0,0,1,1,1,1,0,1])modelRandomForestClassifier(random_state42)scorescross_val_score(model,X,y,cv5,scoringaccuracy)print(每折得分,scores)print(平均得分,scores.mean())这段代码的意思就是把数据分成 5 折每次轮流拿 1 折验证算出 5 个准确率最后取平均这里最值得看的是不只是平均分还要看每折分数波动大不大如果平均分不错但每折差异特别大那说明模型不够稳定。9. 再看一个交叉验证配合调参的例子fromsklearn.model_selectionimportGridSearchCVfromsklearn.svmimportSVC param_grid{C:[0.1,1,10],gamma:[0.01,0.1,1],kernel:[rbf]}gridGridSearchCV(estimatorSVC(),param_gridparam_grid,cv5,scoringaccuracy)grid.fit(X,y)print(最优参数,grid.best_params_)print(最佳交叉验证得分,grid.best_score_)这里的逻辑是每组参数都做 5 折交叉验证比较平均得分选出最优组合所以你可以把GridSearchCV理解成参数搜索 交叉验证打分这两个东西经常是一起出现的。10. 交叉验证能不能代替测试集这是一个特别值得单独说清楚的问题。答案是不能完全代替。交叉验证主要是用来做模型选择参数调优估计模型在训练阶段的大致表现但你最后最好还是要留一个真正独立的测试集。为什么因为你在用交叉验证调参数的时候实际上已经在“利用这些数据做选择”了。哪怕它比单次验证稳很多它依然属于模型开发过程的一部分。所以更规范的流程通常是先留出测试集不碰在训练集上做交叉验证选好模型和参数最后拿测试集做一次最终评估这个顺序非常重要。交叉验证是帮助你更稳地选模型不是让你彻底不需要测试集。11. 时间序列任务里为什么普通交叉验证不合适这里一定要提醒一下不然很多人一上来就会拿cv5到处套。如果你的任务有明显的时间顺序比如股票预测销量预测用户未来行为预测那普通 K 折交叉验证通常不合适。因为普通 K 折会随机打乱数据让未来的数据有机会出现在训练集中再去预测过去的数据。这在真实场景里根本不成立。时间序列任务更适合用按时间顺序滚动验证expanding windowsliding window也就是说交叉验证不是“万能模板”它也得尊重数据本身的结构。12. 交叉验证真正解决的不是“模型一定更好”而是“你对结果更有底”这一点特别值得你在文章里写出来。交叉验证并不会神奇地让模型自动变强。它真正解决的是你对模型效果的判断能不能更稳一点。也就是说它改进的首先不是模型本身而是你的评估可信度。这其实特别重要。因为做机器学习很容易陷入一种错觉这个模型 89%那个模型 90%我就选 90% 的但如果这两个数字只是一次偶然切分的结果那个 1% 的差距可能根本没那么可信。交叉验证就是在帮你减少这种错觉。13. 这一篇真正该留下来的东西讲到这里交叉验证最核心的意义其实已经很清楚了单次划分有偶然性数据少时这种偶然性更明显调参时这种偶然性会误导选择所以需要多次轮流验证来得到更稳定的评估结果如果你把这件事理解透了后面很多实践操作都会更自然为什么GridSearchCV默认带交叉验证为什么很多论文报告的是 CV 均值为什么调参时不能只看一次 lucky split为什么最终还得单独留测试集所以交叉验证并不是“额外多学一个工具”而是机器学习里一种很重要的实验习惯。

更多文章