机器学习 | 模型评估方法(P-R曲线和ROC曲线)

目录

一、P-R曲线

1. P-R曲线的基本概念

2. 计算公式及其含义

3. 绘制方法与代码

4. 具体应用与计算

5. P-R曲线的优缺点

6. 完整代码及运行结果

二、ROC曲线

1. ROC曲线的基本概念

2. 计算公式及其含义

3. AUC

4. 绘制方法与代码

5. 具体应用与计算

6. ROC曲线的优缺点

7. 完整代码及运行结果

三、实验中的问题


机器学习模型的性能评估是至关重要的一部分。在分类问题中,我们通常希望了解模型的预测能力,也就是评估该模型对正类别和负类别的分类结果。PR曲线和ROC曲线是用于评估二分类模型性能的常用工具,通过在不同阈值下的精确度、召回率和真正率、假正率之间的权衡,评估模型的性能。

一、P-R曲线

1. P-R曲线的基本概念

P-R曲线是一种用于评估二分类模型性能的图形工具。该曲线有两个重要指标:精确度(Precision)召回率(Recall)。精确度指模型在预测为正类别的样本中真正为正类别的比例。召回率指模型成功预测为正类别的样本占实际正类别的比例。P-R曲线以不同的阈值为基础,展示了精确度和召回率之间的关系。在P-R曲线中,横轴为Recall召回率,纵轴为Precision精确率。

P —— 精确度,R —— 召回率。

2. 计算公式及其含义

首先引入混淆矩阵的概念,通过统计真实标记和预测结果的组合得到混淆矩阵。

真实情况预测结果
正例反例
正例TP(真正例)FN(假反例)
反例FP(假正例)TN(真反例)

 

TP(true positive)     :真正例,预测结果为正例,真实结果为正例。

FP(false positive) :假正例,预测结果为正例,真实结果为反例。

TN(true negative) :真反例,预测结果为反例,真实结果为反例。

FN(false negative)  :假反例,预测结果为反例,真实结果为正例。

计算公式:

精确度 P:在模型预测为正类的所有样本中,有多少样本是真正的正类别。

P = \frac{TP}{TP + FP} 

  • TP + FP:预测结果为正类的所有样本数,包括预测正确的 + 预测错误的

召回率 R:模型成功预测为正类的样本 占 实际正类别的比例

F = \frac{TP}{TP + FN}

  • TP + FN:实际为正类的样本总数

补充:

  • TP + FP + FN + TN:样本总数
  • FP + TN:实际为负类的样本总数
  • TN + FN:预测结果为负类的所有样本数,包括预测正确的 + 预测错误的

P-R曲线:

如果一个机器的P-R曲线被另一个机器的P-R曲线完全包住,则后者的性能优于前者,例如上面的黑色曲线和黄色曲线所代表的机器优于蓝色曲线代表的机器。

通过平衡点可以判断黑色与黄色的机器性能,平衡点是 P=R 时的取值,如果这个值较大,则该机器的性能较好。 

3. 绘制方法与代码

(1)假设已经得出一系列样本被划分为正类的概率score值,按照大小排序
(2)从高到低,依次将“score”值作为阈值threshold,当测试样本属于正样本的概率大于或等于这个阈值时,我们认为它为正样本,否则为负样本。
(3)每次选取一个不同的阈值threshold,得到一组precision和recall,以recall值为横坐标、precision值为纵坐标,描点。
(4)根据(3)中的坐标点,绘制曲线。

主要代码:

# 模型的概率预测
y_scores = knn.predict_proba(X_test)[:, 1]

# 初始化空列表来存储P-R曲线上的点
precision_points = []
recall_points = []

# 遍历不同的阈值
thresholds = np.linspace(0, 1, 100)  # 生成100个阈值
for threshold in thresholds:
    y_pred = (y_scores >= threshold).astype(int)
    #print("y_pred:",y_pred)
    TP = np.sum((y_pred == 1) & (y_test == 1))
    FP = np.sum((y_pred == 1) & (y_test == 0))
    TN = np.sum((y_pred == 0) & (y_test == 0))
    FN = np.sum((y_pred == 0) & (y_test == 1))
    precision = TP / (TP + FP) if TP + FP > 0 else 1.0
    recall = TP / (TP + FN) if TP + FN > 0 else 1.0
    precision_points.append(precision)
    recall_points.append(recall)

print("TP:",TP)
print("FP:",FP)
print("TN:",TN)
print("FN:",FN)
print("R:",recall_points)
print("P:",precision_points)

# 绘制P-R曲线
plt.plot(recall_points, precision_points, marker='.')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.title('P-R Curve')
plt.grid(True)
plt.show()

(1)计算模型的概率预测值

(2)根据不同的阈值,将概率转化为二元分类结果

(3)对于每个阈值,计算精确度和召回率

(4)绘制P-R曲线,并可以选择进行插值和平滑处理。因为在实际应用中,观察的是连续的性能而不是离散的数据点,通过插值和平滑,可以更好地理解模型性能的整体趋势。

 

4. 具体应用与计算

假设测试集的样本数量为20,测试集的标签为 y_test(标签为1记为正类P,标签为0记为负类N),通过计算得到的模型概率预测为y_scores,当前阈值为0.5,当预测概率大于或等于0.5时为正样本(T),小于0.5时为负样本(F)。

集标签:y_test模型概率预测:y_scores阈值0.5时,概率是否>0.5
0(N)0.0F
1(P)1.0T
0(N)0.6T
1(P)0.8T
0(N)0.4F
1(P)1.0T
1(P)0.8T
0(N)0.0F
0(N)0.4F
1(P)1.0T
1(P)0.6T
1(P)0.4F
1(P)1.0T
0(N)0.2F
0(N)0.6T
1(P)0.4F
0(N)0.2F
1(P)0.4F
0(N)0.0F
0(N)0.0F

由表格可知(同一颜色代表同一类别)

TP 为真实结果是正类,且预测正确的个数,TP = 7

FP 为真实结果是负类,预测错误的个数,FP = 2

FN 为真实结果是正类,预测错误的个数,FN = 3

精确率 P = 7 / ( 7 + 2 ) =  0.7777777777777778

召回率 R = 7 / ( 7 + 3 ) = 0.7

5. P-R曲线的优缺点

优点

  1. 适用于不平衡数据集:P-R曲线更能反映在不平衡数据中模型性能的表现,更关注正类别的性能。

  2. 易于解释:P-R曲线的含义直观,精确度和召回率都是直接可理解的指标。

缺点

  1. 对阈值敏感:P-R曲线的性能受阈值选择的影响,不同阈值下曲线会有不同的形状。

  2. 不适用于多类别分类:P-R曲线主要用于二分类问题,对于多类别问题通常需要其他评估方法。

  3. 无法比较不同模型:P-R曲线通常用于单个模型的性能评估,而无法用于比较不同模型之间的性能。

6. 完整代码及运行结果

import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, precision_recall_curve, roc_curve, auc
import seaborn as sns

# 生成包含1000个样本的随机数据集
np.random.seed(0)
X = np.random.rand(1000, 10)
y = (X[:, 0] + X[:, 1] > 1).astype(int)

# 划分数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 特征标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# 初始化KNN分类器并选择K值
k = 5
knn_classifier = KNeighborsClassifier(n_neighbors=k)

# 训练KNN分类器
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)

# 模型的概率预测
y_scores = knn.predict_proba(X_test)[:, 1]

# 初始化空列表来存储P-R曲线上的点
precision_points = []
recall_points = []

# 遍历不同的阈值
thresholds = np.linspace(0, 1, 10)  # 生成100个阈值
for threshold in thresholds:
    y_pred = (y_scores >= threshold).astype(int)
    #print("y_pred:",y_pred)
    TP = np.sum((y_pred == 1) & (y_test == 1))
    FP = np.sum((y_pred == 1) & (y_test == 0))
    TN = np.sum((y_pred == 0) & (y_test == 0))
    FN = np.sum((y_pred == 0) & (y_test == 1))
    precision = TP / (TP + FP) if TP + FP > 0 else 1.0
    recall = TP / (TP + FN) if TP + FN > 0 else 1.0
    precision_points.append(precision)
    recall_points.append(recall)

print("TP:",TP)
print("FP:",FP)
print("TN:",TN)
print("FN:",FN)
print("R:",recall_points)
print("P:",precision_points)

# 绘制P-R曲线
plt.plot(recall_points, precision_points, marker='.')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.title('P-R Curve')
plt.grid(True)
plt.show()

运行结果:

对应的TP FP TN FN ,以及曲线上的点如下所示

P-R曲线: 

 

二、ROC曲线

1. ROC曲线的基本概念

ROC曲线通常用于评估二元分类器,用于可视化分类器在不同阈值下的性能表现。该曲线的两个重要指标:真正例率(TPR)假正例率(FPR)。TPR指的是被正确分类为正类的正类样本的比例。FPR指的是被错误的分类为正类的负类样本的比例。ROC曲线的目标是在各种不同阈值下,找到一个平衡点,使FPR尽可能低,而TPR尽可能高。ROC曲线的横轴是FPR假正例率,纵轴是TPR真正例率。

2. 计算公式及其含义

真正例率TPR:真实正类中被预测为正类的样本数占所有真实正类的比例

TPR = \frac{TP}{ TP + FN }

假正例率FPR:真实负类中被预测为正类的样本数占所有真实负类的比例

FPR = \frac{FP}{ FP + TN }

3. AUC

AUC表示ROC曲线下的面积,用于衡量模型的泛化性能,分类效果的好坏。AUC表示正例排在负例前面的概率。通过计算ROC曲线下的面积,可以得到一个在0到1之间的值,它表示分类器在不同工作点下对真正例率(TPR)和假正例率(FPR)的权衡。

  • AUC值的计算方法:

(1)将坐标点按照横坐标FPR排序 。
(2)计算第 i 个坐标点和第 i + 1 个坐标点的间距dx 。
(3)获取第 i 个或者第 i + 1个坐标点的纵坐标y。
(4)计算面积微元ds = y dx。
(5)对面积微元进行累加,得到AUC。

  • AUC值对模型性能的判断标准:

完美模型:AUC等于1,表示模型在所有阈值下都能完美区分正例和负例,具有最佳性能。

优秀模型:AUC接近于1,通常在0.9到1之间,表示模型具有很好的性能,可以高度区分正例和负例。

良好模型:AUC约为0.8到0.9之间,表明模型具有良好的性能,可以有效地区分正例和负例。

合理模型:AUC约为0.7到0.8之间,表明模型的性能在一般情况下还可接受,但可能存在改进的空间。

较差模型:AUC约为0.6到0.7之间。模型的性能相对较差,需要改进。

无效模型:AUC小于0.6,模型的性能非常差,不能有效地区分正例和负例。

4. 绘制方法与代码

(1)假设已经得出一系列样本被划分为正类的概率score值,按照大小排序。
(2)从高到低,依次将“score”值作为阈值threshold,当测试样本属于正样本的概率大于或等于这个阈值时,我们认为它为正样本,否则为负样本。
(3)每次选取一个不同的阈值threshold,得到一组FPR和TPR,以FPR值为横坐标、TPR值为纵坐标,描点。
(4)根据(3)中的坐标点,练成曲线。
主要代码:

# 模型的概率预测
y_scores = knn.predict_proba(X_test)[:, 1]

# 初始化空列表来存储P-R曲线上的点
TPR_points = []
FPR_points = []

# 遍历不同的阈值
thresholds = np.linspace(0, 1, 10)  # 生成100个阈值
#for threshold in thresholds:
y_pred = (y_scores >= 0.5).astype(int)
TP = np.sum((y_pred == 1) & (y_test == 1))
FP = np.sum((y_pred == 1) & (y_test == 0))
TN = np.sum((y_pred == 0) & (y_test == 0))
FN = np.sum((y_pred == 0) & (y_test == 1))
TPR = TP / (TP + FN)
FPR = FP / (FP + TN)
TPR_points.append(TPR)
FPR_points.append(FPR)

print("TP:",TP)
print("FP:",FP)
print("TN:",TN)
print("FN:",FN)
print("FPR:",FPR_points)
print("TPR:",TPR_points)

#计算AUC
AUC = roc_auc
print("AUC = ",AUC)

# 绘制 ROC 曲线
plt.figure()
plt.plot(FPR_points, TPR_points, color='darkorange', lw=2)
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate (FPR)')
plt.ylabel('True Positive Rate (TPR)')
plt.title('Receiver Operating Characteristic (ROC) Curve')
plt.show()

5. 具体应用与计算

与介绍P-R曲线时的例子相同,参照该表计算阈值为0.5时的TPR和FPR。

集标签:y_test模型概率预测:y_scores阈值0.5时,概率是否>0.5
0(N)0.0F
1(P)1.0T
0(N)0.6T
1(P)0.8T
0(N)0.4F
1(P)1.0T
1(P)0.8T
0(N)0.0F
0(N)0.4F
1(P)1.0T
1(P)0.6T
1(P)0.4F
1(P)1.0T
0(N)0.2F
0(N)0.6T
1(P)0.4F
0(N)0.2F
1(P)0.4F
0(N)0.0F
0(N)0.0F

TP 为真实结果是正类,且预测正确的个数,TP = 7

FP 为真实结果是负类,预测错误的个数,FP = 2

FN 为真实结果是正类,预测错误的个数,FN = 3

TN 为真实结果是负类,且预测正确的个数,TN = 8

TPR = 7 / ( 7 + 3 ) = 0.7

FPR = 2 / ( 2 + 8 ) = 0.2

6. ROC曲线的优缺点

优点

  1. 综合性能度量:ROC曲线提供了分类器在不同阈值下的性能综合评估,不局限于一个特定阈值的性能。

  2. 可视化:ROC曲线直观展示了分类器性能,帮助决策者更好地理解模型的行为。

  3. 适用于不平衡数据集:ROC曲线关注的是真正例率和假正例率,而不是样本数量。

缺点

  1. 不适用于多类别问题:ROC曲线通常用于二元分类问题,不适用于多类别分类。

  2. 不考虑概率分数差异:ROC曲线不考虑概率分数的绝对值

7. 完整代码及运行结果

import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier

# 生成包含1000个样本的随机数据集
np.random.seed(0)
X = np.random.rand(1000, 10)
y = (X[:, 0] + X[:, 1] > 1).astype(int)

# 划分数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 特征标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# 初始化KNN分类器并选择K值
k = 5
knn_classifier = KNeighborsClassifier(n_neighbors=k)

# 训练KNN分类器
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)

# 模型的概率预测
y_scores = knn.predict_proba(X_test)[:, 1]

# 初始化空列表来存储P-R曲线上的点
TPR_points = []
FPR_points = []

# 遍历不同的阈值
thresholds = np.linspace(0, 1, 10)  # 生成100个阈值
for threshold in thresholds:
    y_pred = (y_scores >= threshold).astype(int)
    TP = np.sum((y_pred == 1) & (y_test == 1))
    FP = np.sum((y_pred == 1) & (y_test == 0))
    TN = np.sum((y_pred == 0) & (y_test == 0))
    FN = np.sum((y_pred == 0) & (y_test == 1))
    TPR = TP / (TP + FN)
    FPR = FP / (FP + TN)
    TPR_points.append(TPR)
    FPR_points.append(FPR)

print("TP:",TP)
print("FP:",FP)
print("TN:",TN)
print("FN:",FN)
print("FPR:",FPR_points)
print("TPR:",TPR_points)

#计算AUC
AUC = roc_auc
print("AUC = ",AUC)

# 绘制 ROC 曲线
plt.figure()
plt.plot(FPR_points, TPR_points, color='darkorange', lw=2)
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('FPR')
plt.ylabel('TPR')
plt.title('ROC')
plt.show()

运行结果:

对应的TP FP TN FN AUC ,以及曲线上的点如下所示

ROC曲线: 

三、实验中的问题

1. 在选取数据集进行knn算法时,选用了上次实验的knn算法实例。根据之前使用的数据集绘制PR曲线和ROC曲线时,程序报错:

该错误的原因可能是因为:

1. 分类的结果必须是数值型,而实际上分类的结果可能为字符型;

2. PR和ROC曲线只针对二分类问题,而程序所用的实例为多分类问题。

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值