模型评价指标—KS

向量数据库大模型机器学习

对于 分类模型 ,在建立好模型后,我们想 对模型进行评价 ,常见的指标有混淆矩阵、KS曲线、ROC曲线、AUC面积等。也可以自己定义函数,把模型结果分割成n(100)份,计算top1的准确率、覆盖率。

之前阐述了混淆矩阵,本文阐述 KS的原理和Python实现实例 ,其它指标会在后续文章中详尽阐述,敬请期待picture.image

本文目录

  1. 详细介绍KS

1.1 什么是KS

1.2 理解KS的一个小例子 2. 用Python如何计算KS值并绘图

2.1 具体代码

2.2 具体实例 3. 如何评价KS

一、详细介绍KS

picture.image

1 什么是KS

KS (Kolmogorov-Smirnov):好坏样本之间累计分布的差值(最大值),用于评估模型的风险区分能力。

好坏样本的累计差异越大,模型的风险区分能力越强,KS指标越大。

2 理解KS的一个小例子

为了便于理解,举一个通俗易懂的小例子(非实际情况)。

现假设有两百个样本,其中100个为逾期客户(标记为1),100个为正常客户(标记为0)。计算模型KS值的步骤如下:

  • step1: 用这两百个样本训练一个模型(可以是逻辑回归、GBDT等),得到两百个样本预测为逾期的prob。
  • step2:把两百个样本根据prob从高到低排序。
  • step3:把样本均分成10组/20组等(最多每个样本是一组,分成两百组)。
  • step4:统计每个组别中逾期客户数量/正常客户数量。
  • step5:统计每个组别中累计逾期客户数量占比/累计正常客户数量占比。
  • step6:计算每个组别中abs(累计逾期客户数量占比-累计正常客户数量占比)。
  • step7:找到累计占比差值绝对值最大的数,即为所求的KS值。

表格形式如下:

picture.image

上表把200个样本按prob从大到小排序,按数量均分成10组。统计每组中逾期客户数占总逾期客户数的比例,以及每组中正常客户数占总正常客户数的比例。

每一组的KS i 为逾期客户累计占比和正常客户累计占比差值的绝对值,最大值0.52即为该模型的KS值,在pop=0.4处取得。

从上表可以发现,逾期客户的prob相对较高,正常客户的prob相对低,即好坏样本的累计分布之间存在差异。

思考一个极端情况,所有逾期客户的prob都高于正常客户的prob,那意味着模型的KS趋近于1,或者为1(分组够细)。

这时,说明模型能完全区分出正常客户和逾期客户。

二、用Python如何计算KS值并绘图

picture.image

1 具体代码

在python中计算KS的具体代码如下:

  
import matplotlib  
import pandas as pd  
import seaborn as sns  
from pandas import Series  
import matplotlib.pyplot as plt  
from sklearn.metrics import roc_curve  
from sklearn.pipeline import make_pipeline  
sns.set(font='SimHei') #解决Seaborn中文显示的问题  
matplotlib.rcParams['font.family']='SimHei'  
plt.rcParams['font.sans-serif'] = ['SimHei'] #中文字体设置-黑体  
plt.rcParams['axes.unicode_minus'] = False #解决保存图像是负号'-'显示为方块的问题  
from sklearn.metrics import roc_curve  
  
def PlotKS(preds,labels,n,asc):  
 #preds is score:asc=1 preds is prob:asc=0  
 pred=preds #预测值  
 bad=labels #1为bad,0为good  
 ksds=pd.DataFrame({'bad':bad,'pred':pred})  
 ksds['good']=1-ksds.bad   
 if asc==1:  
 ksds1=ksds.sort_values(by=['pred','bad'],ascending=[True,True])  
 if asc==0:  
 ksds1=ksds.sort_values(by=['pred','bad'],ascending=[False,True])  
 ksds1.index=range(len(ksds1.pred))  
 ksds1['cumsum_good1']=1.0*ksds1.good.cumsum()/sum(ksds1.good)  
 ksds1['cumsum_bad1']=1.0*ksds1.bad.cumsum()/sum(ksds1.bad)   
 if asc==1:  
 ksds2=ksds.sort_values(by=['pred','bad'],ascending=[True,False])  
 if asc==0:  
 ksds2=ksds.sort_values(by=['pred','bad'],ascending=[False,False])  
 ksds2.index=range(len(ksds1.pred))  
 ksds2['cumsum_good2']=1.0*ksds2.good.cumsum()/sum(ksds2.good)  
 ksds2['cumsum_bad2']=1.0*ksds2.bad.cumsum()/sum(ksds2.bad)   
   
 #ksds1,ksds2->average  
 ksds=ksds1[['cumsum_good1','cumsum_bad1']]  
 ksds['cumsum_good2']=ksds2['cumsum_good2']  
 ksds['cumsum_bad2']=ksds2['cumsum_bad2']  
 ksds['cumsum_good']=(ksds1['cumsum_good1']+ksds2['cumsum_good2'])/2  
 ksds['cumsum_bad']=(ksds1['cumsum_bad1']+ksds2['cumsum_bad2'])/2  
   
 #ks  
 ksds['ks']=ksds['cumsum_bad']-ksds['cumsum_good']  
 ksds['tile0']=range(1,len(ksds.ks)+1)  
 ksds['tile']=1.0*ksds['tile0']/len(ksds['tile0'])  
   
 qe=list(np.arange(0,1,1.0/n))  
 qe.append(1)  
 qe=qe[1:]   
 ks_index=Series(ksds.index)  
 ks_index=ks_index.quantile(q=qe)  
 ks_index=np.ceil(ks_index).astype(int)  
 ks_index=list(ks_index)   
 ksds=ksds.loc[ks_index]  
 ksds=ksds[['tile','cumsum_good','cumsum_bad','ks']]  
 ksds0=np.array([[0,0,0,0]])  
 ksds=np.concatenate([ksds0,ksds],axis=0)  
 ksds=pd.DataFrame(ksds,columns=['tile','cumsum_good','cumsum_bad','ks'])   
 ks_value=ksds.ks.max()  
 ks_pop=ksds.tile[ksds.ks.idxmax()]  
 print('ks_value is '+ str(np.round(ks_value,4))+' + at pop = '+ str(np.round(ks_pop,4)))  
   
 #chart  
 plt.plot(ksds.tile,ksds.cumsum_good,label='cum_good',color='blue', linestyle='-',linewidth=2)  
 plt.plot(ksds.tile,ksds.cumsum_bad,label='cum_bad',color='red', linestyle='-',linewidth=2)  
 plt.plot(ksds.tile,ksds.ks,label='ks',color='green', linestyle='-',linewidth=2)  
   
 plt.axvline(ks_pop,color='grey',linestyle='--')  
 plt.axhline(ks_value,color='green',linestyle='--')  
 plt.axhline(ksds.loc[ksds.ks.idxmax(),'cumsum_good'],color='blue',linestyle='--')  
 plt.axhline(ksds.loc[ksds.ks.idxmax(),'cumsum_bad'],color='red',linestyle='--')  
 plt.title('KS=%s' %np.round(ks_value,4)+  
 'at Pop=%s' %np.round(ks_pop,4),fontsize=15)  
 return ksds

2 具体实例

为了便于理解,举一个具体实例(造的数据):

  
y_1 = y.astype(int)  
PlotKS(y_proba_model_1[:,1],y_1,10,0)

注:如果需要实现数据可以在公众号中回复 “KS值” ,即可免费获取。

y_proba_model_1[:,1]:表示模型预测样本逾期的prob。

y_1:表示模型的实际标签,逾期客户标记为1,正常客户标记为0。

10:表示分成10组。

0:表示输入的是prob。如果输入的是score,对应位置改为1即可。

得到结果如下:

ks_value is 0.354 + at pop = 0.3002

picture.image

picture.image

三、如何评价KS

picture.image我们计算出了模型的KS,那么多少的KS值,模型才是可以使用的?

根据行业内的规范,一般KS值要大于0.2才是一个可用的模型,且KS值越大模型效果越好。

但是,KS值过高,需核验模型是否使用未来变量,要谨慎使用。

具体KS值对应的模型区别能力见下表:

picture.image

跟大家分享一个我实际建模的实例:

有一个模型在训练集上的KS值在0.85左右。根据之前看的资料,我很担心模型的KS值过高,实际应用时效果会比较差。

但在实际上线后,模型的效果表现很好。在大数据建模中,从海量商户中捞风险商户,prob>0.9的商户准确率可以高于90%。

所以,不是模型的KS值过高,就要过分怀疑模型的效果,要根据实际情况再做定夺。

往期回顾:

3D星空图

3D星空图V2版

520表白代码合集

用python绘制皮卡丘

娱乐圈排行榜动态条形图绘制

picture.image

picture.image

扫一扫关注我

19967879837

投稿 微信号、手机

0
0
0
0
关于作者

文章

0

获赞

0

收藏

0

相关资源
字节跳动大数据容器化构建与落地实践
随着字节跳动旗下业务的快速发展,数据急剧膨胀,原有的大数据架构在面临日趋复杂的业务需求时逐渐显现疲态。而伴随着大数据架构向云原生演进的行业趋势,字节跳动也对大数据体系进行了云原生改造。本次分享将详细介绍字节跳动大数据容器化的演进与实践。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论