简介
P-R曲线,是指以查准率(亦称准确率)为纵轴、查全率(亦称召回率)为横轴画出的曲线,反映了查准率随查全率的变化趋势,在机器学习中常用于二分类模型的评价及选择。
相关概念
混淆矩阵
实际为负 | 实际为正 | |
---|---|---|
预测为负 | TN | FP |
预测为正 | FN | TP |
查准率(亦称准确率)
p r e c i s i o n = T P T P + F P precision = \frac{TP}{TP+FP} precision=TP+FPTP
查全率(亦称召回率)
r e c a l l = T P T P + F N recall = \frac{TP}{TP+FN} recall=TP+FNTP
绘制原理
在训练集上训练出二分类模型后我们将测试集中的数据输入模型,这时我们可以计算得到这些数据属于某个类别的概率,将这些预测概率从小到大排列,然后将分类阈值依次设为[0,1]区间中不同的概率值并计算这时的准确率和召回率,最后将这些准确率和召回率在二维坐标系中连起来就得到了ROC曲线
示例
为了说明计算细节,这里使用一个虚构的例子:假设数据集有两个特征,分别是length和width,取值都是整数,标签有两类,分别是0和1,并且类别1是正类(positive);假设我们在这个数据集上训练得到的树模型如下:
并且我们假设训练数据在叶节点的分布如下:
叶节点编号 | 类别为0的样本个数 | 类别为1的样本点个数 | 该叶节点属于类别0的概率 | 该叶节点属于类别1的概率 | 该叶节点预测类别 |
---|---|---|---|---|---|
1 | 900 | 100 | 0.9 | 0.1 | 0 |
2 | 600 | 200 | 0.75 | 0.25 | 0 |
3 | 50 | 950 | 0.05 | 0.95 | 1 |
其中
某 叶 节 点 属 于 类 别 1 的 概 率 = 落 在 该 节 点 的 真 实 类 别 为 1 的 样 本 数 落 在 该 节 点 的 总 样 本 数 某叶节点属于类别1的概率=\frac{落在该节点的真实类别为1的样本数}{落在该节点的总样本数} 某叶节点属于类别1的概率=落在该节点的总样本数落在该节点的真实类别为1的样本数
并且叶节点的预测类别为它的所属概率更大的那个类别
假设测试集为:
特征 | 测试数据1 | 测试数据2 | 测试数据3 | 测试数据4 | 测试数据5 | 测试数据6 | 测试数据7 | 测试数据8 | 测试数据9 | 测试数据10 |
---|---|---|---|---|---|---|---|---|---|---|
length | 2 | 15 | 6 | 1 | 7 | 11 | 5 | 9 | 10 | 12 |
width | 4 | 8 | 6 | 19 | 13 | 2 | 18 | 3 | 2 | 9 |
实际所属类别 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 |
按照我们决策树的划分,可以得到测试集属于正类(类别1)的概率分别为(例如测试数据1最终被分到了叶节点2,那么它属于类别1的概率就是叶节点2属于类别1的概率):
测试数据1 | 测试数据2 | 测试数据3 | 测试数据4 | 测试数据5 | 测试数据6 | 测试数据7 | 测试数据8 | 测试数据9 | 测试数据10 |
---|---|---|---|---|---|---|---|---|---|
0.25 | 0.1 | 0.25 | 0.95 | 0.95 | 0.25 | 0.95 | 0.1 | 0.1 | 0.1 |
将他们按概率大小排列得到:
测试数据 10 | 测试数据9 | 测试数据8 | 测试数据2 | 测试数据1 | 测试数据3 | 测试数据6 | 测试数据4 | 测试数据5 | 测试数据7 | |
---|---|---|---|---|---|---|---|---|---|---|
属于正类的概率 | 0.1 | 0.1 | 0.1 | 0.1 | 0.25 | 0.25 | 0.25 | 0.95 | 0.95 | 0.95 |
实际所属类别 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 |
现在我们将分类阈值分别设为0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,并将概率大于阈值的测试样本预测为类别1,可以得到不同分类阈值下的准确率和召回率:
分类阈值 | TP | FN | FP | 准确率 | 召回率 |
---|---|---|---|---|---|
0 | 5 | 0 | 5 | 0.5 | 1 |
0.1 | 4 | 1 | 2 | 0.67 | 0.8 |
0.2 | 4 | 1 | 2 | 0.67 | 0.8 |
0.3 | 3 | 2 | 0 | 1 | 0.6 |
0.4 | 3 | 2 | 0 | 1 | 0.6 |
0.5 | 3 | 2 | 0 | 1 | 0.6 |
0.6 | 3 | 2 | 0 | 1 | 0.6 |
0.7 | 3 | 2 | 0 | 1 | 0.6 |
0.8 | 3 | 2 | 0 | 1 | 0.6 |
0.9 | 3 | 2 | 0 | 1 | 0.6 |
将(召回率,准确率)在坐标中画出来就得到了P-R曲线:
可见我们虚构的模型表现不太理想
代码实现
自己写代码
我们以sklearn.datasets
中的乳腺癌数据集为例,这是一个二分类数据集,我们将在这个数据集上学习一个分类树模型并绘制该模型的P-R曲线:
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
import matplotlib.pyplot as plt
import numpy as np
data = load_breast_cancer()
X = data.data
y = data.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=66)
dtc = DecisionTreeClassifier(min_samples_leaf=10)
dtc.fit(X_train, y_train)
# predict_proba返回的是每个样本分别属于两个类别的概率,而我们只关心它属于正类的概率
probs = dtc.predict_proba(X_test)[:, 1]
# 就像在上面那个虚构的例子中展示的那样,
# 决策树在做预测时对每个样本会算出它属于两个类别的概率,
# 最终预测结果就是预测概率大于0.5的那个类
y_predict = dtc.predict(X_test)
# 人为地生成一系列不同的阈值
thresholds = np.arange(np.min(probs), np.max(probs), 0.1)
precisions = []
recalls = []
for threshold in thresholds:
y_predict = np.array(probs >= threshold, dtype='int')
pre = precision_score(y_test, y_predict)
recall = recall_score(y_test, y_predict)
precisions.append(pre)
recalls.append(recall)
plt.plot(recalls, precisions)
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.show()
输出:
调用sklearnAPI
sklearn已经封装了绘制P-R曲线的函数,使用时只要导入sklearn.metrics.precision_recall_curve
就可以了。该函数参数如下:
y_true
:真实的类标签,默认在 {-1, 1} 或 {0, 1} 中取值pos_label
:正类的标签,默认为1probas_pred
:样本的预测概率sample_weight
:样本权重
函数返回三个列表:
precision
:不同概率阈值对应的预测准确率recall
:不同的概率阈值对应的预测召回率thresholds
:绘制P-R曲线时取的不同的概率阈值
我们还是以sklearn.datasets
中的乳腺癌数据集为例,在这个数据集上建立一个逻辑回归模型,并绘制该模型的P-R曲线:
from sklearn.metrics import precision_recall_curve
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
import matplotlib.pyplot as plt
data = load_breast_cancer()
X = data.data
y = data.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=66)
model = LogisticRegression(solver='liblinear')
model.fit(X_train, y_train)
scores = model.decision_function(X_test)
precisions, recalls, thresholds = precision_recall_curve(y_test, scores)
plt.plot(precisions, recalls)
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.show()
输出: