机器学习——P-R曲线和Roc曲线

本文介绍了在机器学习中P-R曲线和ROC曲线的原理、计算方法以及它们在评估模型性能、选择分类阈值和比较模型间优劣的应用。重点展示了如何通过这些曲线理解模型的全面表现并优化性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

        性能评估在机器学习中起着重要的作用,帮助我们理解和衡量模型的效果,并为模型选择、参数调优和特征选择提供指导,以达到更好的性能和预测能力。

一、P-R曲线

        1、基本概念

        在信息检索中,我们经常会关心“检索出的信息中有多少比例是用户感兴趣的”“用户感兴趣的信息中有多少被检索出来了”。“查准率”(precision)与“查全率”(recall)是更为适用于此类需求的性能度量。

        P——precision

        R——recall

        既然要画出这个曲线,那我们就要计算出这两个值,所以我们先来了解一下混淆矩阵:

        对于二分类问题,可将样例根据其真实类别与学习器预测类别的组合划 分为真正例(true positive) 、假正例(false positive) 、真反倒(true negative) 、假反例(false negative) 四种情形

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

        TP:被正确划分为正例的个数,即实际为正例且被分类器划分为正例的样例数

        FP:被错误地划分为正例的个数,即实际为反例但被分类器划分为正例的样例数

        FN:被错误地划分为反例的个数,即实际为正例但被分类器划分为反例的样例数

        TN:被正确划分为反例的个数,即实际为反例且被分类器划分为反例的样例数
        

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

        R=\frac{TP}{(TP+FN)}

        通过这两个公式我们就能够计算每个阈值下P和R的值,然后将它们在坐标系上的点连线起来就是一副P-R曲线,类似于图1-1:

        

图1-1

        2、评估P-R曲线

        若一个学习器的P-R曲线被另一个学习器完全”包住”,则后者的性能优于前者。当存在交叉时,可以计算曲线围住面积,但比较麻烦,平衡点(查准率=查全率,BEP)是一种度量方式。

        但BEP还是过于简化了些,更常用的是F1和F_{\beta }度量,它们分别是查准率和查全率的调和平均和加权调和平均。设样例总数为N,定义如下:

        F1=\frac{2*P*R}{P+R}=\frac{2*TP}{N+TP-TN}

        F_{\beta }=\frac{(1+\beta ^{2})*P*R}{(\beta ^{2}*P)+R}

        其中β>0度量了查全率对查准率的相对重要性. β = 1时退化为标准的F1; β >1时查全率有更大影响;β<1时查准率有更大影响。

二、Roc曲线

        1、基本概念

        ROC曲线是机器学习中用于评估二分类模型性能的一种常用工具。它以真正例率(True Positive Rate)为纵轴,假正例率(False Positive Rate)为横轴的曲线。在ROC曲线中,真正例率和假正例率的定义如下所示:

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

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

        其中,TP表示真正例,FN表示假反例,FP表示假正例,TN表示真反例。这四个属性依旧 是基于混淆矩阵,具体可见上一点中P-R曲线有解释。

        依旧是通过这两个公式算出每个阈值下的TPR和FPR,然后将它们在坐标系中画出并连线,即可得到Roc曲线图,如图1-2: 

                                

图1-2

        2、评估模型

        ROC曲线可以反映模型对于不同阈值下真正例率和假正例率的权衡关系,从而帮助我们选择合适的分类阈值以平衡真正例和假正例的权衡。ROC曲线越接近左上角,说明模型的性能越好。

        通常情况下,我们还会计算ROC曲线下的面积(AUC),这个数值可以用来比较不同模型之间的性能表现。ROC曲线下面积越大,表示该模型性能越好。一般来说,AUC值在0.5到1之间,0.5表示随机预测分类结果,而1则表示完美的分类器。

三、绘制P-R曲线和Roc曲线

        获取数据集并划分

        在绘制图像之前,我们需要先获取一个数据集并且对其进行训练和预测结果

​
# 生成数据集
X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, random_state=1)

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

​

        这里我们直接sklearn函数库里的make_classification()函数为我们生成1000个样本,其中有10个特征和一个标签,是一个二元分类数据集

        训练模型

        本次实验我们选择k—近邻算法作为我们本次的训练模型,我们先写一个knn分类器:

class KNNClassifier:
    def __init__(self, k):
        self.k = k

    def fit(self, X_train, y_train):
        self.X_train = X_train
        self.y_train = y_train

    def _euclidean_distance(self, x1, x2):
        return np.sqrt(np.sum((x1 - x2)**2))

    def predict(self, X_test):
        y_pred = []
        
        for x_test in X_test:
            distances = [self._euclidean_distance(x_test, x_train) for x_train in self.X_train]
            k_indices = np.argsort(distances)[:self.k]
            k_nearest_labels = [self.y_train[i] for i in k_indices]
            y_pred.append(max(set(k_nearest_labels), key=k_nearest_labels.count))
            
        return np.array(y_pred)
        训练

        接着将训练集用于模型训练,并且预测结果:

knn = KNNClassifier(k=5)
knn.fit(X_train, y_train)
y_pred = knn.predict(X_test)

        现在我们看看本次预测的准确率:

accuracy = accuracy_score(y_test, y_pred)
print("预测准确率:", accuracy)

        可以发现我们本次的准确率还是蛮高的,说明我们的k值取得比较好,预测比较准确

        混淆矩阵

        

def confusion_matrix(y_true, y_pred):
    # 获取所有类别
    classes = list(set(y_true))
    classes.sort()
    
    # 初始化混淆矩阵
    conf_matrix = [[0 for i in range(len(classes))] for j in range(len(classes))]
    
    # 计算混淆矩阵
    for i in range(len(y_true)):
        true_index = classes.index(y_true[i])
        pred_index = classes.index(y_pred[i])
        conf_matrix[true_index][pred_index] += 1
        
    return conf_matrix

# 计算混淆矩阵
conf_matrix = confusion_matrix(y_test, y_pred)
print("混淆矩阵:")
print(conf_matrix)  

        这就是我们本次预测的混淆矩阵,可以看到TP,FP,FN,TN都被计算了出来

        P-R曲线
def calculate_precision_recall(y_true, y_pred):
    thresholds = sorted(set(y_pred), reverse=True)
    precision = []
    recall = []
    for threshold in thresholds:
        y_pred_binary = [1 if y >= threshold else 0 for y in y_pred]
        true_positives = sum([1 for i in range(len(y_true)) if y_true[i] == 1 and y_pred_binary[i] == 1])
        false_positives = sum([1 for i in range(len(y_true)) if y_true[i] == 0 and y_pred_binary[i] == 1])
        true_negatives = sum([1 for i in range(len(y_true)) if y_true[i] == 0 and y_pred_binary[i] == 0])
        false_negatives = sum([1 for i in range(len(y_true)) if y_true[i] == 1 and y_pred_binary[i] == 0])
        if true_positives + false_positives == 0:
            precision.append(1.0)
        else:
            precision.append(float(true_positives) / (true_positives + false_positives))
        if true_positives + false_negatives == 0:
            recall.append(1.0)
        else:
            recall.append(float(true_positives) / (true_positives + false_negatives))
    return precision, recall

# 绘制PR曲线
precision, recall = calculate_precision_recall(y_test, y_pred)
plt.figure()
plt.step(recall, precision, where='post')
plt.xlabel('召回率')
plt.ylabel('精确率')
plt.title('PR 曲线')
plt.ylim([0.0, 1.05])
plt.xlim([0.0, 1.0])
plt.show()
         运行结果:

        可以发现本次运行出来的图形较为平滑,可能是因为本次训练出来的正确率较高

        ROC曲线

        

def custom_roc_curve(y_true, y_scores):
    thresholds = np.unique(y_scores)  # 所有不重复的预测分数
    thresholds = np.append(thresholds, thresholds.max() + 1)  # 添加一个额外的阈值,确保最终点在图上

    tpr = []  # TPR
    fpr = []  # FPR

    for threshold in thresholds:
        y_pred = (y_scores >= threshold).astype(int)
        true_positives = np.sum((y_true == 1) & (y_pred == 1))
        false_positives = np.sum((y_true == 0) & (y_pred == 1))
        true_negatives = np.sum((y_true == 0) & (y_pred == 0))
        false_negatives = np.sum((y_true == 1) & (y_pred == 0))
        
        tpr_value = true_positives / (true_positives + false_negatives) if (true_positives + false_negatives) > 0 else 0
        fpr_value = false_positives / (false_positives + true_negatives) if (false_positives + true_negatives) > 0 else 0
        
        tpr.append(tpr_value)
        fpr.append(fpr_value)

    return fpr, tpr


def custom_auc(fpr, tpr):
    auc = 0
    for i in range(1, len(fpr)):
        auc += 0.5 * (fpr[i] - fpr[i - 1]) * (tpr[i] + tpr[i - 1])
    return auc

# 绘制ROC曲线
fpr, tpr = custom_roc_curve(y_test, y_pred)
roc_auc = custom_auc(fpr, tpr)
#fpr, tpr, _ = roc_curve(y_test, y_pred)
#roc_auc = auc(fpr, tpr)
plt.figure()
plt.plot(fpr, tpr, label='ROC 曲线 (AUC = %0.2f)' % roc_auc)
plt.plot([0, 1], [0, 1], 'k--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('假正率')
plt.ylabel('真正率')
plt.title('ROC 曲线')
plt.legend(loc="lower right")
plt.show()
        运行结果:

        可以发现曲线也是趋于平滑的,且AUC的值基本接近于0了,说明了本次预测很成功,knn模型适用于本次数据集的测试

四、实验小结

        本次实验实现的P-R曲线和Roc曲线是评判机器学习模型的重要标准。PR曲线和ROC曲线的优势在于它们不受类别不平衡问题的影响,能够帮助我们更全面地评估模型的性能。除了直观地展示模型的性能之外,这些曲线还可以帮助我们根据具体的需求调整分类阈值,从而平衡召回率、精确度、真阳性率和假阳性率等指标。此外,通过在多个模型上比较PR曲线和ROC曲线,我们可以找到最佳的模型,并进一步优化性能。

        总的来说,PR曲线和ROC曲线的绘制和分析对于机器学习模型评估至关重要。它们提供了一种可视化的方式来理解模型在不同分类阈值下的性能表现,并帮助我们选择合适的阈值以满足具体需求。同时,它们也是比较不同模型性能的重要工具,有助于选择最优模型并进行性能改进。

### 训练LRSVM模型并绘制P-R曲线ROC曲线 对于鸢尾花数据集,可以采用逻辑回归(Logistic Regression, LR)支持向量机(Support Vector Machine, SVM)两种方法来构建二分类器,并通过`scikit-learn`库实现模型训练。为了评估这些模型的表现,可以通过计算预测概率并使用`matplotlib``sklearn.metrics`模块下的函数来绘制Precision-Recall(P-R)曲线接收者操作特性(ROC)曲线。 #### 数据预处理 由于原始的鸢尾花数据集中有三个类别,在此仅选取两个类别的样本作为正负样本来进行二分类任务。此外,还需要标准化特征值以便更好地适应某些类型的机器学习算法的要求[^2]。 ```python from sklearn import datasets from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split # 加载鸢尾花数据集 iris = datasets.load_iris() X = iris.data[(iris.target == 0) | (iris.target == 1)] # 只取前两类 y = iris.target[(iris.target == 0) | (iris.target == 1)] # 特征缩放 scaler = StandardScaler().fit(X) X_scaled = scaler.transform(X) # 划分训练集测试集 X_train, X_test, y_train, y_test = train_test_split( X_scaled, y, test_size=0.3, random_state=42, stratify=y) ``` #### 构建LRSVM模型 接下来创建逻辑回归支持向量机实例,并调用`.fit()`方法完成模型拟合过程;之后再利用`.predict_proba()`获取各条记录属于某一特定类别的可能性分数,这一步骤对于后续绘图至关重要[^4]。 ```python from sklearn.linear_model import LogisticRegression from sklearn.svm import SVC lr_clf = LogisticRegression(random_state=42).fit(X_train, y_train) svm_clf = SVC(probability=True, kernel='linear', C=1e5, random_state=42).fit(X_train, y_train) probas_lr = lr_clf.predict_proba(X_test) probas_svm = svm_clf.predict_proba(X_test) ``` #### 绘制P-R曲线 根据得到的概率估计值,借助于`precision_recall_curve`工具函数可方便地获得精确率(precision)-召回率(recall)之间的关系变化情况,并最终形成直观可视化的图形表示形式。 ```python import numpy as np import matplotlib.pyplot as plt from sklearn.metrics import precision_recall_curve, auc def plot_pr_curve(y_true, probas_pred, model_name): precisions, recalls, _ = precision_recall_curve(y_true, probas_pred[:, 1]) pr_auc = auc(recalls, precisions) plt.figure(figsize=(8,6)) plt.plot(recalls, precisions, label=f'{model_name} PR curve (area={pr_auc:.2f})') plt.xlabel('Recall') plt.ylabel('Precision') plt.title(f'Precision-Recall Curve ({model_name})') plt.legend(loc="best") plt.show() plot_pr_curve(y_test, probas_lr, 'Logistic Regression') plot_pr_curve(y_test, probas_svm, 'SVM') ``` #### 绘制ROC曲线 同样地,也可以应用类似的思路去展示真正例率(TPR)随假正例率(FPR)增长的趋势,即所谓的ROC曲线。这里会额外引入一个参数——曲线下面积(Area Under the Curve,AUC),它用来衡量整个分类系统的整体性能优劣程度[^3]。 ```python from sklearn.metrics import roc_curve, RocCurveDisplay fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(12, 6)) for clf, name in zip([lr_clf, svm_clf], ['Logistic Regression', 'SVM']): RocCurveDisplay.from_estimator(clf, X_test, y_test, ax=ax[0], name=name) ax[0].set_title("Receiver Operating Characteristic Curves") plt.tight_layout() plt.show() ``` 上述代码展示了如何基于Python环境中的`scikit-learn`框架快速搭建起针对鸢尾花数据集上执行二元分类任务所需的全部流程,包括但不限于数据准备、模型建立、评价指标可视化等方面的工作内容。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿K还阔以

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值