分类评价标准

    技术2024-07-10  77

    文章目录

    1. 概述2.混淆矩阵4.多个类别4.1.代码 5.ROC和AUC5.1.ROC的理论5.2.ROC绘制5.2.1.代码 5.3.AUC5.4.ROC的相关总结5.4.1.ROC曲线的优点5.4.2.ROC曲线的缺点 6.PR曲线6.1.代码6.2.使用场景 7.IoU8.代码参考

    1. 概述

    对语料进行分类后,要对分类结果进行评价。假设模型分类后的结果如下表2.3:

    (1)准确率(precision)和召回率(recall) 准确率,是分类结果中的某类别判断正确的文档中有多少是真正的正样本的比例,是针对预测结果而言的,衡量的是分类系统的查准率。计算公式如下:

    p = a a + b ∗ 100 % p=\frac{a}{a+b}*100\% p=a+ba100%

    召回率,是原来某个类别的文本的分类结果中有多少被预测为正确的比例,是针对原来样本而言的,衡量的是分类系统的查全率。计算公式如下:

    r = a a + c ∗ 100 % r=\frac{a}{a+c}*100\% r=a+ca100%

    但是,准确率和召回率不总是正相关,有时是负相关,需要F测度来平衡。 (2)F测度(F-measure) 是正确率和召回率的的加权调和平均,公式如下: r = ( a 2 + 1 ) p ∗ r a 2 ( p + r ) r=\frac{(a^2+1)p*r}{a^2(p+r)} r=a2(p+r)(a2+1)pr

    当时,就是F1,即 F 1 = 2 p ∗ r p + r F1=\frac{2p*r}{p+r} F1=p+r2pr

    (3)宏平均(Macro-averaging)和微平均(Micro-averaging) 准确率和召回率只是从一个类别上对分类结果进行评价,并没有对所有类别进行整体评价,这是要用到宏平均和微平均。

    宏平均首先对所有的类别分别求出准确率和召回率,计算每个类别的F1测度,然后求平均值,公式如下: 宏准确率: m a c r o p = ∑ k ∈ C p k ∣ C ∣ macro_p=\sum_{k \in C}\frac{p_k}{|C|} macrop=kCCpk 宏召回率: m a c r o r = ∑ k ∈ C r k ∣ C ∣ macro_r=\sum_{k \in C}\frac{r_k}{|C|} macror=kCCrk 宏平均: m a c r o F 1 = ∑ k ∈ C F 1 , k ∣ C ∣ macro_{F1}=\sum_{k \in C}\frac{F_{1,k}}{|C|} macroF1=kCCF1,k 微平均首先计算出所有类别的微准确率和微召回率,然后根据F1测度计算微平均,计算公式如下: 微准确率: 微召回率:

    微平均:

    2.混淆矩阵

    在机器学习中,有一个普遍适用的称为混淆姐阵(confusion matrix)的工具,它可以帮助人们更好地了解分类中的错误。有这样一个关于在房子周围可能发现的动物类型的预测,这个预测的三类问题的混淆矩阵如下表所示。

    3.一个类别的评价 接下来,我们考虑另外一个混淆矩阵,这次的矩阵只针对一个类别的问题。在下表中,给出了该混淆矩阵。在这个一个类的问题中,如果将一个正例判为正例,那么就可以认为产生了一个真正例(True Positive, TP ,也称真阳);如果对一个反例正确地判为反例,则认为产生了一个真反例(True Negative, TN ,也称真阴)。相应地,另外两种情况则分别称为伪反例(False Negative, FN ,也称假阴)和伪正例(False Positive, FP ,也称假阳)。 一个类别的解释: 所有样本中,存在属于该类别和不属于该类别的数据。预测结果也就有属于该类别和不属于该类别。

    True positives(TP,真正) : 预测为正,实际为正True negatives(TN,真负):预测为负,实际为负False positives(FP,假正): 预测为正,实际为负False negatives(FN,假负):预测为负,实际为正

    在分类中,当某个类别的重要性高于其他类别时,我们就可以利用上述定义来定义出多个比错误率更好的新指标。

    (1) Precision(精确率) Pre = TP/(TP+FP)(预测为正样本中,真正正确的比例) 当前预测为正样本类别中,被正确分类的比例(即正式正样本所占比例),就是我们一般理解意义上所关心的正样本的分类精确率;(精确率表示真正预测为正样本的样本数占所有预测为正样本的样本数的比例) 通俗说:预测为正样本中正确的概率。(样本分为正负,正样本和负样本中都有预测对和错的样本,求的是预测为正样本中预测正确的概率)

    Precision衡量的是所有被预测为正例的样本中有多少是真正例(实际是正样本,也预测为正样本)。但Precision并没有表现有多少正例是被错判为了负例(即FN),举个极端的例子,分类器只将一个正样本判为正例,其他所有都判为负例,这种情况下Precision为100%,但其实遗漏了很多正例,所以Precision常和下面的Recall (TPR) 相结合。

    (2) 召回率(recall) R=TP/(TP+FN),所有的正样本(样本数据集中的标号为正的数据)中,预测为正样本且正确的比例。 给出的是预测为正例的真实正例占所有真实正例的比例。在召回率很大的分类器中,真正判错的正例的数目并不多。 FN:负负得正,实际该样本为正的。

    precision和recall的差别是分母,正样本的来源不同: (3) precision是预测结果中标号为正的所有样本; (4) recall是测试集中标号为正的所有样本。

    (5) F-Score F-Score 是精确率Precision和召回率Recall的加权调和平均值。该值是为了综合衡量Precision和Recall而设定的。

    当a=1时,

    (6) Accuracy(准确率,ACC) ACC = (TP+TN)/(TP+TN+FN+FP)(所有样本) 表示预测正确的样本占所有样本的比率。正样本预测为正样本,负样本预测为负样本。

    这时,Precision和Recall都很重要,权重相同。 当有些情况下,我们认为Precision更重要,那就调整a的值小于1;如果我们认为Recall更加重要,那就调整a的值大于1。 一般来说,当F-Score或F1-score较高时,说明结果较理想。

    我们可以很容易构造一个高正确率或高召回率的分类器,但是很难同时保证两者成立。如果将任何样本都判为正例,那么召回率达到百分之百而此时正确率很低。构建一个同时使正确率和召回率最大的分类器是具有挑战性的。

    4.多个类别

    TP_class:所有类别的TP。 FP_class:所有类别的FP。 TN_class:所有类别的TN。 FN_class:所有类别的FN。 F1_class:所有类别的F1-class。

    微平均F1值(Micro-F1-measure)和宏平均F1值(Macro-F1-measure)。 Micro_F1=sum(TP_class) / (sum(TP_class) + sum(FP_class)) Macro_F1= sum(F1_class) / len(F1_class)

    所有类别的评价标准: micro_macro = (Micro_F1+Macro_F1)/2

    4.1.代码

    该代码是中国法研杯2018参赛时的评价标准:

    def cail_evaluator(predict_labels_list, marked_labels_list): “”” predict_labels_list: 预测结果[[pred1,pred2],[pred1,pred2],....] marked_labels_list:[[pred1,pred2],[pred1,pred2],....] one-hot和label id的区别 “”” # predict labels category predict_labels_category = [] samples = len(predict_labels_list) print('num of samples: ', samples) for i in range(samples): # number of samples predict_norm = sigmoid(predict_labels_list[i]) predict_category = [1 if i > 0.5 else 0 for i in predict_norm] predict_labels_category.append(predict_category) # marked labels category marked_labels_category = [] num_class = len(predict_labels_category[0]) print('num of classes: ', num_class) for i in range(samples): marked_category = to_categorical_single_class(marked_labels_list[i]) marked_labels_category.append(marked_category) tp_list = [] fp_list = [] fn_list = [] f1_list = [] for i in range(num_class): # 类别个数 # print('i: ', i) tp = 0.0 # predict=1, truth=1 fp = 0.0 # predict=1, truth=0 fn = 0.0 # predict=0, truth=1 # 样本个数 pre = [p[i] for p in predict_labels_category] # 第i个类别的预测结果 mar = [p[i] for p in marked_labels_category] # 第i个类别的真实结果 pre = np.asarray(pre) mar = np.asarray(mar) for i in range(len(pre)): if pre[i] == 1 and mar[i] == 1: tp += 1 elif pre[i] == 1 and mar[i] == 0: fp += 1 elif pre[i] == 0 and mar[i] == 1: fn += 1 # print('tp: %s, fp: %s, fn:%s ' %(tp, fp, fn)) precision = 0.0 if tp + fp > 0: precision = tp / (tp + fp) recall = 0.0 if tp + fn > 0: recall = tp / (tp + fn) f1 = 0.0 if precision + recall > 0: f1 = 2.0 * precision * recall / (precision + recall) # print('f1: ', f1) tp_list.append(tp) fp_list.append(fp) fn_list.append(fn) f1_list.append(f1) # micro level f1_micro = 0.0 if sum(tp_list) + sum(fp_list) > 0: f1_micro = sum(tp_list) / (sum(tp_list) + sum(fp_list)) # macro level f1_macro = sum(f1_list) / len(f1_list) score12 = (f1_macro + f1_micro) / 2.0 return f1_micro, f1_macro, score12

    5.ROC和AUC

    另一个用于度量分类中的非均衡性的工具是ROC曲线(ROC curve),ROC代表接收者操作特征(receiver operating characteristic)

    5.1.ROC的理论

    延生的意思: True Positive (真正例,TP):实际为正例,预测为正例。 False Negative (假负例,FN):实际为正例,预测为负例。 True Negative (真负例,TN):实际为负例,预测为负例。 False Positive (假正例,FP):实际为负例,预测为正例。

    True Positive Rate (TPR,真正例率) = TP/(TP+FN),又称Recall(查全率),Sensitivity(灵敏性)。Recall (TPR)衡量的是所有的正例中有多少是被正确分类了,也可以看作是为了避免假负例(FN)的发生,因为TPR高意味着FN低。Recall的问题和Precision正相反,没有表现出有多少负例被错判为正例(即FP),若将所有样本全划为正例,则Recall为100%,但这样也没多大用。

    False Negative Rate (FNR,假负例率) = FN(/TP+FN) = 1−TPR,由混淆矩阵可以看出该指标的着眼点在于正例,意为有多少正例被错判成了负例。

    True Negative Rate (TNR,真负例率) = TN/(TN+FP),又称Specificity(特异性)。Specificity衡量的是所有的负例中有多少是被正确分类了,由于类别不平衡问题中通常关注正例能否正确被识别,Specificity高则FP低,意味着很少将负例错判为正例,即该分类器对正例的判别具有“特异性”,在预测为正例的样本中很少有负例混入。

    False Positive Rate (FPR,假正例率) = FP/(TN+FP) = 1−TNR, 由混淆矩阵可以看出该指标的着眼点在于负例,意为有多少负例被错判成了正例。在ROC曲线中分别以TPR和FPR作为纵、横轴作图,显示出一种正例与负例之间的“博弈”,在下篇文章中详解。

    FP和FN还有个还有个与之相关的概念,那就是统计假设检验中的第一类错误 (Type I error)和第二类错误 (Type II error) 。由于我们比较关心正例,所以将负例视为零假设,正例视为备选假设,则第一类错误为错误地拒绝零假设 (负例),选择备选假设,则为FP;第二类错误为错误地接受零假设,则为FN。

    TPR =TP/P= TP / (TP+FN):真正例率,实际正样本中,预测为正确的实际正样本的样本占测试集中标号为正的样本的概率 FPR=FP/N=FP / (FP + TN):假正例率,实际负样本中,预测为错误的实际正样本的样本占测试集中标号为负的样本的概率

    可以看出,当一个样本被分类器判为正例,若其本身是正例,则TPR增加;若其本身是负例,则FPR增加,因此ROC曲线可以看作是随着阈值的不断移动,所有样本中正例与负例之间的“对抗”。曲线越靠近左上角,意味着越多的正例优先于负例,模型的整体表现也就越好。

    理想目标:TPR=1,FPR=0,即图中(0,1)点,故ROC曲线越靠拢(0,1)点,越偏离45度对角线越好,Sensitivity、Specificity越大效果越好。

    FPR=FP/N=FP / (FP + TN) TPR =TP/P= TP / (TP+FN) 先看一下ROC曲线中的随机线,图中[0,0]到[1,1]的虚线即为随机线,该线上所有的点都表示该阈值下TPR=FPR,若二者相等,意味着无论一个样本本身是正例还是负例,分类器预测其为正例的概率是一样的,这等同于随机猜测(注意这里的“随机”不是像抛硬币那样50%正面50%反面的那种随机)。

    上图中B点就是一个随机点,无论是样本数量和类别如何变化,始终将75%的样本分为正例。

    ROC曲线围成的面积 (即AUC)可以解读为:从所有正例中随机选取一个样本A,再从所有负例中随机选取一个样本B,分类器将A判为正例的概率比将B判为正例的概率大的可能性。可以看到位于随机线上方的点(如图中的A点)被认为好于随机猜测。在这样的点上TPR总大于FPR,意为正例被判为正例的概率大于负例被判为正例的概率。

    从另一个角度看,由于画ROC曲线时都是先将所有样本按分类器的预测概率排序,所以AUC反映的是分类器对样本的排序能力,依照上面的例子就是A排在B前面的概率。AUC越大,自然排序能力越好,即分类器将越多的正例排在负例之前。

    5.2.ROC绘制

    ROC本质上就是在设定某一阈值之后,计算出该阈值对应的TPR & FPR,便可以绘制出ROC上对应的一个点,当设定若干个阈值之后,便可以连成ROC曲线,因此可以想见,当所采样的阈值越多,ROC Curve越平滑。

    假设已经得出一系列样本被划分为正类的概率,然后按照大小排序,下图是一个示例,图中共有20个测试样本,“Class”一栏表示每个测试样本真正的标签(p表示正样本,n表示负样本),“Score”表示每个测试样本属于正样本的概率。

    接下来,我们从高到低,依次将“Score”值作为阈值threshold,当测试样本属于正样本的概率大于或等于这个threshold时,我们认为它为正样本,否则为负样本。举例来说,对于图中的第4个样本,其“Score”值为0.6,那么样本1,2,3,4都被认为是正样本,因为它们的“Score”值都大于等于0.6,而其他样本则都认为是负样本。每次选取一个不同的threshold,我们就可以得到一组FPR和TPR,即ROC曲线上的一点。这样一来,我们一共得到了20组FPR和TPR的值,将它们画在ROC曲线的结果如下图:

    算法过程: (1)假设有P个正例,N个反例,首先拿到分类器对于每个样本预测为正例的概率; (2)根据概率对所有样本进行逆序排列,然后将分类阈值设为最大,即把所有样本均预测为反例,此时图上的点为 (0,0)。 (3)然后将分类阈值依次设为每个样本的预测概率,即依次将每个样本划分为正例,如果该样本为真正例,则TP+1,即TPR+1/P ; 如果该样本为负例,则FP+1,即FPR+1/N。 (4)最后的到所有样本点的TPR和FPR值,用线段相连。

    5.2.1.代码

    def get_roc(pos_prob,y_true): """ :param pos_prob:[0.34,0.78,0.43,0.1,0.8,0.95,...] :param y_true:[0,1,0,0,1,1,...] :return: """ pos = y_true[y_true==1] neg = y_true[y_true==0] threshold = np.sort(pos_prob)[::-1] # [::-1]数据逆转,从小到大--》从大到小 y = y_true[pos_prob.argsort()[::-1]] tpr_all = [0] fpr_all = [0] tpr = 0 ; fpr = 0 x_step = 1/float(len(neg)) # 负样本的步长 y_step = 1/float(len(pos)) # 正样本的步长 y_sum = 0 for i in range(len(threshold)): # 样本个数 if y[i] == 1: tpr += y_step # 如果预测为正样本,则总步长加1,负样本不更新 tpr_all.append(tpr) # tpr的更新 fpr_all.append(fpr) else: fpr += x_step # 如果预测为负样本,则总步长加1 fpr_all.append(fpr) tpr_all.append(tpr) y_sum += tpr return tpr_all,fpr_all,y_sum*x_step

    5.3.AUC

    AUC(Area under Curve):Roc曲线下的面积,介于0.1和1之间。Auc作为数值可以直观的评价分类器的好坏,值越大越好。

    AUC 即ROC曲线下的面积,计算方式即为ROC Curve的微积分值,其物理意义可以表示为:随机给定一正一负两个样本,将正样本排在负样本之前的概率,因此AUC越大,说明正样本越有可能被排在负样本之前,即分类额结果越好。

    ROC曲线有个很好的特性:当测试集中的正负样本的分布变换的时候,ROC曲线能够保持不变。在实际的数据集中经常会出现样本类不平衡,即正负样本比例差距较大,而且测试数据中的正负样本也可能随着时间变化。下图是ROC曲线和Presision-Recall曲线的对比:

    在上图中,(a)和©为Roc曲线,(b)和(d)为Precision-Recall曲线。 (a)和(b)展示的是分类其在原始测试集(正负样本分布平衡)的结果,©(d)是将测试集中负样本的数量增加到原来的10倍后,分类器的结果,可以明显的看出,ROC曲线基本保持原貌,而Precision-Recall曲线变化较大。 解读:ROC曲线围成的面积 (即AUC)可以解读为:从所有正例中随机选取一个样本A,再从所有负例中随机选取一个样本B,分类器将A判为正例的概率比将B判为正例的概率大的可能性。可以看到位于随机线上方的点(如图中的A点)被认为好于随机猜测。在这样的点上TPR总大于FPR,意为正例被判为正例的概率大于负例被判为正例的概率。 从另一个角度看,由于画ROC曲线时都是先将所有样本按分类器的预测概率排序,所以AUC反映的是分类器对样本的排序能力,依照上面的例子就是A排在B前面的概率。AUC越大,自然排序能力越好,即分类器将越多的正例排在负例之前。

    5.4.ROC的相关总结

    1) ROC 可以反映二分类器的总体分类性能,但是无法直接从图中识别出分类最好的阈值,事实上最好的阈值也是视具体的场景所定; 2)ROC Curve 对应的AUC越大(或者说对于连续凸函数的ROC曲线越接近(0,1) )说明分类性能越好; 3)ROC曲线一定是需要在 y = x之上的,否则就是一个不理想的分类器;

    5.4.1.ROC曲线的优点

    放一张混淆矩阵图可能看得更清楚一点 :

    兼顾正例和负例的权衡。因为TPR聚焦于正例,FPR聚焦于与负例,使其成为一个比较均衡的评估方法。

    ROC曲线选用的两个指标, TPR=TPP=TP/(TP+FN), TPR=TPP=TP/(TP+FN), FPR=FPN=FP/(FP+TN), FPR=FPN=FP/(FP+TN), 都不依赖于具体的类别分布。 注意TPR用到的TP和FN同属P列,FPR用到的FP和TN同属N列,所以即使P或N的整体数量发生了改变,也不会影响到另一列。也就是说,即使正例与负例的比例发生了很大变化,ROC曲线也不会产生大的变化,而像Precision使用的TP和FP就分属两列,则易受类别分布改变的影响。 参考文献 [1] 中举了个例子,负例增加了10倍,ROC曲线没有改变,而PR曲线则变了很多。作者认为这是ROC曲线的优点,即具有鲁棒性,在类别分布发生明显改变的情况下依然能客观地识别出较好的分类器。

    5.4.2.ROC曲线的缺点

    上文提到ROC曲线的优点是不会随着类别分布的改变而改变,但这在某种程度上也是其缺点。因为负例N增加了很多,而曲线却没变,这等于产生了大量FP。像信息检索中如果主要关心正例的预测准确性的话,这就不可接受了。

    在类别不平衡的背景下,负例的数目众多致使FPR的增长不明显,导致ROC曲线呈现一个过分乐观的效果估计。ROC曲线的横轴采用FPR,根据FPR =FP/N=FP/(FP+TN),当负例N的数量远超正例P时,FP的大幅增长只能换来FPR的微小改变。结果是虽然大量负例被错判成正例,在ROC曲线上却无法直观地看出来。(当然也可以只分析ROC曲线左边一小段) 举个例子,假设一个数据集有正例20,负例10000,开始时有20个负例被错判,FPR=20/(20+9980)=0.002,接着又有20个负例错判,FPR2=40/(40+9960)=0.004,在ROC曲线上这个变化是很细微的。而与此同时Precision则从原来的0.5下降到了0.33,在PR曲线上将会是一个大幅下降。

    6.PR曲线

    PR曲线展示的是Precision vs Recall的曲线,PR曲线与ROC曲线的相同点是都采用了TPR (Recall),都可以用AUC来衡量分类器的效果。不同点是ROC曲线使用了FPR,而PR曲线使用了Precision,因此PR曲线的两个指标都聚焦于正例。类别不平衡问题中由于主要关心正例,所以在此情况下PR曲线被广泛认为优于ROC曲线。

    PR曲线的绘制与ROC曲线类似,PR曲线的AUC面积计算公式为:

    在PR曲线中,以Recall为x轴,Precision为y轴。

    绘制ROC曲线和PR曲线都是选定不同阈值,从而得到不同的x轴和y轴的值,画出曲线。

    PR曲线展示的是Precision vs Recall的曲线,PR曲线与ROC曲线的相同点是都采用了TPR (Recall),都可以用AUC来衡量分类器的效果。不同点是ROC曲线使用了FPR,而PR曲线使用了Precision,因此PR曲线的两个指标都聚焦于正例。类别不平衡问题中由于主要关心正例,所以在此情况下PR曲线被广泛认为优于ROC曲线。

    算法过程: (1)假设有P个正例,N个反例,首先拿到分类器对于每个样本预测为正例的概率; (2)根据概率对所有样本进行逆序排列,然后将分类阈值设为最大,即把所有样本均预测为反例,此时图上的点为(0,0)。 (3)然后将分类阈值依次设为每个样本的预测概率,即依次将每个样本划分为正例,如果该样本为真正例,则tp=tp+1,recall=tp/P,precision=tp/(tp+fp) ,auc=(recall(i)-recall(i-1))*precision(i); 如果该样本为负例,则fp=fp+1,即recall=tp/P,precision=tp/(tp+fp)。 (4)最后的到所有样本点的recall和precision值,用线段相连。

    6.1.代码

    def get_pr(pos_prob,y_true): """ :param pos_prob:[0.34,0.78,0.43,0.1,0.8,0.95,...] :param y_true:[0,1,0,0,1,1,...] :return: """ pos = y_true[y_true==1] threshold = np.sort(pos_prob)[::-1] y = y_true[pos_prob.argsort()[::-1]] recall = [] ; precision = [] tp = 0 ; fp = 0 # y_step = 1/float(len(pos)) auc = 0 for i in range(len(threshold)): # 每个样本 if y[i] == 1: tp += 1 # 随着样本的增加,recall和precision跟着改变 recall.append(tp/len(pos)) precision.append(tp/(tp+fp)) auc += (recall[i]-recall[i-1])*precision[i] else: fp += 1 recall.append(tp/len(pos)) precision.append(tp/(tp+fp)) return precision,recall,auc

    6.2.使用场景

    ROC曲线由于兼顾正例与负例,所以适用于评估分类器的整体性能,相比而言PR曲线完全聚焦于正例。 如果有多份数据且存在不同的类别分布,比如信用卡欺诈问题中每个月正例和负例的比例可能都不相同,这时候如果只想单纯地比较分类器的性能且剔除类别分布改变的影响,则ROC曲线比较适合,因为类别分布改变可能使得PR曲线发生变化时好时坏,这种时候难以进行模型比较;反之,如果想测试相同类别分布下对分类器的性能的影响,则PR曲线比较适合。

    如果想要评估在相同的类别分布下正例的预测情况,则宜选PR曲线。

    类别不平衡问题中,ROC曲线通常会给出一个乐观的效果估计,所以大部分时候还是PR曲线更好。

    最后可以根据具体的应用,在曲线上找到最优的点,得到相对应的precision,recall,f1 score等指标,去调整模型的阈值,从而得到一个符合具体应用的模型。

    7.IoU

    IoU这一值,可以理解为系统预测出来的框与原来图片中标记的框的重合程度。 计算方法即检测结果Detection Result与 Ground Truth 的交集比上它们的并集,即为检测的准确率:

    如下图所示: 蓝色的框是:GroundTruth 黄色的框是:DetectionResult 绿色的框是:DetectionResult ⋂ GroundTruth 红色的框是:DetectionResult ⋃ GroundTruth

    8.代码

    # coding: utf-8 # Author: shelley # 2020/5/2614:01 # https://nbviewer.jupyter.org/github/massquantity/Class-Imbalance/blob/master/Code_Class_Imbalance.ipynb#Confusion-Matrix import numpy as np import pandas as pd import seaborn as sns import matplotlib.pyplot as plt from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from sklearn.decomposition import PCA from sklearn.model_selection import train_test_split from sklearn.model_selection import StratifiedKFold X,y = make_classification(n_samples=2000, n_features=10, n_informative=4, n_redundant=1, n_classes=2, n_clusters_per_class=1, weights=[0.9,0.1], flip_y=0.1, random_state=2018) pca = PCA(n_components=2) X_pca = pca.fit_transform(X) X_pca = pd.DataFrame(X_pca) X_pca.columns=["pca_a","pca_b"] X_pca["y"] = y print(X_pca[:3]) # sns.set() # sns.lmplot("pca_a","pca_b",data=X_pca, hue="y", fit_reg=False, markers=["o","x"],size=8,aspect=1.5,legend=False) # plt.legend(fontsize=20,bbox_to_anchor=(0.98, 0.6),edgecolor ='r') # plt.xlabel("axis_1",fontsize=17) # plt.ylabel("axis_2",fontsize=17) # plt.show() kf = StratifiedKFold(n_splits=2, random_state=42) for train_index, test_index in kf.split(X,y): X_train, X_test = X[train_index], X[test_index] y_train, y_test = y[train_index], y[test_index] lr = LogisticRegression() lr.fit(X_train,y_train) print(lr.score(X_test,y_test)) pos_prob_lr = lr.predict_proba(X_test)[:,1] rf = RandomForestClassifier(random_state=42) rf.fit(X_train,y_train) print(rf.score(X_test,y_test)) pos_prob_rf = rf.predict_proba(X_test)[:,1] # roc def get_roc(pos_prob,y_true): """ :param pos_prob:[0.34,0.78,0.43,0.1,0.8,0.95,...] :param y_true:[0,1,0,0,1,1,...] :return: """ pos = y_true[y_true==1] neg = y_true[y_true==0] threshold = np.sort(pos_prob)[::-1] # [::-1]数据逆转,从小到大--》从大到小 y = y_true[pos_prob.argsort()[::-1]] tpr_all = [0] fpr_all = [0] tpr = 0 ; fpr = 0 x_step = 1/float(len(neg)) # 负样本的步长 y_step = 1/float(len(pos)) # 正样本的步长 y_sum = 0 for i in range(len(threshold)): # 样本个数 if y[i] == 1: tpr += y_step # 如果预测为正样本,则总步长加1 tpr_all.append(tpr) # tpr的更新 fpr_all.append(fpr) else: fpr += x_step # 如果预测为负样本,则总步长加1 fpr_all.append(fpr) tpr_all.append(tpr) y_sum += tpr return tpr_all,fpr_all,y_sum*x_step tpr_lr,fpr_lr,auc_lr = get_roc(pos_prob_lr,y_test) tpr_rf,fpr_rf,auc_rf = get_roc(pos_prob_rf,y_test) # plt.figure(figsize=(10,6)) # plt.plot(fpr_lr,tpr_lr,label="Logistic Regression (AUC: {:.3f})".format(auc_lr),linewidth=2) # plt.plot(fpr_rf,tpr_rf,'g',label="Random Forest (AUC: {:.3f})".format(auc_rf),linewidth=2) # plt.xlabel("False Positive Rate",fontsize=16) # plt.ylabel("True Positive Rate",fontsize=16) # plt.title("ROC Curve",fontsize=16) # plt.legend(loc="lower right",fontsize=16) # plt.show() X_test_dup = np.vstack((X_test,X_test[y_test==0],X_test[y_test==0],X_test[y_test==0], X_test[y_test==0],X_test[y_test==0],X_test[y_test==0], X_test[y_test==0],X_test[y_test==0],X_test[y_test==0])) y_test_dup = np.array(y_test.tolist() + y_test[y_test==0].tolist()*9) # pos_prob_lr_dup = lr.predict_proba(X_test_dup)[:,1] # pos_prob_rf_dup = rf.predict_proba(X_test_dup)[:,1] # tpr_lr_dup,fpr_lr_dup,auc_lr_dup = get_roc(pos_prob_lr_dup,y_test_dup) # tpr_rf_dup,fpr_rf_dup,auc_rf_dup = get_roc(pos_prob_rf_dup,y_test_dup) # plt.figure(figsize=(10,6)) # plt.plot(fpr_lr_dup,tpr_lr_dup,label="Logistic Regression (AUC: {:.3f})".format(auc_lr_dup),linewidth=2) # plt.plot(fpr_rf_dup,tpr_rf_dup,'g',label="Random Forest (AUC: {:.3f})".format(auc_rf_dup),linewidth=2) # plt.xlabel("False Positive Rate",fontsize=16) # plt.ylabel("True Positive Rate",fontsize=16) # plt.title("ROC Curve",fontsize=16) # plt.legend(loc="lower right",fontsize=16) # plt.show() # index = np.random.permutation(len(X_test_dup)) # X_test_dup = X_test_dup[index] # y_test_dup = y_test_dup[index] # pos_prob_lr_dup = lr.predict_proba(X_test_dup)[:,1] # pos_prob_rf_dup = rf.predict_proba(X_test_dup)[:,1] # tpr_lr_dup,fpr_lr_dup,auc_lr_dup = get_roc(pos_prob_lr_dup,y_test_dup) # tpr_rf_dup,fpr_rf_dup,auc_rf_dup = get_roc(pos_prob_rf_dup,y_test_dup) # # plt.figure(figsize=(10,6)) # plt.plot(fpr_lr_dup,tpr_lr_dup,label="Logistic Regression (AUC: {:.3f})".format(auc_lr_dup),linewidth=2) # plt.plot(fpr_rf_dup,tpr_rf_dup,'g',label="Random Forest (AUC: {:.3f})".format(auc_rf_dup),linewidth=2) # plt.xlabel("False Positive Rate",fontsize=16) # plt.ylabel("True Positive Rate",fontsize=16) # plt.title("ROC Curve",fontsize=16) # plt.legend(loc="lower right",fontsize=16) # plt.show() # Precision Recall Curve def get_pr(pos_prob,y_true): """ :param pos_prob:[0.34,0.78,0.43,0.1,0.8,0.95,...] :param y_true:[0,1,0,0,1,1,...] :return: """ pos = y_true[y_true==1] threshold = np.sort(pos_prob)[::-1] y = y_true[pos_prob.argsort()[::-1]] recall = [] ; precision = [] tp = 0 ; fp = 0 # y_step = 1/float(len(pos)) auc = 0 for i in range(len(threshold)): if y[i] == 1: tp += 1 recall.append(tp/len(pos)) precision.append(tp/(tp+fp)) auc += (recall[i]-recall[i-1])*precision[i] else: fp += 1 recall.append(tp/len(pos)) precision.append(tp/(tp+fp)) return precision,recall,auc precision_lr,recall_lr,auc_lr = get_pr(pos_prob_lr,y_test) precision_rf,recall_rf,auc_rf = get_pr(pos_prob_rf,y_test) plt.figure(figsize=(10,6)) plt.plot(recall_lr,precision_lr,label="Logistic Regression (AUC: {:.3f})".format(auc_lr),linewidth=2) plt.plot(recall_rf,precision_rf,label="Random Forest (AUC: {:.3f})".format(auc_rf),linewidth=2) plt.xlabel("Recall",fontsize=16) plt.ylabel("Precision",fontsize=16) plt.title("Precision Recall Curve",fontsize=17) plt.legend(fontsize=16) plt.show()

    参考

    ROC、Precision、Recall、TPR、FPR理解 https://www.jianshu.com/p/be2e037900a1

    机器学习之分类器性能指标之ROC曲线、AUC值 https://www.cnblogs.com/dlml/p/4403482.html

    ROC曲线和PR(Precision-Recall)曲线的联系

    机器学习之类别不平衡问题 (2) —— ROC和PR曲线 https://www.cnblogs.com/massquantity/p/8592091.html

    机器学习之类别不平衡问题 (1) —— 各种评估指标 https://www.cnblogs.com/massquantity/p/8550875.html

    Processed: 0.028, SQL: 9