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

第 14 篇:交叉验证到底在解决什么问题------为什么切一次验证集,有时还不够

前面我们已经讲过训练集、验证集、测试集,也多次提到过一个词:交叉验证

很多人第一次接触交叉验证时,都会有点疑惑:

不是已经有训练集和验证集了吗?

为什么还要再搞一套验证方式?

难道一次划分还不够吗?

这个问题特别值得单独讲。

因为交叉验证看起来只是一个"技术细节",但它解决的其实是一个非常现实的问题:

如果你只切一次验证集,你看到的结果,可能只是那一次切分碰巧的结果。

也就是说,模型好不好,有时候并不只取决于模型本身,还取决于你那次恰好怎么分了数据。

交叉验证,就是为了尽量减少这种偶然性。


1. 先想一个很真实的问题:你切的这次验证集,真的有代表性吗

假设你现在有一份不算特别大的数据集,1000 条样本。

你按 8:2 切分:

  • 800 条做训练集
  • 200 条做验证集

然后训练了一个模型,验证集准确率 88%。

这看上去没什么问题。

但你有没有想过:

这 200 条样本,恰好是你这次分到的。

如果换另一批 200 条来做验证,结果还会是 88% 吗?

不一定。

有些切分可能刚好验证集比较容易,结果就偏高。

有些切分可能刚好验证集比较难,结果就偏低。

尤其当数据量不大,或者样本分布本身不太均匀时,这种波动会更明显。

所以只切一次验证集,最大的问题不是"不能用",而是:

结果可能不够稳。


2. 交叉验证,本质上是在重复做"轮流验证"

交叉验证最常见的一种形式,叫 K 折交叉验证(K-Fold Cross Validation)

它的做法很简单:

  1. 把数据分成 K 份
  2. 每次拿其中 1 份做验证集
  3. 剩下 K-1 份做训练集
  4. 这样轮流做 K 次
  5. 最后把 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_estimators
  • max_depth
  • min_samples_leaf

你当然可以:

  • 试一组参数
  • 看一次验证集结果
  • 再试下一组

但更稳一点的做法是:

每一组参数,都跑一次交叉验证。

也就是说:

  • 不是只看某一次切分下分数高不高
  • 而是看这组参数在多次切分下平均表现怎么样

这样选出来的参数,一般会更靠谱。

这也是为什么 GridSearchCVRandomizedSearchCV 这些工具内部,通常就是拿交叉验证来帮你比较参数。


8. 用代码看一个最基础的交叉验证例子

先看最简单的写法:

python 复制代码
import numpy as np
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier

X = np.array([
    [2, 50],
    [3, 55],
    [4, 60],
    [5, 65],
    [6, 70],
    [7, 75],
    [8, 80],
    [9, 85],
    [1, 45],
    [10, 90]
])

y = np.array([0, 0, 0, 0, 1, 1, 1, 1, 0, 1])

model = RandomForestClassifier(random_state=42)

scores = cross_val_score(model, X, y, cv=5, scoring="accuracy")

print("每折得分:", scores)
print("平均得分:", scores.mean())

这段代码的意思就是:

  • 把数据分成 5 折
  • 每次轮流拿 1 折验证
  • 算出 5 个准确率
  • 最后取平均

这里最值得看的是:

  • 不只是平均分
  • 还要看每折分数波动大不大

如果平均分不错,但每折差异特别大,那说明模型不够稳定。


9. 再看一个交叉验证配合调参的例子

python 复制代码
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC

param_grid = {
    "C": [0.1, 1, 10],
    "gamma": [0.01, 0.1, 1],
    "kernel": ["rbf"]
}

grid = GridSearchCV(
    estimator=SVC(),
    param_grid=param_grid,
    cv=5,
    scoring="accuracy"
)

grid.fit(X, y)

print("最优参数:", grid.best_params_)
print("最佳交叉验证得分:", grid.best_score_)

这里的逻辑是:

  • 每组参数都做 5 折交叉验证
  • 比较平均得分
  • 选出最优组合

所以你可以把 GridSearchCV 理解成:

参数搜索 + 交叉验证打分

这两个东西经常是一起出现的。


10. 交叉验证能不能代替测试集

这是一个特别值得单独说清楚的问题。

答案是:

不能完全代替。

交叉验证主要是用来做:

  • 模型选择
  • 参数调优
  • 估计模型在训练阶段的大致表现

但你最后最好还是要留一个真正独立的测试集。

为什么?

因为你在用交叉验证调参数的时候,实际上已经在"利用这些数据做选择"了。

哪怕它比单次验证稳很多,它依然属于模型开发过程的一部分。

所以更规范的流程通常是:

  1. 先留出测试集,不碰
  2. 在训练集上做交叉验证
  3. 选好模型和参数
  4. 最后拿测试集做一次最终评估

这个顺序非常重要。

交叉验证是帮助你更稳地选模型,不是让你彻底不需要测试集。


11. 时间序列任务里,为什么普通交叉验证不合适

这里一定要提醒一下,不然很多人一上来就会拿 cv=5 到处套。

如果你的任务有明显的时间顺序,比如:

  • 股票预测
  • 销量预测
  • 用户未来行为预测

那普通 K 折交叉验证通常不合适。

因为普通 K 折会随机打乱数据,让未来的数据有机会出现在训练集中,再去预测过去的数据。

这在真实场景里根本不成立。

时间序列任务更适合用:

  • 按时间顺序滚动验证
  • expanding window
  • sliding window

也就是说,交叉验证不是"万能模板",它也得尊重数据本身的结构。


12. 交叉验证真正解决的,不是"模型一定更好",而是"你对结果更有底"

这一点特别值得你在文章里写出来。

交叉验证并不会神奇地让模型自动变强。

它真正解决的是:

你对模型效果的判断,能不能更稳一点。

也就是说,它改进的首先不是模型本身,而是你的评估可信度

这其实特别重要。

因为做机器学习,很容易陷入一种错觉:

  • 这个模型 89%
  • 那个模型 90%
  • 我就选 90% 的

但如果这两个数字只是一次偶然切分的结果,那个 1% 的差距可能根本没那么可信。

交叉验证就是在帮你减少这种错觉。


13. 这一篇真正该留下来的东西

讲到这里,交叉验证最核心的意义其实已经很清楚了:

  • 单次划分有偶然性
  • 数据少时这种偶然性更明显
  • 调参时这种偶然性会误导选择
  • 所以需要多次轮流验证,来得到更稳定的评估结果

如果你把这件事理解透了,后面很多实践操作都会更自然:

  • 为什么 GridSearchCV 默认带交叉验证
  • 为什么很多论文报告的是 CV 均值
  • 为什么调参时不能只看一次 lucky split
  • 为什么最终还得单独留测试集

所以交叉验证并不是"额外多学一个工具",而是机器学习里一种很重要的实验习惯。

相关推荐
大连好光景1 小时前
回顾机器学习几个模型(监督+分类任务)
决策树·随机森林·机器学习·逻辑回归·svm
lolo大魔王2 小时前
Go语言的循环语句、判断语句、通道选择语句
开发语言·算法·golang
B博士3 小时前
科研进展 | JAG: 大光斑高光谱激光雷达遥感辐射传输模型从垂直视角解锁森林叶绿素分布密码
人工智能·jag·高光谱激光雷达·森林分层叶绿素诊断
Yao.Li3 小时前
PVN3D ORT CUDA Custom Ops 实现与联调记录
人工智能·3d·具身智能
诺伦3 小时前
LocalClaw 在智能制造的新机会:6部门AI+电商政策下的工厂AI升级方案
人工智能·制造
小陈工5 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
海清河晏1116 小时前
数据结构 | 单循环链表
数据结构·算法·链表
wuweijianlove10 小时前
算法性能的渐近与非渐近行为对比的技术4
算法
墨染天姬10 小时前
【AI】端侧AIBOX可以部署哪些智能体
人工智能