混淆矩阵(Confusion Matrix)
混淆矩阵是衡量分类型模型准确度中最基本,最直观,计算最简单的方法。
简单来说,混淆矩阵就是一张表格。
他的四个基础指标如下:TP(true positive):真实值是positive,预测值也是positive的值数量
TN(true negative):真实值是negative,预测值也是negative的值数量
FP(false positive):真实值是negative,预测值是positive的值数量
FN(false negative):真是只是positive,预测值是negative的值数量
将这四个指标合并在一个表格中,就是:
再此基础上,又衍生了如下四个指标:准确率ACC:所有判断正确的结果占总的观测值比重
精确率PPV:在预测值是positive中,预测对的占比
灵敏度TPR:在真实值是positive中,预测对的占比
F1 score:综合了precision与recall的产出的结果。取值范围从[0,1],1代表模型的输出最好(全部预测对),0代表模型的输出结果最差
python代码实现绘制混淆矩阵
def confusion_matrix_(y_pred,y_true):
'''params:y_pred:预测值y_true:真实值result:绘制混淆矩阵和准确率、精确率、F1值'''
CM = confusion_matrix(y_true,y_pred)
fig = plt.figure(figsize=(12,6))
fig.suptitle(r'Confusion Matrix')
#画热力图
ax1 = fig.add_subplot(1,2,1)
sns.heatmap(CM,annot=True,cmap='Blues',linewidths=0.5,ax=ax1)
ax1.set_xlabel('predicate')
ax1.set_ylabel('true')
#文本显示指标
(tn,fp,fn,tp) = CM.ravel()
acc = format_2(accuracy_score(y_true, y_pred))
pre0 = format_2(tn/(fn+tn)); pre1 = format_2(tp/(tp+fp))
recall0 = format_2(tn/(fp+tn)); recall1 = format_2(tp/(tp+fn))
f10 = format_2(2*tn/(2*tn+fp+fn))
f11 = format_2(2*tp/(2*tp+fp+fn))
col_labels = ['precision','recall','F1']
row_labels = [' '*4+'0'+' '*4,' '*4+'1'+' '*4]
table_vals = [[pre0,recall0,f10],[pre1,recall1,f11]]
ax2 = fig.add_subplot(1,2,2)
ax2.table(cellText=table_vals,rowLabels=row_labels,
colLabels=col_labels,loc='center')
ax2.text(0.35,0.6,
r'''accuracy:{acc}'''.format(acc=acc),
horizontalalignment='center',
verticalalignment='center',
fontsize=20, color='black',
transform=ax2.transAxes)
ax2.axis('off')
ax2.set_xticks([])
ax2.set_yticks([])
plt.show()
总结以上指标都是在模型实现了分类的基础上计算的
accuracy计算简单,但是很容易受样本不均衡的影响。比如,100个样本,正样本90,全部预测为正样本,accuracy=90%,显然,这样的模型是无意义的
precision和recall很难放在一起衡量模型。因为在实际情况中,要增加precision,模型严格一点,只把很确认的值判断为positive,这样recall就很低。相反的,提高recall,尽可能多的判为positive,这样precision就会降低。为了均衡鱼与熊掌,F1 score就是对他们做了一个调和。
precision和recall这两个指标在使用的时候,要根据实际场景选择。比如地震预测,就可以牺牲precision要高的recall,嫌疑人定罪问题就要搞得precision。
ROC/AUC
真正率 TPR(true positive rate):真实值是positive中预测正确的占比
假正率 FPR(false negative rate)真实值是negative中预测错误的占比
我们根据学习器的预测结果,把阈值从0变到最大,即刚开始是把每个样本作为正例进行预测,随着阈值的增大,学习器预测正样例数越来越少,直到最后没有一个样本是正样例。在这一过程中,每次计算出TPR和FPR,分别以它们为横、纵坐标作图,就得到了 ROC曲线。
AUC就是ROC曲线下的面积。AUC是指随机给定一个正样本和一个负样本,分类器输出该正样本为正的那个概率值比分类器输出该负样本为正的那个概率值要大的可能性。所以AUC反应的是分类器对样本的排序能力。
python代码实现auc曲线绘制
def auc_roc_ks_(y_predproab,y_true,plot=True):
'''
params:
y_predproab:预测值概率
y_true:真实值
plot:是否绘制roc曲线,默认绘制
result:
绘制roc曲线,并显示auc和ks
'''
fpr,tpr,threshold = roc_curve(y_true,y_predproab)
auc_ = format_4(auc(fpr,tpr))
ks = format_4(max(abs(tpr - fpr)))
if plot:
plt.figure(figsize=(6,6))
plt.title('ROC_AUC_KS',fontsize=25)
plt.plot(fpr,tpr,color='navy',lw=2,
label='auc:{}'.format(auc_))
plt.plot([0,1],[0,1],color='darkorange',lw=2,linestyle='--')
#标记ks
gap = abs(fpr-tpr)
plt.plot(fpr,gap,linewidth=2,color='r',label='ks:{}'.format(ks))
plt.xlabel('False Positive Rate',fontsize=20)
plt.ylabel('True Positiobe Rate',fontsize=20)
plt.legend(loc='best')
plt.show()
else:
print('auc:{auc_}\nks:{ks}'.format(auc_=auc_,ks=ks))
总结ROC曲线反映模型在选取不同阈值时的敏感性和精确性,所以能很容易查出任意阈值对学习起的泛化性能影响,有助于选择最佳阈值
ROC曲线越靠近左上角,模型的查全率越高。
测试集样本分布发生变化时,ROC曲线能够保持不变。因为ROC、AUC同时考虑了学习器对正、负样本的分类能力,所以对样本是否分布均衡并不敏感。
ROC、AUC都只适合于二分类
KS
ks用于对模型风险区分能力进行评估。计算好坏样本的累计差异,差异最大值为ks。ks值越大,模型的风险区分能力越强。
计算步骤按照模型的结果对每个账户进行打分
所有账户按照评分排序,从小到大分为10组(或20组)
计算每个评分区间的好坏账户数。
计算每个评分区间的累计好账户数占总好账户数比率(good%)和累计坏账户数占总坏账户数比率(bad%)。
计算每个评分区间累计坏账户占比与累计好账户占比差的绝对值(累计bad%-累计good%),然后对这些绝对值取最大值即得此评分模型的K-S值。
python代码计算ks
def calc_ks(y_predproab,y_true):
'''
params:
y_predproab:预测值概率/正样本分组
y_true:真实值/正负样本标记label
return:
ks
'''
fpr,tpr,threshold = roc_curve(y_true,y_predproab)
ks = format_4(max(abs(tpr - fpr)))
return ks
GINI系数
Gini系数是20世纪初意大利学者科拉多·基尼根据劳伦茨曲线所定义的判断年收入分配公平程度的指标。在模型评价中,Gini统计值衡量坏账户数在好账户数上的的累积分布与随机分布曲线之间的面积,好账户与坏账户分布之间的差异越大,GINI指标越高,表明模型的风险区分能力越强。
纵坐标是tpr,在信用评分模型中就是坏用户率,横坐标是(tp+fp)/(tp+fp+tn+fn) ,总样本中预测为正例的占比。中间的对角线表示样本模型的随机分布,也可以称之为绝对公平线。曲线(洛伦兹曲线)是使用模型的真实累积分布情况,阈值越低,预测值中正例占比越高,tpr也越大。
Gini系数的计算
当正样本占比很小的时候,tp和fn可忽略不计,图形的横坐标变为fpr,洛伦兹曲线接近于ROC曲线,auc = A + 0.5,gini=2*AUC-1成立
当样本分布均衡的时候,用高数的积分计算
python代码计算ks
def gini_(y_predproab, y_true):
'''
params:
y_predproab:预测值概率/正样本分组
y_true:真实值/正负样本标记label
return:
gini系数
'''
a = np.asarray(np.c_[y_true, y_predproab, np.arange(len(y_true))], dtype=np.float)
a = a[np.lexsort((a[:,2],-1*a[:,1]))]
#根据等高梯形求曲线面积
totalLosses = a[:,0].sum()
giniSum = a[:,0].cumsum().sum()/totalLosses
#计算阴影面积
giniSum -= (len(y_true)+1)/2.
return giniSum/len(y_true)
def calc_gini(y_predproab, y_true):
'''
params:
y_predproab:预测值概率/正样本分组
y_true:真实值/正负样本标记label
return:
gini:按照预测值排序gini系数
ginimax:按照真实值排序给gini系数
gini/ginimax
'''
gini = gini_(y_predproab,y_true)
ginimax = gini_(y_true,y_true)
return format_4(gini),format_4(ginimax),format_4(gini/ginimax)
Lift/Gain
Lift曲线的衡量的是模型通过某个阈值划定预测结果的命中率,对比不用模型随机划定结果的命中率的提升度。简单来说,就是相较于不用这个模型,用了模型后,效果提升了多少。
不使用模型positive的占比:正样本占总样本的占比,作为模型的baseline。代表不使用模型,自然分类的效果。
使用后模型的占比:预测为positive的样本中分类正确的占比
Gain与Lift 相当类似。Lift chart是不同阈值下Lift和Depth(样本中预测为正例的占比,也是图中数据的百分位,data sets)的轨迹,Gain chart是不同阈值下PV+和Depth的轨迹,而PV+=lift*pi1= tp/(tp+fp),所以它们显而易见的区别就在于纵轴刻度的不同:
python代码绘制lift/gain曲线
def plot_lift(y_predproab, y_true):
'''
params:
y_predproab:预测值概率/正样本分组
y_true:真实值/正负样本标记label
result:
绘制lifi曲线
'''
result = pd.DataFrame([y_true,y_predproab]).T
result.columns = ['target','proba']
result = result.sort_values(['proba','target'],ascending=False).reset_index()
del result['index']
result.set_index((result.index+1)/result.shape[0],inplace=True)
result['bad_sum'] = result['target'].cumsum()
result['count_sum'] = [i+1 for i in range(result.shape[0])]
result['rate'] = result['bad_sum']/result['count_sum']
result['lift'] = result['rate']/(result['target'].sum()/result.shape[0])
fig = plt.figure(figsize=(12,6))
ax1 = fig.add_subplot(1,2,1)
ax1.grid(True,linestyle='-.')
ax1.plot(result['rate'],color='red',label='Lift model')
ax1.plot(result.index,[result['target'].sum()/result.shape[0]]*result.shape[0],color='blue',label='Lift random')
ax1.set_title('Lift Chart',fontsize=25)
ax1.set_ylabel('tp/(tp+fp)',fontsize=20)
ax1.set_xlabel('data sets',fontsize=20)
ax1.set_xticks([i/10 for i in range(11)])
plt.legend(loc='best')
ax2 = fig.add_subplot(1,2,2)
ax2.plot(result['lift'],color='darkorange')
ax2.grid(True,linestyle='-.')
ax2.set_title('Cumulative Lift Chart',fontsize=25)
ax2.set_ylabel('lift',fontsize=20)
ax2.set_xlabel('data sets',fontsize=20)
ax2.set_xticks([i/10 for i in range(11)])
plt.show()
PSI
PSI(Population Stability Index) 群体稳定性指标
PSI表示样本在分组后,针对不同样本或者不同时间样本,population是否发生了变化,即看各个分数区间内人数占总人数的占比是否有显著变化。
例如,在建模前,对于时间跨度大的样本,对用户分组,按照时间区间将第一个样本时间作为期望值,可衡量样本量是否稳定。
最常用的还是衡量测试样本及模型开发样本评分的的分布差异。按分数分档后,针对不同样本,或者不同时间的样本,看人数占比是否明显。
PSI小于0.1时候模型稳定性很高,0.1-0.2一般,需要进一步研究,大于0.2模型稳定性差,建议修复。
计算步骤统计总进件量(数据库统计),如总进件量为3400条数据
将总进件量按区间统计出各分区间量
算出各区间占比(本区间数/总进件数)
设定期望占比
计算各区间psi并最终进行求和
关于作者
csdn:CSDN-专业IT技术社区-登录blog.csdn.net