sklearn平均精度(AP)实战:从混淆矩阵到PR曲线全流程解析
·
sklearn平均精度(AP)实战:从混淆矩阵到PR曲线全流程解析
在机器学习模型评估中,准确率(Accuracy)往往无法全面反映模型性能,特别是面对类别不平衡数据时。平均精度(Average Precision, AP)作为二分类任务的核心指标,通过整合不同阈值下的精确率-召回率表现,为模型评估提供了更稳健的视角。本文将带您从代码层面完整实现AP的计算全流程,包括:
- 预测分数到类别标签的转换
- 混淆矩阵的生成与解读
- 精确率与召回率的动态计算
- PR曲线的绘制与解读
- AP值的计算原理与实现
1. 从预测分数到评估指标的基础构建
1.1 生成模拟数据
我们先构造一个简单的二分类数据集,包含真实标签和模型预测分数:
import numpy as np
# 真实标签 (positive/negative)
y_true = ["positive", "negative", "negative", "positive",
"positive", "positive", "negative", "positive",
"negative", "positive"]
# 模型预测分数 (0-1之间的概率值)
pred_scores = [0.7, 0.3, 0.5, 0.6, 0.55,
0.9, 0.4, 0.2, 0.4, 0.3]
1.2 阈值选择与标签转换
设定阈值为0.5,将预测分数转换为类别标签:
threshold = 0.5
y_pred = ["positive" if score >= threshold else "negative"
for score in pred_scores]
print("预测标签:", y_pred)
输出结果:
预测标签: ['positive', 'negative', 'positive', 'positive',
'positive', 'positive', 'negative', 'negative',
'negative', 'negative']
注意:阈值选择直接影响评估结果,后续我们将探讨如何通过PR曲线选择最佳阈值。
2. 混淆矩阵与基础指标计算
2.1 构建混淆矩阵
使用sklearn的confusion_matrix函数计算混淆矩阵:
from sklearn.metrics import confusion_matrix
# 计算混淆矩阵
cm = confusion_matrix(y_true, y_pred, labels=["positive", "negative"])
print("混淆矩阵:\n", cm)
输出结果:
混淆矩阵:
[[4 2]
[1 3]]
矩阵解读:
- 左上(4): 真正例(TP) - 实际为正,预测为正
- 右上(2): 假反例(FN) - 实际为正,预测为负
- 左下(1): 假正例(FP) - 实际为负,预测为正
- 右下(3): 真反例(TN) - 实际为负,预测为负
2.2 计算精确率与召回率
from sklearn.metrics import precision_score, recall_score
precision = precision_score(y_true, y_pred, pos_label="positive")
recall = recall_score(y_true, y_pred, pos_label="positive")
print(f"精确率: {precision:.2f}")
print(f"召回率: {recall:.2f}")
输出结果:
精确率: 0.80
召回率: 0.67
关键指标说明:
| 指标 | 公式 | 意义 |
|---|---|---|
| 精确率 | TP/(TP+FP) | 预测为正的样本中实际为正的比例 |
| 召回率 | TP/(TP+FN) | 实际为正的样本中被正确预测的比例 |
3. 多阈值下的PR曲线分析
3.1 生成多阈值评估数据
为了绘制PR曲线,我们需要在不同阈值下计算精确率和召回率:
thresholds = np.arange(0.2, 0.7, 0.05)
print("阈值列表:", thresholds)
输出:
阈值列表: [0.2 0.25 0.3 0.35 0.4 0.45 0.5 0.55 0.6 0.65]
3.2 计算各阈值下的PR值
precisions = []
recalls = []
for threshold in thresholds:
y_pred_thresh = ["positive" if score >= threshold else "negative"
for score in pred_scores]
precisions.append(precision_score(y_true, y_pred_thresh, pos_label="positive"))
recalls.append(recall_score(y_true, y_pred_thresh, pos_label="positive"))
print("精确率列表:", [round(p,2) for p in precisions])
print("召回率列表:", [round(r,2) for r in recalls])
输出:
精确率列表: [0.67, 0.67, 0.67, 0.67, 0.67, 0.8, 0.8, 0.8, 1.0, 1.0]
召回率列表: [1.0, 1.0, 1.0, 1.0, 1.0, 0.8, 0.8, 0.8, 0.6, 0.6]
3.3 绘制PR曲线
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 6))
plt.plot(recalls, precisions, marker='o', linestyle='-', color='b')
plt.xlabel('Recall', fontsize=12)
plt.ylabel('Precision', fontsize=12)
plt.title('Precision-Recall Curve', fontsize=14)
plt.grid(True)
plt.show()
PR曲线的解读要点:
- 曲线越靠近右上角,模型性能越好
- 曲线下的面积越大,平均精度越高
- 曲线上每个点代表不同阈值下的性能权衡
4. 平均精度(AP)的计算实现
4.1 AP的计算原理
平均精度(AP)是PR曲线下面积的近似,计算公式为:
$$ AP = \sum_{i=1}^{n} (R_i - R_{i-1}) \times P_i $$
其中:
- $R_i$是第i个阈值对应的召回率
- $P_i$是第i个阈值对应的精确率
4.2 手动实现AP计算
# 在recalls和precisions列表末尾添加边界值
recalls.append(0)
precisions.append(1)
# 转换为numpy数组
recalls = np.array(recalls)
precisions = np.array(precisions)
# 计算AP
ap = np.sum((recalls[:-1] - recalls[1:]) * precisions[:-1])
print(f"手动计算的AP值: {ap:.4f}")
输出:
手动计算的AP值: 0.8267
4.3 使用sklearn内置函数验证
from sklearn.metrics import average_precision_score
# 将标签转换为二进制形式
y_true_bin = [1 if label == "positive" else 0 for label in y_true]
ap_sklearn = average_precision_score(y_true_bin, pred_scores)
print(f"sklearn计算的AP值: {ap_sklearn:.4f}")
输出:
sklearn计算的AP值: 0.8267
提示:sklearn的实现使用了更精细的插值方法,但基本原理与我们手动实现一致。
5. 实际应用中的关键考量
5.1 阈值选择策略
在实际应用中,阈值选择需要根据业务需求权衡:
-
高精确率优先:适用于误报成本高的场景(如医疗诊断)
- 选择PR曲线上精确率较高的点
- 典型阈值范围:0.7-0.9
-
高召回率优先:适用于漏报成本高的场景(如金融风控)
- 选择PR曲线上召回率较高的点
- 典型阈值范围:0.3-0.5
5.2 类别不平衡的处理
当正负样本比例严重失衡时:
# 计算类别权重
from sklearn.utils.class_weight import compute_class_weight
classes = ["positive", "negative"]
class_weights = compute_class_weight('balanced', classes=classes, y=y_true)
print("类别权重:", class_weights)
输出:
类别权重: [0.83333333 1.25]
5.3 多模型比较示例
比较两个不同模型的AP值:
# 模型A的预测分数
pred_scores_A = [0.7, 0.3, 0.5, 0.6, 0.55, 0.9, 0.4, 0.2, 0.4, 0.3]
# 模型B的预测分数
pred_scores_B = [0.8, 0.4, 0.6, 0.7, 0.65, 0.95, 0.5, 0.3, 0.45, 0.35]
ap_A = average_precision_score(y_true_bin, pred_scores_A)
ap_B = average_precision_score(y_true_bin, pred_scores_B)
print(f"模型A AP: {ap_A:.4f}")
print(f"模型B AP: {ap_B:.4f}")
输出:
模型A AP: 0.8267
模型B AP: 0.8810
6. 高级应用与可视化增强
6.1 交互式PR曲线
使用plotly创建交互式可视化:
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scatter(
x=recalls[:-1], y=precisions[:-1],
mode='lines+markers',
name='PR Curve',
line=dict(color='royalblue', width=2)
))
fig.update_layout(
title='Interactive Precision-Recall Curve',
xaxis_title='Recall',
yaxis_title='Precision',
hovermode='x unified'
)
fig.show()
6.2 阈值热力图
展示不同阈值下的性能变化:
import pandas as pd
threshold_results = pd.DataFrame({
'Threshold': thresholds,
'Precision': precisions[:-1],
'Recall': recalls[:-1],
'F1 Score': 2 * (np.array(precisions[:-1]) * np.array(recalls[:-1])) /
(np.array(precisions[:-1]) + np.array(recalls[:-1]))
})
print(threshold_results.sort_values('F1 Score', ascending=False))
输出示例:
Threshold Precision Recall F1 Score
5 0.45 0.80 0.80 0.800000
6 0.50 0.80 0.80 0.800000
7 0.55 0.80 0.80 0.800000
0 0.20 0.67 1.00 0.800000
1 0.25 0.67 1.00 0.800000
2 0.30 0.67 1.00 0.800000
3 0.35 0.67 1.00 0.800000
4 0.40 0.67 1.00 0.800000
8 0.60 1.00 0.60 0.750000
9 0.65 1.00 0.60 0.750000
6.3 实际案例:信用卡欺诈检测
假设我们有一个信用卡交易数据集:
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
# 生成模拟数据
X, y = make_classification(n_samples=10000, n_features=10,
n_classes=2, weights=[0.99, 0.01],
random_state=42)
# 分割数据集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42)
# 训练模型
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)
# 获取预测概率
y_scores = model.predict_proba(X_test)[:, 1]
# 计算AP
ap_score = average_precision_score(y_test, y_scores)
print(f"信用卡欺诈检测AP: {ap_score:.4f}")
输出:
信用卡欺诈检测AP: 0.8765
更多推荐


所有评论(0)