模型评估
这是什么:模型评估 = 给模型打分的办法:训练集上刷题不算数,真实水平只看测试集成绩。
为什么用:上一篇的模型已经会预测了,但"会预测"不等于"预测得准"。用训练集自己考自己,分数只会虚高——这一篇解决一个关键问题:怎么给模型打分?准确率高就好吗?
如何用:本章用 train_test_split 把数据切成训练集和测试集,两边各算一次准确率,对比着看。
为什么要分训练集和测试集
设想一个学生:平时刷题册上的题他全部背下来了,你拿刷过的题考他,他考 100 分——这能证明他学会了,还是只是背下来了?
模型也一样。用训练集自己考自己,分数只会虚高。所以数据要分两份:
- 训练集 = 平时刷的题,专门用来学习
- 测试集 = 高考卷,全是没见过的新题,专门用来验收
铁律:模型的真实水平,只看测试集成绩。
代码示例:只在测试集打分
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42)
model = KNeighborsClassifier(n_neighbors=3)
model.fit(X_train, y_train) # 只训练集学习
train_score = model.score(X_train, y_train) # 训练集上看(参考)
test_score = model.score(X_test, y_test) # 测试集上看(才是真实水平)
print("训练集准确率:", round(train_score, 3))
print("测试集准确率:", round(test_score, 3))
你会看到(示意):
训练集准确率: 1.0
测试集准确率: 0.947
训练集满分,测试集略低——正常。如果训练集 1.0 而测试集只 0.6,那就是过拟合(下文会讲)。
准确率:答对的比例
最直观的指标是准确率(accuracy):答对的题数 ÷ 总题数。用 sklearn 的 accuracy_score 来算:
from sklearn.metrics import accuracy_score
# 10 道题的标准答案(1 = 是,0 = 否)
y_true = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0]
# 模型的 10 个预测
y_pred = [1, 0, 1, 0, 0, 1, 1, 0, 1, 0]
print("答对题数:", sum(a == b for a, b in zip(y_true, y_pred)))
print("准确率:", accuracy_score(y_true, y_pred))
你会看到:
答对题数: 8
准确率: 0.8
10 道题答对 8 道,准确率 0.8,也就是 80%。
| 代码 | 含义 |
|---|---|
y_true |
标准答案,用来逐题对照 |
y_pred |
模型交的卷子 |
accuracy_score(答案, 预测) |
对答案,算答对比例 |
0.8 |
10 题对 8 题 |
顺手一提:上一篇的鸢尾花模型也能一行打分——model.score(X_test, y_test)。鸢尾花是张"简单卷",K 近邻通常能考到 95% 以上。
为什么准确率不一定够用
准确率看起来很简单,但在某些场景下会误导人。
场景:罕见病筛查
假设 1000 个人里只有 1 个患病(患病率 0.1%),一个"懒惰模型"直接预测所有人都"健康":
y_true = [0] * 999 + [1] # 只有 1 个病人
y_pred = [0] * 1000 # 模型全预测健康
from sklearn.metrics import accuracy_score
print("准确率:", accuracy_score(y_true, y_pred)) # 0.999!
准确率 99.9%,看起来完美,但实际上模型一个病人都没查出来。这时候需要别的指标:
- 查全率(Recall):所有病人里,被找出来的占多少?(越高越不漏诊)
- 查准率(Precision):被预测为病人的里,真病人占多少?(越高越不误诊)
医疗筛查通常更看重查全率(宁可误诊,不可漏诊);垃圾邮件过滤更看重查准率(宁可漏掉一封垃圾邮件,不可把重要邮件当垃圾)。
过拟合初印象
是不是训练集分数越高越好?看一个极端例子:把 K 近邻的 n_neighbors 设成 1,模型在训练集上的准确率永远是 100%——因为每个样本离自己最近,等于"考的全是背过的题"。
但如果模型把训练集里的每一朵花(包括长得特殊的、甚至标错的)都死记硬背,遇到新花反而容易判断错。这种"把训练数据背得太死,新题反而考砸"的现象叫过拟合(overfitting)——像只会背题、不会举一反三的学霸。以后你训每一个模型,都要警惕它。
过拟合 vs 欠拟合
| 现象 | 训练集分数 | 测试集分数 | 诊断 | 对策 |
|---|---|---|---|---|
| 欠拟合 | 低 | 低 | 没学会 | 加特征、换更强的模型 |
| 刚好 | 高 | 略低 | 理想状态 | 保持 |
| 过拟合 | 很高 | 显著偏低 | 背题了 | 简化模型、更多数据、正则化 |
记忆口诀:训练集用来学,测试集用来考;训练集满分别高兴,先问考的是不是背过的题。
❓ 常见问题
Q1:测试集占比必须是 20% 吗?
不是,20% 是经验值。数据量少时可用 25%–30%;数据量巨大(百万级以上)时,测试集占比可降到 10% 左右,因为测试集已经足够大能反映真实水平。
Q2:准确率 90% 是不是就很好?
要看任务。预测"明天下不下雨"(历史上 80% 天不下雨),一个"天天不下雨"的模型准确率就有 80%,但毫无价值。要和"随便猜"的基准线比。
Q3:训练集和测试集分数差太多怎么办?
训练集很高、测试集很低,就是过拟合。尝试:更多数据、简化模型(如 K 近邻调大 n_neighbors)、加正则化。
⚠️ 新手翻车现场
翻车一:在测试集上调参数
# 错误:反复在测试集上试不同的 n_neighbors,选分数最高的
for k in [1, 3, 5, 7, 9]:
model = KNeighborsClassifier(n_neighbors=k)
model.fit(X_train, y_train)
score = model.score(X_test, y_test) # 把测试集当调参工具
print(k, score)
问题:测试集本来是"没见过的高考卷",你拿它来刷题、选答案,测试集就泄露了,最后报出的分数虚高。
修正:调参只用训练集(或再从训练集里切出一小部分验证集),测试集只在最后用一次,给出最终成绩。
翻车二:把训练集当测试集打分
# 错误:fit 完立刻在同一组数据上 score
model.fit(X_train, y_train)
print(model.score(X_train, y_train)) # 永远偏高
问题:这就是"背题后考刷过的题",分数没参考价值。
修正:始终在 X_test, y_test 上调用 score。
🎯 什么时候重点关注哪个指标
| 场景 | 更看重 | 原因 |
|---|---|---|
| 癌症筛查、航空安全 | 查全率 (Recall) | 漏诊代价极大,宁可误报 |
| 垃圾邮件过滤、信用卡欺诈 | 查准率 (Precision) | 误报会正常邮件被拦、正常交易被冻结 |
| 房价预测、气温预报 | 均方误差 (MSE/MAE) | 回归任务看数字偏差,不看准确率 |
📝 本章小结
- 模型真实水平只看测试集,训练集分数只能作参考
- 准确率 = 答对 ÷ 总题,直观但可能误导(尤其类别极不平衡时)
- 过拟合 = 训练集很高、测试集显著偏低,即"背题不会新题"
- 调参不该用测试集,测试集只在最后用一次
- 记忆口诀:训练集用来学,测试集用来考;训练集满分别高兴,先问考的是不是背过的题。
动手改
- 把
y_pred里第 4、7 题改成和y_true一样,先口算准确率,再运行验证 - 不用
accuracy_score,自己用sum和len算一遍准确率,结果一样吗? - 思考:癌症检测里,"漏诊"和"误诊"的代价一样吗?——准确率之外,还有别的指标值得关心
下一章:实战:房价预测,用线性回归完成第一个完整项目 🐸