Skip to content

模型评估:别被 99% 的准确率骗了 ​

这一篇比任何算法都重要。 评估错了,模型再好也是白做;评估对了,你能一眼看出谁在自欺。

一个真实场景 ​

信用卡欺诈检测,欺诈占比 0.5%。你的模型把所有交易都判为「正常」,准确率 99.5%。 这个模型毫无价值,但它「准确率」很高。

结论:指标选错,努力白费。

一、分类指标:先看混淆矩阵 ​

预测为正预测为负
实际为正TP(真正例)FN(漏报)
实际为负FP(误报)TN(真负例)

由它派生出所有分类指标:

指标公式回答的问题何时重点看
准确率 Accuracy(TP+TN)/全部整体对了多少仅类别均衡时
精确率 PrecisionTP/(TP+FP)报出来的有多少是真的误报代价高(推荐、审核)
召回率 RecallTP/(TP+FN)真的有多少被抓到漏报代价高(风控、医疗)
F12PR/(P+R)P 与 R 的调和平均需要单指标汇总
AUC-ROC曲线下面积排序能力,与阈值无关类别均衡偏好、需要整体排序
PR-AUCPR 曲线下面积正类稀有时的排序能力极不平衡时首选

记住这条

类别比例 < 10% 时,别用准确率,别只看 ROC-AUC,要看 PR-AUC 与 F1。 ROC 曲线在极不平衡时会被大量 TN 拉得虚高,看起来很美,业务上没用。

python
from sklearn.metrics import (classification_report, confusion_matrix,
                             roc_auc_score, average_precision_score)

y_pred = clf.predict(X_test)
y_proba = clf.predict_proba(X_test)[:, 1]

print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred, digits=3))
print("ROC-AUC:", round(roc_auc_score(y_test, y_proba), 4))
print("PR-AUC :", round(average_precision_score(y_test, y_proba), 4))

二、阈值不是 0.5 —— 是业务决策 ​

模型输出概率,阈值决定「多确信才报警」。

阈值召回精确业务含义
0.2高低宁可错杀,人工复核量大
0.5中中默认,通常不是最优
0.8低高只抓最确定的,漏很多

正确做法:算出代价函数,选期望代价最小的阈值。

python
import numpy as np
# 漏报损失 1000 元,误报损失 20 元(人工核查成本)
cost_fn, cost_fp = 1000, 20

rows = []
for t in np.arange(0.05, 0.96, 0.05):
    pred = y_proba >= t
    fn = ((y_test == 1) & ~pred).sum()
    fp = ((y_test == 0) & pred).sum()
    rows.append((t, fn, fp, fn * cost_fn + fp * cost_fp))

best = min(rows, key=lambda r: r[3])
print(f"最优阈值={best[0]:.2f}  期望总代价={best[3]:.0f} 元")

这段代码的价值远超任何调参 —— 它把「模型」变成「决策」。

三、回归指标 ​

指标含义注意
MAE平均绝对误差与原目标同量纲,最易解释
RMSE均方根误差对大误差惩罚重,怕异常值
MAPE平均百分比误差目标接近 0 时会爆炸
R²解释了多少方差可为负(比预测均值还差)
python
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import numpy as np

print("MAE :", round(mean_absolute_error(y_test, pred), 3))
print("RMSE:", round(np.sqrt(mean_squared_error(y_test, pred)), 3))
print("R²  :", round(r2_score(y_test, pred), 3))

永远先看 MAE 的量级:一个 MAE=50 的房价模型(单位万元)意味着平均差 50 万,业务上可能完全不可用,哪怕 R²=0.9。

四、交叉验证:小数据集的必修课 ​

单次切分的结果随机性太大。K 折交叉验证把数据切 K 份,轮流做验证。

python
from sklearn.model_selection import cross_val_score, StratifiedKFold

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(clf, X, y, cv=cv, scoring="roc_auc")
print(f"AUC = {scores.mean():.4f} ± {scores.std():.4f}")
场景用什么
普通分类StratifiedKFold(保持类别比例)
普通回归KFold
时间序列TimeSeriesSplit(绝不能用随机切分)
分组数据(同一用户多条)GroupKFold(防止同组跨越 train/test)

时间序列用随机切分 = 穿越未来

随机切分会让模型「用明天的数据预测今天」。必须按时间顺序切:训练用过去,验证用未来。

五、数据泄漏:分数虚高的头号元凶 ​

数据泄漏 = 训练时用到了预测时拿不到的信息。

泄漏类型例子怎么发现
预处理泄漏在全量数据上做标准化/填充分数异常高,去掉泄漏后大跌
目标泄漏特征里含「结果」信息(如「结案金额」用于预测「是否结案」)单特征重要性异常高(>0.9)
时间穿越随机切分时间数据换 TimeSeriesSplit 后分数降很多
重复样本同一实体出现在 train 和 test用 groupby 查重复
目标编码泄漏用全量 y 均值编码类别必须 CV 内做

检查清单(上线前必过):

六、报告怎么写 ​

一份可信的模型报告包含:

部分内容
问题定义业务问题、标签定义、成功标准(指标 + 阈值)
数据规模、时间范围、缺失情况、切分方式
基线常数基线 + 简单模型的分数
最终模型算法、超参、CV 分数(均值 ± 标准差)
测试集结果混淆矩阵、PR-AUC、选定阈值及理由
重要特征Top10 及业务解释
局限数据偏差、覆盖范围、已知失效场景
上线建议阈值、监控指标、重训频率

最后两行最能体现水平。 只报好数字、不说局限的报告,是新手报告。

通过标准 ​

下一步 ​

→ 集成学习与树模型

AI 学习路线 · 从入门到专家