一、为什么评估分类模型不能只看准确率一个极端的例子1000 个样本里只有 10 个欺诈交易模型什么都不做、全部猜正常准确率就有 99%——但这个模型完全没用。所以评估分类模型的核心问题是用什么视角看准不准取决于业务更怕哪种错误。二、一切的原点混淆矩阵预测为正 预测为负 ┌──────────┬──────────────┬──────────────┐ 实际为正 │ TP真正例 │ FN假负例 │ ├──────────┼──────────────┼──────────────┤ 实际为负 │ FP假正例 │ TN真负例 │ └──────────┴──────────────┴──────────────┘名称俗称含义TP抓对了实际是正模型也判正FN漏了实际是正模型却判负FP冤枉了实际是负模型却判正TN正确排除实际是负模型也判负fromsklearn.metricsimportconfusion_matrix,ConfusionMatrixDisplay cmconfusion_matrix(y_test,y_pred)dispConfusionMatrixDisplay(cm,display_labels[正常,欺诈])disp.plot()所有后续指标都从这四个数派生出来。三、基础指标1. 准确率AccuracyAccuracyTPTNTPFPFNTN\text{Accuracy} \frac{TP TN}{TP FP FN TN}AccuracyTPFPFNTNTPTN​含义所有样本中模型判对了多少比例。什么时候好用类别比较平衡、两种错误代价差不多时。什么时候失效类别严重不平衡——正例只占 1% 时全猜负就有 99% 的准确率。2. 精确率PrecisionPrecisionTPTPFP\text{Precision} \frac{TP}{TP FP}PrecisionTPFPTP​含义模型说这是正例的那些里面多少真的是正例。通俗理解我报上去的嫌疑人里多少是真犯。关注精确率的场景原因垃圾邮件过滤把正常邮件误判为垃圾邮件用户很愤怒搜索引擎排序前几条结果不相关用户就不信任了推荐系统推了用户不喜欢的东西体验差3. 召回率Recall / Sensitivity / TPRRecallTPTPFN\text{Recall} \frac{TP}{TP FN}RecallTPFNTP​含义真正的正例中模型找出来多少。通俗理解所有真犯里我抓到了多少。关注召回率的场景原因癌症筛查漏掉一个病人比误报严重百倍欺诈检测漏掉一笔欺诈损失可能巨大故障预警漏报警比误报代价高得多4. 精确率与召回率的关系它们是此消彼长的。阈值调低 → 召回率升、精确率降阈值调高 → 精确率升、召回率降。阈值 0.9: Precision0.95 Recall0.30 (很谨慎只报很有把握的) 阈值 0.5: Precision0.80 Recall0.70 (默认平衡点) 阈值 0.2: Precision0.50 Recall0.95 (宁可错杀不放过)Precision 1.0 |\ | \ | \___________ | 0.0 ────────────── Recall 0.0 1.05. F1-ScoreF12×Precision×RecallPrecisionRecallF1 \frac{2 \times Precision \times Recall}{Precision Recall}F1PrecisionRecall2×Precision×Recall​含义精确率和召回率的调和平均。为什么用调和平均而不用算术平均调和平均对极端值更敏感——一个指标很低就会把 F1 拉下来不像算术平均可以一个 0.99 一个 0.01 算出 0.50看起来还行。Precision0.99, Recall0.01 算术平均 0.50 (看着还行实际很烂) 调和平均 0.02 (真实反映了问题)6. Fβ-Score带权重的 F 值Fβ(1β2)×Precision×Recallβ2×PrecisionRecallF_\beta \frac{(1\beta^2) \times Precision \times Recall}{\beta^2 \times Precision Recall}Fβ​β2×PrecisionRecall(1β2)×Precision×Recall​β 值侧重场景β1精确率和召回率同等重要通用β2召回率重要性是精确率的 2 倍癌症筛查、欺诈检测β0.5精确率重要性是召回率的 2 倍垃圾邮件过滤fromsklearn.metricsimportf1_score,fbeta_score f1f1_score(y_test,y_pred)f2fbeta_score(y_test,y_pred,beta2)# 更重召回f05fbeta_score(y_test,y_pred,beta0.5)# 更重精确四、基于概率排序的指标前面那些指标都基于硬判断0 或 1但模型输出的是概率。概率本身的质量也很重要。1. ROC 曲线把阈值从 1.0 慢慢降到 0.0每个阈值都有一组 (FPR, TPR)连起来就是 ROC 曲线。TPR (真正率即召回率) 1.0 | ___________ | / | / ← 曲线越贴近左上角越好 | / | / | / ← 对角线 随机猜测 0.0 ────────────────── FPR (假正率) 0.0 1.0好模型的曲线贴近左上角随机模型是对角线比随机还差的模型在左下角反转一下就能用2. AUCArea Under ROC CurveROC 曲线下的面积取值 [0, 1]。AUC解读0.5跟瞎猜没区别0.6很弱0.7还行0.8不错0.9很好1.0完美大概率过拟合了AUC 的概率含义随机取一个正样本和一个负样本模型给正样本的分数高于负样本的概率就是 AUC。fromsklearn.metricsimportroc_auc_score,roc_curve y_probmodel.predict_proba(X_test)[:,1]aucroc_auc_score(y_test,y_prob)fpr,tpr,thresholdsroc_curve(y_test,y_prob)importmatplotlib.pyplotasplt plt.plot(fpr,tpr,labelfAUC{auc:.3f})plt.plot([0,1],[0,1],--,colorgray)plt.xlabel(FPR)plt.ylabel(TPR)plt.legend()plt.show()AUC 的优点不依赖特定阈值衡量的是模型整体的排序能力。AUC 的盲区当正例极少时如 0.5%AUC 可能看起来很高0.95但实际_precision_可能只有 10%——因为 FPR 稍微上升一点就意味着大量误报。3. PR 曲线与 AP当正例极度稀少时PR 曲线比 ROC 更有参考价值。Precision 1.0 |\ | \ | \____ | \______ | \____ 0.0 ────────────────── Recall 0.0 1.0APAverage PrecisionPR 曲线下面积。fromsklearn.metricsimportaverage_precision_score,precision_recall_curve apaverage_precision_score(y_test,y_prob)precision,recall,_precision_recall_curve(y_test,y_prob)plt.plot(recall,precision,labelfAP{ap:.3f})plt.xlabel(Recall)plt.ylabel(Precision)plt.legend()plt.show()为什么极度不平衡时看 PR因为 ROC 的横轴 FPR 即使从 0.01 涨到 0.02在 99% 负例的基数下误报数量也翻倍了——但曲线看起来几乎没变。PR 曲线对这种变化敏感得多。4. 对数损失Log LossLogLoss−1n∑i1n[yilog⁡(pi)(1−yi)log⁡(1−pi)]\text{LogLoss} -\frac{1}{n}\sum_{i1}^{n}[y_i \log(p_i) (1-y_i)\log(1-p_i)]LogLoss−n1​i1∑n​[yi​log(pi​)(1−yi​)log(1−pi​)]衡量概率预测的靠谱程度——不只看排没排对还看给的概率是否准确。情况预测概率实际Log Loss很确信且对了0.9910.01对了但犹豫0.5110.67很确信但错了0.9904.60犹豫且错了0.5100.67关键确信但判错的惩罚 犹豫但判错。所以 Log Loss 驱动模型不仅要判对还要有把握才下结论。fromsklearn.metricsimportlog_loss lllog_loss(y_test,y_prob)五、多分类场景的评估宏观 vs 微观 vs 加权三种聚合方式把每个类的指标合成一个全局指标方式做法特点macro每个类分别算再简单平均大类小类权重相同对小类表现敏感weighted按各类样本量加权平均大类影响更大micro把所有类的 TP/FP/FN 合在一起算等同于整体准确率多分类时fromsklearn.metricsimportclassification_reportprint(classification_report(y_test,y_pred,target_names[体育,财经,娱乐,科技]))输出示例precision recall f1-score support 体育 0.92 0.89 0.90 200 财经 0.88 0.85 0.86 180 娱乐 0.80 0.82 0.81 150 科技 0.85 0.90 0.87 170 accuracy 0.87 700 macro avg 0.86 0.87 0.86 700 weighted avg 0.87 0.87 0.87 700macro avg四个类的 F1 简单平均 (0.900.860.810.87)/4weighted avg按各类数量加权 → 大类的表现权重更高多分类 AUC方式思路One-vs-Rest (OvR)每个类 vs 其余所有类各算一次 AUC再平均One-vs-One (OvO)每对类别算一次 AUC再平均fromsklearn.metricsimportroc_auc_score# OvR 方式auc_ovrroc_auc_score(y_test,y_prob,multi_classovr)# OvO 方式auc_ovoroc_auc_score(y_test,y_prob,multi_classovo)六、阈值的选择模型输出概率默认用 0.5 做阈值——但 0.5 未必是最优的。方法 1按业务代价选择漏判一个欺诈的平均损失 5000 元 误报一笔正常的平均成本 50 元 代价比 100:1 → 阈值应该调得很低如 0.1宁可多报方法 2F1 最优阈值fromsklearn.metricsimportf1_score best_f1,best_thr0,0.5forthrinnp.arange(0.1,0.9,0.01):y_pred_thr(y_probthr).astype(int)f1f1_score(y_test,y_pred_thr)iff1best_f1:best_f1,best_thrf1,thrprint(f最优阈值:{best_thr:.2f}, F1:{best_f1:.4f})方法 3Youden’s J 统计量JTPR−FPRJ TPR - FPRJTPR−FPR选让 J 最大的阈值即 ROC 曲线上离对角线最远的点。fpr,tpr,thresholdsroc_curve(y_test,y_prob)j_scorestpr-fpr best_idxnp.argmax(j_scores)best_thresholdthresholds[best_idx]七、交叉验证让评估更可靠单次 train/test 划分有偶然性交叉验证用多轮评估取平均结果更稳。K 折交叉验证原始数据分成 5 份 第1轮[测试][训练][训练][训练][训练] → 算 AUC₁ 第2轮[训练][测试][训练][训练][训练] → 算 AUC₂ 第3轮[训练][训练][测试][训练][训练] → 算 AUC₃ 第4轮[训练][训练][训练][测试][训练] → 算 AUC₄ 第5轮[训练][训练][训练][训练][测试] → 算 AUC₅ 最终 AUC mean(AUC₁..₅) ± std(AUC₁..₅)fromsklearn.model_selectionimportcross_val_score scorescross_val_score(model,X,y,cv5,scoringroc_auc)print(fAUC:{scores.mean():.4f}±{scores.std():.4f})分层 K 折StratifiedKFold每折内正负比例一致不平衡数据必用。fromsklearn.model_selectionimportStratifiedKFold cvStratifiedKFold(n_splits5,shuffleTrue,random_state42)scorescross_val_score(model,X,y,cvcv,scoringroc_auc)八、指标选择指南按数据特征选数据特征首选指标辅助指标类别平衡AccuracyF1, AUC类别不平衡AUC F1AP, Recall目标Precision极度稀疏正例 1%APF2, PR 曲线多分类macro-F1混淆矩阵 各类 F1按业务目标选业务目标首选指标原因漏判代价高欺诈、癌症Recall 固定 Precision控制误报上限最大化召回误报代价高垃圾邮件、封号Precision 固定 Recall控制漏判上限最大化精确排序场景推荐、搜索AUC / AP只关心排序对不对概率需要准确保险定价Log Loss / Brier Score概率校准很重要通用、不确定F1 AUC 一起看互补视角九、完整评估代码模板importnumpyasnpimportmatplotlib.pyplotaspltfromsklearn.metricsimport(classification_report,confusion_matrix,roc_auc_score,roc_curve,average_precision_score,precision_recall_curve,log_loss,fbeta_score)fromsklearn.model_selectionimportcross_val_score,StratifiedKFolddefevaluate_model(model,X_test,y_test,model_nameModel):分类模型全面评估y_probmodel.predict_proba(X_test)[:,1]y_pred(y_prob0.5).astype(int)# 1. 基础指标print(f\n{*50})print(f{model_name}评估报告)print(f{*50})print(classification_report(y_test,y_pred,digits4))# 2. 关键单值指标aucroc_auc_score(y_test,y_prob)apaverage_precision_score(y_test,y_prob)lllog_loss(y_test,y_prob)f2fbeta_score(y_test,y_pred,beta2)print(fAUC:{auc:.4f})print(fAP:{ap:.4f})print(fLog Loss:{ll:.4f})print(fF2:{f2:.4f})# 3. 混淆矩阵cmconfusion_matrix(y_test,y_pred)print(f\n混淆矩阵:\n{cm})# 4. 绘图fig,axesplt.subplots(1,2,figsize(12,5))# ROC 曲线fpr,tpr,_roc_curve(y_test,y_prob)axes[0].plot(fpr,tpr,labelfAUC{auc:.3f})axes[0].plot([0,1],[0,1],--,colorgray)axes[0].set_xlabel(FPR)axes[0].set_ylabel(TPR)axes[0].set_title(ROC Curve)axes[0].legend()# PR 曲线prec,rec,_precision_recall_curve(y_test,y_prob)axes[1].plot(rec,prec,labelfAP{ap:.3f})axes[1].set_xlabel(Recall)axes[1].set_ylabel(Precision)axes[1].set_title(PR Curve)axes[1].legend()plt.tight_layout()plt.show()return{auc:auc,ap:ap,log_loss:ll,f2:f2}# 使用resultsevaluate_model(rf_model,X_test,y_test,随机森林)十、常见坑与避坑坑现象解法只看 Accuracy不平衡数据下 99% 准确率但模型没用必须同时看 F1/AUC用测试集调参测试集上效果很好上线后崩了留出验证集调参测试集只看一次阈值永远是 0.5业务需要高召回但用了 0.5 阈值根据业务代价调阈值不平衡数据不看 PRAUC0.95 但 Precision 只有 5%正例稀少时务必看 PR 曲线和 AP交叉验证不分层某折里几乎没有正例指标不稳定用StratifiedKFold多分类只看 macro微小类别表现很差被掩盖混淆矩阵逐类检查不看概率校准预测概率 0.7 但实际正例率只有 0.4画校准曲线calibration curve最后一句话没有万能指标。选什么指标取决于你的业务更怕哪种错误。先想清楚漏判和误报哪个代价更大再选指标和调阈值比无脑追 AUC 有效得多。