Skip to content

分类指标

在机器学习中,训练出一个分类模型只是第一步。

就像医生不能仅凭感觉判断病情,我们也需要一套科学的体检指标来评估模型的健康状况。

这些指标就是分类指标,它们能告诉我们模型预测得有多准、哪里做得好、哪里还有不足。本文将系统学习这些至关重要的评估工具。


为什么需要分类指标?

一个模型"看起来很准"并不代表它有用,我们需要从多个角度量化它的表现。

想象一下,你训练了一个模型来识别邮件是否为垃圾邮件,模型对 100 封邮件做出了预测,你可能会问:

  • "它预测对了多少封?" —— 这引出了准确率
  • "在真正的垃圾邮件中,它找出了多少?" —— 这引出了召回率
  • "它说是垃圾邮件的,有多少真的是垃圾?" —— 这引出了精确率

如果只用"对了多少"来评判,就像只用考试总分评价学生,会忽略很多重要信息。

不同的业务场景,关注的重点完全不同:

业务场景最怕发生优先追求可以适当牺牲
疾病诊断漏掉病人(FN)高召回率一些精确率
垃圾邮件过滤误删重要邮件(FP)高精确率一些召回率

因此,我们需要一整套指标,从不同角度全面评估模型性能。


核心概念:混淆矩阵

几乎所有分类指标都源于一个强大的工具 —— 混淆矩阵(Confusion Matrix),它是理解模型预测结果的"全景地图"。

什么是混淆矩阵

它是一个表格,展示了模型预测结果与真实标签之间的所有四种可能情况。

实例

python
# 一个混淆矩阵的示例(以二分类"是/否垃圾邮件"为例)

from sklearn.metrics import confusion_matrix

# 1 代表垃圾邮件(如 "RUNOOB 限时优惠" 这类推广邮件),0 代表正常邮件

y_true = [1, 0, 1, 1, 0, 0, 1, 0, 0, 1]  # 真实标签

y_pred = [1, 0, 0, 1, 0, 0, 1, 1, 0, 1]  # 模型的预测结果

# 计算混淆矩阵

# 返回的矩阵中:行表示真实类别,列表示预测类别

cm = confusion_matrix(y_true, y_pred)

print("混淆矩阵:")

print(cm)
python
混淆矩阵:
[[4 1]
 [1 4]]

矩阵中的 4 个数字分别对应四种情况。为了更直观地理解,我们把它可视化:

预测:垃圾邮件

预测:正常邮件

真实
垃圾邮件

4

TP 真正例

正确识别出的垃圾邮件

1

FN 假负例

被漏掉的垃圾邮件(Type II Error)

真实
正常邮件

1

FP 假正例

被误判的正常邮件(Type I Error)

4

TN 真负例

正确放行的正常邮件

对角线(TP 与 TN)是预测正确的样本,非对角线(FP 与 FN)是预测错误的样本。

让我们拆解这四个核心术语:

术语缩写含义在垃圾邮件例子中的解释
真正例TP模型预测为,真实也是模型正确识别出的垃圾邮件
假正例FP模型预测为,但真实是模型误判为垃圾邮件的正常邮件(Type I Error)。
真负例TN模型预测为,真实也是模型正确识别出的正常邮件
假负例FN模型预测为,但真实是模型漏掉垃圾邮件(Type II Error)。

记忆技巧真 / 假看的是预测是否正确正 / 负看的是模型的预测结果


核心分类指标详解

有了混淆矩阵,各种评估指标就有了统一的计算基础,下面逐一介绍最常用的四个指标。

准确率 - 最直观的指标

准确率(Accuracy)衡量模型预测正确的样本占总样本的比例。

实例

python
from sklearn.metrics import accuracy_score

accuracy = accuracy_score(y_true, y_pred)

print(f"准确率: {accuracy:.2f}")
python
准确率: 0.80

优点:非常直观,易于理解。

常见陷阱数据不平衡时准确率会严重失真。如果 99% 的邮件都是正常邮件,一个把所有邮件都预测为正常的"笨模型",准确率也能高达 99%,但它一封垃圾邮件都抓不到。

精确率 - "宁缺毋滥"的指标

精确率(Precision)关注模型预测出的正例中有多少是真正的正例,衡量预测结果的可靠性

它回答的问题:在我们预测为垃圾邮件的邮件中,有多少真的是垃圾邮件?

高精确率意味着:模型说"这是垃圾邮件"时,可信度很高。

实例

python
from sklearn.metrics import precision_score

precision = precision_score(y_true, y_pred)

print(f"精确率: {precision:.2f}")
python
精确率: 0.80

召回率 - "宁可错杀"的指标

召回率(Recall)关注所有真实的正例中被模型找出了多少,衡量模型发现正例的能力

它回答的问题:在所有真正的垃圾邮件中,我们找出了多少?

高召回率意味着:模型很少漏掉真正的垃圾邮件。

实例

python
from sklearn.metrics import recall_score

recall = recall_score(y_true, y_pred)

print(f"召回率: {recall:.2f}")
python
召回率: 0.80

F1 分数 - 精确率与召回率的调和平均

精确率和召回率通常相互矛盾,提高一个,另一个往往会降低。

F1 分数是它们的调和平均数,旨在找到一个平衡点。

调和平均的特点:它更倾向于惩罚极端值,只有当精确率和召回率都较高时,F1 分数才会高。

实例

python
from sklearn.metrics import f1_score

f1 = f1_score(y_true, y_pred)

print(f"F1分数: {f1:.2f}")
python
F1分数: 0.80

指标对比与选择指南

四个指标各有侧重,选错指标可能让模型优化方向与业务目标背道而驰:

指标公式关注点适用场景举例
准确率(TP+TN)/总数整体预测正确率类别均衡,且 FP 和 FN 代价相似的场景。
精确率TP/(TP+FP)预测为正的样本的准确性FP 代价高:如垃圾邮件过滤(怕误删重要邮件)、推荐系统(怕推荐劣质商品)。
召回率TP/(TP+FN)真实为正的样本被找出的比例FN 代价高:如疾病筛查(怕漏诊)、欺诈检测(怕漏掉欺诈交易)。
F1 分数2PR/(P+R)精确率与召回率的平衡需要综合考量,没有明确偏向的场景;类别不平衡时比准确率更好。

进阶指标:ROC 曲线与 AUC

当模型的预测结果是一个概率值(例如,某邮件是垃圾邮件的概率为 0.8)时,我们需要设定一个阈值(如 0.5)来决定最终分类。

ROC 曲线帮助我们评估模型在不同阈值下的整体性能。

真正率与假正率

ROC 曲线的两个坐标轴由这两个指标构成:

  • 真正率(TPR):其实就是召回率
  • 假正率(FPR):所有真实负例中,被错误预测为正例的比例。

ROC 曲线

ROC 曲线以 FPR 为横轴TPR 为纵轴,曲线上的每一个点都对应一个特定的分类阈值。

  • 理想点:左上角 (0, 1),即 FPR=0(没有误报),TPR=1(全部召回)。
  • 随机线:从 (0,0) 到 (1,1) 的对角线,代表一个随机猜测模型的性能。

下图画出了本文示例数据对应的 ROC 曲线:

模型把 3 封真正的垃圾邮件全部排在最前面,曲线紧贴左上角,阴影区域即 ROC 曲线下的面积(AUC = 1.00)。

AUC 值

AUC(Area Under Curve)是 ROC 曲线下的面积,取值范围在 0 到 1 之间:

AUC 取值含义
AUC = 1完美模型。
0.5 < AUC < 1模型具有一定的预测能力,值越大越好。
AUC = 0.5模型没有区分能力,等同于随机猜测。
AUC < 0.5模型比随机猜测还差,通常意味着预测方向反了。

AUC 的优势在于它对类别不平衡不敏感,并且评估的是模型整体的排序能力(将正样本排在负样本前面的能力)。

实例

python
from sklearn.metrics import roc_curve, auc

# 假设我们有一些预测概率(1 代表垃圾邮件,0 代表正常邮件)

y_true = [1, 0, 1, 0, 1]

y_scores = [0.9, 0.4, 0.6, 0.3, 0.8]  # 模型预测为正例的概率

# 计算不同阈值下的 FPR 与 TPR

fpr, tpr, thresholds = roc_curve(y_true, y_scores)

# AUC 为 ROC 曲线下的面积

roc_auc = auc(fpr, tpr)

print(f"AUC 值: {roc_auc:.2f}")

# 绘制 ROC 曲线(可选,需要 matplotlib)

import matplotlib.pyplot as plt

plt.figure()

plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (area = {roc_auc:.2f})')

plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--', label='Random Guess')

plt.xlim([0.0, 1.0])

plt.ylim([0.0, 1.05])

plt.xlabel('False Positive Rate')

plt.ylabel('True Positive Rate')

plt.title('Receiver Operating Characteristic (ROC) Curve')

plt.legend(loc="lower right")

plt.show()

输出:

python
AUC 值: 1.00

多分类问题的指标

当类别超过两个时(如识别猫、狗、兔子),上述指标可以通过"平均"的方式扩展到多分类。

平均方式计算方法特点
宏平均(macro)先计算每个类别的指标(如精确率),再对所有类别的指标取算术平均。平等看待每个类别,小类别的表现会明显影响结果。
微平均(micro)先汇总所有类别的 TP、FP 等,再用汇总后的值计算一个全局指标。平等看待每个样本,受大类别影响更大。

在 Scikit-learn 中,可以通过 average 参数指定:

实例

python
from sklearn.metrics import precision_score

# 三分类示例:0 = 猫,1 = 狗,2 = 兔子

y_true = [0, 1, 2, 0, 1, 2, 0, 1, 2]  # 真实标签

y_pred = [0, 2, 2, 0, 1, 1, 0, 0, 2]  # 模型的预测结果

precision_macro = precision_score(y_true, y_pred, average='macro')  # 宏平均:先算每个类别,再取平均

precision_micro = precision_score(y_true, y_pred, average='micro')  # 微平均:先汇总 TP/FP,再算全局指标

print(f"宏平均精确率: {precision_macro:.2f}")

print(f"微平均精确率: {precision_micro:.2f}")
python
宏平均精确率: 0.64
微平均精确率: 0.67

宏平均(0.64)低于微平均(0.67),说明模型在样本较少的类别上表现更弱,这正是宏平均对类别更敏感的体现。


实践练习:综合评估一个分类模型

现在,让我们用著名的鸢尾花数据集,完整地评估一个分类模型。

实例

python
from sklearn.datasets import load_iris

from sklearn.model_selection import train_test_split

from sklearn.linear_model import LogisticRegression

from sklearn.metrics import classification_report, confusion_matrix, accuracy_score, roc_auc_score

# 1. 加载数据

iris = load_iris()

X = iris.data

y = iris.target

target_names = iris.target_names

# 2. 划分训练集和测试集

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 3. 训练一个简单的逻辑回归模型

model = LogisticRegression(max_iter=200)

model.fit(X_train, y_train)

# 4. 在测试集上进行预测

y_pred = model.predict(X_test)

y_pred_proba = model.predict_proba(X_test)  # 获取预测概率,用于 AUC

# 5. 计算并打印各种指标

print("=== 混淆矩阵 ===")

# 多分类的混淆矩阵是 N x N 的

print(confusion_matrix(y_test, y_pred))

print("\n=== 分类报告(包含精确率、召回率、F1)===")

# classification_report 是一个非常方便的函数,一次性输出多个指标

print(classification_report(y_test, y_pred, target_names=target_names))

print(f"\n=== 准确率 ===")

print(f"{accuracy_score(y_test, y_pred):.4f}")

# 6. 多分类的 AUC,通常计算每个类别相对于其他类别的"一对多" AUC,然后取平均

# 注意:roc_auc_score 在多分类时需要指定 multi_class='ovr' (One-vs-Rest)

try:

    auc_ovr = roc_auc_score(y_test, y_pred_proba, multi_class='ovr', average='macro')

    print(f"\n=== 宏平均 AUC (OvR) ===")

    print(f"{auc_ovr:.4f}")

except Exception as e:

    print(f"\n计算 AUC 时出错(可能某些类别在测试集中未出现): {e}")
python
=== 混淆矩阵 ===
[[19  0  0]
 [ 0 13  0]
 [ 0  0 13]]

=== 分类报告(包含精确率、召回率、F1)===
              precision    recall  f1-score   support

      setosa       1.00      1.00      1.00        19
  versicolor       1.00      1.00      1.00        13
   virginica       1.00      1.00      1.00        13

    accuracy                           1.00        45
   macro avg       1.00      1.00      1.00        45
weighted avg       1.00      1.00      1.00        45

=== 准确率 ===
1.0000

=== 宏平均 AUC (OvR) ===
1.0000

运行这段代码,你将看到一个完整的模型评估报告。

请尝试修改模型参数或换用不同的模型(如 sklearn.tree.DecisionTreeClassifier),观察这些指标如何变化。


注意事项与常见误区

最后,梳理几个初学者最容易踩的坑:

常见误区正确做法
类别不平衡时只看准确率同时关注 F1 分数AUC,它们对不平衡数据更稳健。
用单一阈值下的精确率、召回率评价模型整体能力结合 ROC 曲线与 AUC,评估模型在所有阈值下的排序能力。
正类标签不是 1 时直接调用评分函数通过 pos_label 参数显式指定正类标签,避免算错方向。

AI 思考中...

随机森林

无监督学习 – 聚类

基于 VitePress 构建,部署于 GitHub Pages