🐸 青蛙编程网
首页 / AI / 机器学习 / 4. 模型评估

模型评估

这是什么:模型评估 = 给模型打分的办法:训练集上刷题不算数,真实水平只看测试集成绩。

为什么用:上一篇的模型已经会预测了,但"会预测"不等于"预测得准"。用训练集自己考自己,分数只会虚高——这一篇解决一个关键问题:怎么给模型打分?准确率高就好吗?

如何用:本章用 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) 回归任务看数字偏差,不看准确率

📝 本章小结

  • 模型真实水平只看测试集,训练集分数只能作参考
  • 准确率 = 答对 ÷ 总题,直观但可能误导(尤其类别极不平衡时)
  • 过拟合 = 训练集很高、测试集显著偏低,即"背题不会新题"
  • 调参不该用测试集,测试集只在最后用一次
  • 记忆口诀:训练集用来学,测试集用来考;训练集满分别高兴,先问考的是不是背过的题。

动手改

  1. 把 y_pred 里第 4、7 题改成和 y_true 一样,先口算准确率,再运行验证
  2. 不用 accuracy_score,自己用 sum 和 len 算一遍准确率,结果一样吗?
  3. 思考:癌症检测里,"漏诊"和"误诊"的代价一样吗?——准确率之外,还有别的指标值得关心

下一章:实战:房价预测,用线性回归完成第一个完整项目 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。