别再只盯着准确率了!用Python实战带你搞懂精准率、召回率和F1值(附代码)
别再只盯着准确率了!用Python实战带你搞懂精准率、召回率和F1值(附代码)
当你第一次接触机器学习分类问题时,准确率(Accuracy)往往是最直观的评估指标。但真实世界的数据往往并不像教科书上的示例那样完美平衡。想象一下,你正在开发一个信用卡欺诈检测系统,欺诈交易可能只占总交易的0.1%——即使你把所有交易都预测为"正常",也能获得99.9%的准确率,但这显然毫无意义。
这就是为什么在实战中,我们需要更精细的评估指标:精准率(Precision)、召回率(Recall)和F1值。这些指标能帮助我们更全面地评估模型在不同场景下的表现,特别是在处理类别不平衡的数据时。本文将用一个真实的信用卡欺诈检测数据集,带你从代码层面理解这些关键指标。
1. 环境准备与数据探索
首先确保你已经安装了必要的Python库:
!pip install numpy pandas matplotlib seaborn scikit-learn
我们将使用Kaggle上的信用卡欺诈检测数据集。这个数据集包含2013年9月欧洲持卡人的交易记录,其中492笔是欺诈交易,占所有284,807笔交易的0.172%——典型的极度不平衡数据集。
import pandas as pd
from sklearn.model_selection import train_test_split
# 加载数据
data = pd.read_csv('creditcard.csv')
# 查看类别分布
print(data['Class'].value_counts())
print(f"欺诈交易占比: {data['Class'].mean():.4%}")
# 特征与标签分离
X = data.drop('Class', axis=1)
y = data['Class']
# 分割训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
数据探索时,我们特别关注几个关键点:
- 特征标准化:除了Amount和Time,其他特征(V1-V28)都是PCA降维后的结果,已经标准化
- 类别不平衡:欺诈交易仅占0.172%,这会影响我们后续的模型评估
- 数据泄露风险:确保测试集与训练集完全隔离,特别是时间序列数据
2. 理解混淆矩阵与基础指标
在深入精准率和召回率之前,我们需要先理解它们的源头——混淆矩阵(Confusion Matrix)。混淆矩阵是分类问题中最基础也最重要的评估工具。
让我们用逻辑回归模型生成第一个预测,并绘制混淆矩阵:
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
# 训练简单逻辑回归模型
model = LogisticRegression(max_iter=1000)
model.fit(X_train, y_train)
# 预测并生成混淆矩阵
y_pred = model.predict(X_test)
cm = confusion_matrix(y_test, y_pred)
disp = ConfusionMatrixDisplay(confusion_matrix=cm)
disp.plot()
混淆矩阵中的四个关键数字:
| 预测\实际 | 负类(0) | 正类(1) |
|---|---|---|
| 负类(0) | TN | FN |
| 正类(1) | FP | TP |
- TP(True Positive):正确预测的正例(实际是欺诈且预测为欺诈)
- FP(False Positive):错误预测的正例(实际正常但预测为欺诈)
- TN(True Negative):正确预测的负例(实际正常且预测为正常)
- FN(False Negative):错误预测的负例(实际是欺诈但预测为正常)
从这四个基础数字,我们可以推导出所有重要指标:
from sklearn.metrics import precision_score, recall_score, f1_score
# 计算各项指标
accuracy = (cm[0,0] + cm[1,1]) / cm.sum()
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
print(f"准确率: {accuracy:.4f}")
print(f"精准率: {precision:.4f}")
print(f"召回率: {recall:.4f}")
print(f"F1值: {f1:.4f}")
3. 精准率 vs 召回率:业务场景决定侧重点
精准率和召回率往往是一对矛盾指标,理解它们的区别和适用场景至关重要。
3.1 精准率(Precision):预测正例的可靠性
精准率的定义是:在所有预测为正例的样本中,真正为正例的比例。
精准率 = TP / (TP + FP)
高精准率意味着当模型预测为正例时,这个预测很可能是正确的。适合以下场景:
- 垃圾邮件检测:宁可漏掉一些垃圾邮件,也尽量不要把正常邮件误判为垃圾邮件
- 推荐系统:推荐给用户的内容必须高度相关,宁愿少推荐也不要推荐不相关内容
3.2 召回率(Recall):找出真实正例的能力
召回率的定义是:在所有真实正例中,被正确预测为正例的比例。
召回率 = TP / (TP + FN)
高召回率意味着模型能够找出大部分真实正例。适合以下场景:
- 疾病诊断:宁可误诊一些健康人,也尽量不要漏诊真正患病的病人
- 欺诈检测:尽可能识别出所有欺诈交易,即使这意味着一些正常交易会被错误标记
3.3 实际应用中的权衡
在我们的信用卡欺诈检测案例中,默认模型可能表现出:
精准率: 0.75
召回率: 0.60
这意味着:
- 当模型预测某笔交易是欺诈时,有75%的概率确实是欺诈
- 但模型只能检测出60%的真实欺诈交易
我们可以通过调整分类阈值来平衡这两个指标:
from sklearn.metrics import precision_recall_curve
# 获取预测概率而非硬分类
y_scores = model.predict_proba(X_test)[:, 1]
# 计算不同阈值下的精准率和召回率
precisions, recalls, thresholds = precision_recall_curve(y_test, y_scores)
# 绘制PR曲线
plt.plot(thresholds, precisions[:-1], label="精准率")
plt.plot(thresholds, recalls[:-1], label="召回率")
plt.xlabel("阈值")
plt.legend()
plt.show()
通过PR曲线,我们可以直观地看到随着阈值变化,精准率和召回率如何此消彼长。业务需求决定了我们应该选择哪个平衡点。
4. F1值:精准率与召回率的调和平均
当我们需要同时考虑精准率和召回率时,F1值提供了一个综合指标。它是精准率和召回率的调和平均数:
F1 = 2 × (精准率 × 召回率) / (精准率 + 召回率)
调和平均数比简单算术平均数更重视较小值。这意味着只有当精准率和召回率都较高时,F1值才会高。
计算F1值的几种方法:
# 方法1:直接使用sklearn
f1 = f1_score(y_test, y_pred)
# 方法2:手动计算
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1_manual = 2 * (precision * recall) / (precision + recall)
# 验证两者一致
assert abs(f1 - f1_manual) < 1e-6
F1值特别适合以下场景:
- 类别不平衡的数据集
- 需要同时考虑假阳性(FP)和假阴性(FN)代价的情况
- 没有明确偏向精准率或召回率的业务需求
5. ROC曲线与AUC:全面评估模型性能
除了精准率和召回率,ROC曲线和AUC也是评估分类模型的重要工具。
5.1 ROC曲线绘制
ROC曲线描绘了在不同阈值下,真阳性率(TPR,即召回率)与假阳性率(FPR)的关系。
from sklearn.metrics import roc_curve, roc_auc_score
# 计算FPR和TPR
fpr, tpr, thresholds = roc_curve(y_test, y_scores)
# 绘制ROC曲线
plt.plot(fpr, tpr)
plt.plot([0, 1], [0, 1], 'k--') # 随机猜测的对角线
plt.xlabel('假阳性率(FPR)')
plt.ylabel('真阳性率(TPR)')
plt.title('ROC曲线')
plt.show()
5.2 AUC值解读
AUC(Area Under Curve)是ROC曲线下的面积,提供了模型整体性能的单一指标。
auc = roc_auc_score(y_test, y_scores)
print(f"AUC值: {auc:.4f}")
AUC值的解释:
- 0.5:模型不比随机猜测好
- 0.7-0.8:可以接受
- 0.8-0.9:优秀
-
0.9:极好
对于我们的信用卡欺诈检测模型,AUC值通常比准确率更能反映模型的真实性能,特别是在极度不平衡的数据集上。
6. 实际应用技巧与陷阱
在实战中应用这些指标时,有几个关键注意事项:
6.1 类别不平衡的处理
面对极度不平衡的数据,我们可以考虑:
-
重采样技术:
from imblearn.over_sampling import SMOTE smote = SMOTE(random_state=42) X_res, y_res = smote.fit_resample(X_train, y_train) -
类别权重调整:
model = LogisticRegression(class_weight='balanced') -
使用更适合的评估指标:如F1值、AUC而非准确率
6.2 多分类问题的扩展
对于多分类问题,这些指标有两种主要计算方式:
- 宏平均(Macro-average):计算每个类别的指标后取平均
- 微平均(Micro-average):汇总所有类别的TP/FP/FN/TN后计算
from sklearn.metrics import precision_score
# 宏平均
precision_macro = precision_score(y_test, y_pred, average='macro')
# 微平均
precision_micro = precision_score(y_test, y_pred, average='micro')
6.3 阈值优化的实用方法
寻找最佳分类阈值的几种策略:
- 基于业务成本:如果假阳性和假阴性的成本已知,可以最小化总成本
- Youden's J统计量:最大化TPR - FPR
youden_j = tpr - fpr optimal_idx = np.argmax(youden_j) optimal_threshold = thresholds[optimal_idx] - 基于PR曲线:在精准率和召回率之间找到业务可接受的平衡点
7. 完整代码示例与实战建议
最后,让我们整合所有内容,提供一个完整的评估流程示例:
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import (accuracy_score, precision_score,
recall_score, f1_score,
roc_auc_score, confusion_matrix,
precision_recall_curve, roc_curve)
import matplotlib.pyplot as plt
# 1. 数据准备
data = pd.read_csv('creditcard.csv')
X = data.drop('Class', axis=1)
y = data['Class']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# 2. 模型训练(带类别权重)
model = LogisticRegression(class_weight='balanced', max_iter=1000)
model.fit(X_train, y_train)
# 3. 预测与评估
y_pred = model.predict(X_test)
y_scores = model.predict_proba(X_test)[:, 1]
# 计算各项指标
metrics = {
"准确率": accuracy_score(y_test, y_pred),
"精准率": precision_score(y_test, y_pred),
"召回率": recall_score(y_test, y_pred),
"F1值": f1_score(y_test, y_pred),
"AUC": roc_auc_score(y_test, y_scores)
}
# 打印指标
for name, value in metrics.items():
print(f"{name}: {value:.4f}")
# 绘制PR曲线
precisions, recalls, thresholds = precision_recall_curve(y_test, y_scores)
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(thresholds, precisions[:-1], label="精准率")
plt.plot(thresholds, recalls[:-1], label="召回率")
plt.xlabel("阈值")
plt.legend()
plt.title("PR曲线")
# 绘制ROC曲线
plt.subplot(1, 2, 2)
fpr, tpr, _ = roc_curve(y_test, y_scores)
plt.plot(fpr, tpr)
plt.plot([0, 1], [0, 1], 'k--')
plt.xlabel('假阳性率(FPR)')
plt.ylabel('真阳性率(TPR)')
plt.title('ROC曲线')
plt.tight_layout()
plt.show()
在实际项目中,我通常会遵循以下工作流程:
- 明确业务目标:确定是更看重精准率还是召回率
- 基准模型建立:使用简单模型(如逻辑回归)建立基准
- 全面评估:计算所有相关指标,绘制PR和ROC曲线
- 阈值优化:根据业务需求调整分类阈值
- 模型迭代:尝试更复杂的模型,持续监控指标变化
记住,没有"最好"的模型,只有最适合特定业务场景的模型。在信用卡欺诈检测中,我们可能更倾向于高召回率;而在垃圾邮件过滤中,高精准率可能更重要。理解这些指标背后的含义,才能在实际工作中做出明智的决策。
更多推荐



所有评论(0)