赞
踩
目录
2、准确率(Accuracy)、精确率(Precision)、灵敏度(Sensitivity)、召回率(Recall)、特异度(Specificity)、F1 Score等指标
混淆矩阵是最简单、最基础的分类的评估指标,在这里只讲二分类的混淆矩阵,多分类与二分类类似。
真实值是positive,模型认为是positive的数量(True Positive=TP)
真实值是positive,模型认为是negative的数量(False Negative=FN):这就是统计学上的第一类错误(Type I Error)
真实值是negative,模型认为是positive的数量(False Positive=FP):这就是统计学上的第二类错误(Type II Error)
真实值是negative,模型认为是negative的数量(True Negative=TN)
from sklearn.metrics import confusion_matrix import matplotlib.pyplot as plt import numpy as np def plot_confusion_matrix(y, y_hat, labels = [1,0]): # y是真实的标签,y_hat是预测的标签,labels表示label的展示顺序 cm = confusion_matrix(y,y_hat,labels = labels) plt.figure(figsize=(5, 5)) plt.imshow(cm, interpolation='nearest',cmap=plt.cm.binary) plt.xticks(np.array(range(len(labels))), labels, rotation=90) # 将标签印在x轴坐标上 plt.yticks(np.array(range(len(labels))), labels) # 将标签印在y轴坐标上 plt.title('Confusion Matrix') # 标题 plt.xlabel('predict label') # x轴 plt.ylabel('true label') # y轴 ind_array = np.arange(len(labels)) x, y = np.meshgrid(ind_array, ind_array) for x_val, y_val in zip(x.flatten(), y.flatten()): c = cm[y_val][x_val] plt.text(x_val, y_val, "%s" % (c,), color='red', fontsize=20, va='center', ha='center') plt.show()
图形结果:
使用较多的指标通常为精确率和召回率。
精确率描述模型预测结果准确度的衡量指标,表示模型预测结果有多准。
召回率描述模型预测结果全面性的衡量指标,表示模型预测结果涵盖有多全。
一般来说:
阈值越大,召回率(TPR)越低,精确率(PPV)越高,假阳性率(FPR)越低。
当阈值为0时,召回率(TPR)等于1,假阳性率(FPR)等于1(因为所有的样本都预测为正样本,所以真阳性率为1,假阳性率也为1)
当阈值为1时,召回率(TPR)等于0,假阳性率(FPR)等于0(因为所有的样本都被预测为负样本,所以真阳性率为0,假阳性率为0)
假设:全行下个月信用卡用户共1,000,000,其中实际逾期的客户数为10,000;模型预测出15,000个客户会出现逾期,其中8000个客户为真实逾期用户。
从而:精确率=TP/(TP+FP)=8000/15000=0.53
召回率=TP/(TP+FN)=8000/10000=0.8
ROC的全称是Receiver Operating Characteristic Curve,中文名字叫“受试者工作特征曲线”,顾名思义,其主要的分析方法就是画这条特征曲线。
ROC曲线的横轴是FPR(假阳性率、误诊率)、纵轴是TPR(真阳性率、灵敏度)。
这条曲线代表的是在不同的阈值下,FPR和TPR的一个变化曲线,通常,我们希望FPR尽可能的小,而TPR尽可能的大,所以曲线越靠近左上角,模型的效果越好。
如下图,模型效果A>B>C
AUC(Area Under Curve)被定义为ROC曲线下的面积,取值范围一般在0.5和1之间。使用AUC值作为评价标准是因为很多时候ROC曲线并不能清晰的说明哪个分类器的效果更好,而作为一个数值,对应AUC更大的分类器效果更好。
AUC = 1,是完美分类器,采用这个预测模型时,存在至少一个阈值能得出完美预测。绝大多数预测的场合,不存在完美分类器。
0.5 < AUC < 1,优于随机猜测。这个分类器(模型)妥善设定阈值的话,能有预测价值。
AUC = 0.5,跟随机猜测一样(例:丢铜板),模型没有预测价值。
AUC < 0.5,比随机猜测还差;但只要总是反预测而行,就优于随机猜测。
Gini coefficient 是指绝对公平线(line of equality)和洛伦茨曲线(Lorenz Curve)围成的面积与绝对公平线以下面积的比例,即gini coefficient = A面积 / (A面积+B面积) 。
用在评判分类模型的预测效力时,是指ROC曲线曲线和中线围成的面积与中线之上面积的比例。因此Gini coefficient与AUC可以互相转换:
gini =A/ (A+ B) = (AUC - C) / (A+ B) = (AUC -0.5) /0.5=2*AUC -1
from sklearn.metrics import auc,roc_curve import matplotlib.pyplot as plt def plot_roc_curve(y, y_hat_proba, pos_label = 1): # This is a function to plot a roc curve # y指的是真实值,y_hat_proba指的是预测的概率结果 fpr, tpr, thresholds = roc_curve(y, y_hat_proba, pos_label=pos_label) AUC = auc(fpr, tpr) lw = 2 plt.figure(figsize=(8, 8)) plt.plot(fpr, tpr, color='darkorange', lw=lw, label='ROC curve (area = %0.4f)' % AUC) ###假正率为横坐标,真正率为纵坐标做曲线 plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--') plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.title('Receiver operating characteristic example') plt.legend(loc="lower right") plt.show()
图形结果:
KS(Kolmogorov-Smirnov)曲线(洛伦兹曲线)的纵轴是表示TPR和FPR的值,就是这两个值可以同时在一个纵轴上体现,横轴就是阈值,表示模型能够将正、负客户区分开的程度越大。两条曲线之间相距最远的地方对应的阈值,就是最能划分模型的阈值,即KS=max(TPR-FPR),KS值越大,模型的区分度越好。
说明K-S曲线的做法:
(1)把模型对样本的输出概率(predict_proba)从大到小排序,计算对应不同阈值时,大于等于阈值的样本数占总样本的百分比percentage
(2)计算阈值取每个概率时对应的TPR和FPR值,分别画(percentage, TPR)和(percentage, FPR)的曲线
(3) K-S曲线上的KS值,即max(TPR−FPR),即两条曲线间的最大间隔距离。
(有的资料横轴是阈值,都是一样的道理)
from sklearn.metrics import roc_curve import matplotlib.pyplot as plt def KS_cal(y,y_hat_proba): # 计算ks值 fpr, tpr, _ = roc_curve(y, y_hat_proba) diff = np.subtract(tpr, fpr) ks = diff.max() return ks def plot_ks(y,y_hat_proba): # 画ks曲线 fpr, tpr, thresholds = roc_curve(y, y_hat_proba) diff = np.subtract(tpr, fpr) ks = diff.max() y_len = len(y) # 计算比例,这样计算比较快 # 也可以自己划分样本的比例,自己计算fpr,tpr y_hat_proba_sort = sorted(y_hat_proba, reverse=True) cnt_list = [] cnt = 0 for t in thresholds: for p in y_hat_proba_sort[cnt:]: if p >= t: cnt += 1 else: cnt_list.append(cnt) break percentage = [c/float(y_len) for c in cnt_list] if min(thresholds)<=min(y_hat_proba_sort): percentage.append(1) # 以下为画图部分 best_thresholds = thresholds[np.argmax(diff)] best_percentage = percentage[np.argmax(diff)] best_fpr = fpr[np.argmax(diff)] lw = 2 plt.figure(figsize=(8, 8)) plt.plot(percentage, tpr, color='darkorange', lw=lw, label='True Positive Rate') plt.plot(percentage, fpr, color='darkblue', lw=lw, label='False Positive Rate') plt.plot(percentage, diff, color='darkgreen', lw=lw, label='diff') plt.plot([best_percentage, best_percentage], [best_fpr, best_fpr+ks], color='navy', lw=lw, linestyle='--',label = 'ks = %.2f, thresholds = %.2f'%(ks,best_thresholds)) plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel('percentage') plt.title('Kolmogorov-Smirnov') plt.legend(loc="lower right") plt.show()
图形结果:
CAP(Cumulative Accuracy Profile)的步骤如下:首先,对于所有预测的结果,按照其概率probability值降序排列起来,然后我们顺序依次把样本划分到观察集中。假设所有的样本数量为T,真实的正样本数量为Tp,真实的负样本的数量为Tn,Tp+Tn=T。每次都划分一个样本到观察集中,观察集中的样本个数为O,观察集中包含的真正的正样本数为Op。
在最理想的情况下,存在一个阈值,大于的样本全部为真实正样本,小于的全部为真实负样本,所以在这种情况下,probability>时,每增加一个样本到预测的正样本中,都是真实的正样本,直到阈值,此时O=Op=Tp,然后再往里加样本时,所有的样本都是负样本,所以将一直保持O=Op=Tp的状态,直到所有样本添加完。
在最差的情况下,则是一个斜率为常数的直线。
(有的文章CAP算的是占所有样本的比例,而本文算的是个数,都是一样的道理)
然而实际情况不是理想情况,通常预测的正样本中往往掺杂着负样本,如下图所示:
AR值为上图绿色的部分除以橙色绿色的面积和,AR越接近1,说明模型的效果越好。
def AR_cal(y,y_hat): # 计算AR值 if type(y)==pd.core.frame.DataFrame: y = y.iloc[:,0] total_count = len(y) pos_count = int(np.sum(y)) a = auc([0, total_count], [0, pos_count]) ap = auc([0, pos_count, total_count], [0, pos_count, pos_count]) - a model_y = [y_ for _, y_ in sorted(zip(y_hat, y), reverse=True)] y_values = np.append([0], np.cumsum(model_y)) x_values = np.arange(0, total_count + 1) ar = auc(x_values, y_values) - a AR = ar/float(ap) return AR def plot_CAP(y,y_hat): # 画CAP曲线,与计算AR值 if type(y)==pd.core.frame.DataFrame: y = y.iloc[:,0] total_count = len(y) pos_count = int(np.sum(y)) a = auc([0, total_count], [0, pos_count]) # random model ap = auc([0, pos_count, total_count], [0, pos_count, pos_count]) - a model_y = [y_ for _, y_ in sorted(zip(y_hat, y), reverse=True)] y_values = np.append([0], np.cumsum(model_y)) x_values = np.arange(0, total_count + 1) ar = auc(x_values, y_values) - a AR = ar/float(ap) lw = 2 plt.figure(figsize=(8, 8)) plt.plot([0, pos_count, total_count], [0, pos_count, pos_count], color='darkblue', lw=lw, label='Perfect Model') plt.plot(x_values, y_values, color='darkgreen', lw=lw, label='Actual Model') plt.plot([0, total_count], [0, pos_count], color='darkorange', lw=lw, label='Random Model',linestyle='--') plt.xlim([0.0, total_count]) plt.ylim([0.0, pos_count+1]) plt.xlabel('Total Observations') plt.ylabel('Positive Observations') plt.title('Cumulative Accuracy Profile, AR = %.2f'%AR) plt.legend(loc="lower right") plt.show()
图形结果:
由于模型是以特定时期的样本所开发的,此模型是否适用于开发样本之外的族群,必须经过稳定性测试才能得知。稳定度指标(population stability index ,PSI)可衡量测试样本及模型开发样本评分的的分布差异,为最常见的模型稳定度评估指针。其实PSI表示的就是按分数分档后,针对不同样本,或者不同时间的样本,population分布是否有变化,就是看各个分数区间内样本占总样本的占比是否有显著变化。
上面的公式可以保证始终不为负,且PSI越小,模型越稳定。
PSI<0.1 样本分布有微小变化
PSI 0.1~0.2 样本分布有变化
PSI>0.2 样本分布有显著变化
PSI既可以评估模型整体的稳定性,也可以评估特征的稳定性
风控模型不稳定时的排查方向
当通过PSI指标发现模型不稳定时,我们该如何去排查原因?引起模型不稳定的因素是多种多样的,主要包括:
def PSI_cal(score_actual, score_except, bins = 10): actual_min = score_actual.min() actual_max = score_actual.max() binlen = (actual_max - actual_min) / bins cuts = [actual_min + i * binlen for i in range(1, bins)] cuts.insert(0, -float("inf")) cuts.append(float("inf")) actual_cuts = np.histogram(score_actual, bins=cuts) except_cuts = np.histogram(score_except, bins=cuts) actual_df = pd.DataFrame(actual_cuts[0], columns=['actual']) predict_df = pd.DataFrame(except_cuts[0], columns=['predict']) psi_df = pd.merge(actual_df, predict_df, right_index=True, left_index=True) psi_df['actual_rate'] = (psi_df['actual'] + 1) / psi_df['actual'].sum() # 计算占比,分子加1,防止计算PSI时分子分母为0 psi_df['predict_rate'] = (psi_df['predict'] + 1) / psi_df['predict'].sum() psi_df['psi'] = (psi_df['actual_rate'] - psi_df['predict_rate']) * np.log( psi_df['actual_rate'] / psi_df['predict_rate']) psi = psi_df['psi'].sum() return psi
Lift图衡量的是,与不利用模型相比,模型的预测能力“变好”了多少,lift(提升指数)越大,模型的运行效果越好。
例如,原来不适用模型,而采用随机的营销方式,只有1%的客户会成功转化;而使用了模型进行精准营销后,会有10%的客户成功转化。那么,lift=10%/1%=10。
Gain图是描述整体精准度的指标,也就是PPV,在模型预测是Positive的样本中,模型预测正确的比例,这个比例也是越大越好,在0~1之间。
Gain图、Lift图和KS画图的方式比较类似:
(1)把模型对样本的输出概率(predict_proba)从大到小排序,按顺序选取截断点,并得到对应截断点对应的累计样本比例percentage
(2)计算每个截断点对应的Gain和Lift值,分别画(percentage, Gain)和(percentage, Lift)的曲线
正常来说Gain和Lift图除了坐标轴的尺度不同外,曲线应该一样的,但是我查了好多资料发现Gain图的纵坐标是预测出来的正样本中真实的正样本占所有真实正样本的占比(这两个曲线一般没人用,反正意思理解了就可以了)
def lift_cal(y,y_hat): if type(y) == pd.core.frame.DataFrame: y = y.iloc[:, 0] total_count = len(y) pos_count = int(np.sum(y)) pos_proportion = pos_count/float(total_count) PRECISION = precision_score(y, y_hat) lift = PRECISION/pos_proportion return lift def plot_gain(y,y_hat_proba,bins = 10): if type(y) == pd.core.frame.DataFrame: y = y.iloc[:, 0] gain_list = [] total_count = len(y) pos_count = int(np.sum(y)) df = pd.DataFrame({'y':y,'y_hat_proba':y_hat_proba}) df.sort_values(by='y_hat_proba',inplace = True,ascending=False) df.reset_index(drop = True,inplace = True) binlen = len(y) / bins cuts = [int(i * binlen) for i in range(1, bins)] cuts.append(total_count) x = [0]+[float(i)/bins for i in range(1,bins+1)] for c in cuts: cumulative_pos = np.sum(df['y'][:c]) gain_list.append(cumulative_pos/float(pos_count)) gain_list=[0]+gain_list lw = 2 plt.figure(figsize=(8, 8)) plt.plot(x, gain_list, color='darkorange',lw=lw,label='proportion of cumulative events(model)') plt.plot([0,1],[0,1],color='darkblue',lw=lw,label='proportion of cumulative events(random)',linestyle='--') plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel('The proportion of datasets') plt.title('Gain Chart') plt.legend(loc="lower right") plt.grid() plt.show() def plot_lift(y, y_hat_proba, bins=10): if type(y) == pd.core.frame.DataFrame: y = y.iloc[:, 0] lift_list = [] total_count = len(y) pos_count = int(np.sum(y)) pos_proportion = pos_count / float(total_count) df = pd.DataFrame({'y':y,'y_hat_proba':y_hat_proba}) df.sort_values(by='y_hat_proba',inplace = True,ascending=False) df.reset_index(drop = True,inplace = True) binlen = len(y) / bins cuts = [int(i * binlen) for i in range(1, bins)] cuts.append(total_count) x = [float(i)/bins for i in range(1,bins+1)] for c in cuts: y_hat = list(np.repeat(1,c))+list(np.repeat(0,total_count-c)) lift_list.append(precision_score(df['y'], y_hat)/pos_proportion) lw = 2 plt.figure(figsize=(8, 8)) plt.plot(x, lift_list, color='darkorange',lw=lw,label='model lift') plt.plot([x[0],x[-1]],[1,1],color='darkblue',lw=lw,label='random lift',linestyle='--') plt.xlim([0.0, 1.0]) plt.ylim([0.0, lift_list[0]+1]) plt.xlabel('The proportion of datasets') plt.title('Lift Chart') plt.legend(loc="lower right") plt.grid() plt.show()
画图结果
Gain曲线图,当橙色的线快速上升至1时,表示模型较好。
Lift曲线图,当橙色的线在很高的提升值上保持一段后,迅速下降至1时,表示模型较好。
一般我在项目中,通常以召回率、精确率、F1-Score为主,ROC、AUC为辅,其它的一般很少看。当然这个也和项目的最终目标与个人的习惯有很大的关系。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。