利用Logistic回归拟合信用评分卡模型

一、关于互联网金融授信产品的风控建模

如何利用机器学习以及大数据技术来降低风险呢?如何建立信用评分的模型呢?
本文将针对这些问题简单介绍互金行业中授信产品的风控建模过程,内容主要如下:
·信用风险定义
·信用风险评分卡类型
·信用评分模型建立的基本流程

1.信用风险定义

①风险管理的概念
风险管理最早起源于美国。1930年由美国管理协会保险部最先倡导风险管理,后面在全球流行开来,随着互联网的迅猛发展,大数据、数据挖掘和机器学习等新兴技术开始出现,让风险管理更为精准。
他们通过收集银行系统本身的征信数据以及用户在互联网上的的各种数据,包括人际关系、历史消费行为、身份特征等,通过大数据“画像”技术,对用户进行全面的定位,由此来预测用户的履约能力、降低信贷风险。

②什么是信用风险?
信用风险又称违约风险,是指借款人、证券发行人或交易对方因种种原因,不愿或无力履行合同条件而构成违约,致使银行、投资者或交易对方遭受损失的可能性。即受信人不能履行还本付息的责任而使授信人的预期收益与实际收益发生偏离的可能性,它是金融风险的主要类型。
万事都有风险,但对于金融行业来讲,风险控制尤为重要。对于海量的用户数据处理,传统的人工授信方式显然是很乏力的,因此现在大多互联网金融P2P公司都采用机器学习、大数据等技术对风险进行自动化评估,来最大程度的降低风险。当然,这些技术的应用并不能百分百的保证零风险,因为有很多人为因素是不可控的,但是信用风控技术在很大程度上帮助金融企业进行了很好的风险管控,通过降低风险减少损失来间接增加利润。

2.信用风险评分卡类型

①信用评级
用过信用卡的朋友都知道,开卡需要申请(筛选好坏用户),消费了就需要定期进行债务偿还,如果不偿还就有人发短信催你。因此,信用评级可根据用户的整个使用周期分为以下四种类型:
1)申请者评级(Application):个人客户申请相应金融产品,对用户进行筛选分类,判断时好时坏,是否通过申请(A卡)
2)行为评级(Behavier):个人客户通过申请后在使用期间的历史行为数据进行评级,对客户可能出现的逾期、延期等行为进行预测(B卡)
3)催收评级(Collection):对业务中存量客户是否需要催收的预测(C卡)
4)欺诈评级(Fraud):业务中新客户可能存在的欺诈行为的预测(F卡)
每个评级阶段的模型都是不一样的,因为每个阶段的用户显现的特征都不一样,因此需要针对各个阶段进行单独的模型开发。

②信用评分卡
尽管有了评级分类,但是信用对于我们来说仍然是一个比较抽象的概念,因此可以通过量化的方式来更直观的使用信用,而分数是一种不错的量化方式,通过分数的高低来衡量风险概率,分数越高代表信用越好。信用评分卡就是通过大数据的统计分析,根据用户的各种资料信息,对用户信用进行评估(打分)。根据以上信用评级,相应的可以分为四种评分卡:
1)申请评分卡(A卡)
2)行为评分卡(B卡)
3)催收评分卡(C卡)
4)欺诈评分卡(F卡)
是一种以分数的形式来衡量风险几率的一种手段,也是对未来一段时间内违约、逾期、失联概率的预测。一般来说,分数越高,风险越小。
A卡一般可做贷款0-1年的信用分析,B卡则是在申请人有了一定行为后,有了较大数据进行的分析,一般为3-5年,C卡则对数据要求更大,需加入催收后客户反应等属性数据。每种评分卡的模型会不一样。在A卡中常用的有逻辑回归,AHP等,而在后面两种卡中,常使用多因素逻辑回归,精度等方面更好。 基于个人借贷的场景,确定“违约”的定义: 根据新的Basel II Capital Accord(巴塞尔二资本协议),一般逾期90天算作违约

3. 信用评分建模的基本流程
通过对消费者的人口特征、信用历史记录、交易记录等大量数据进行系统的分析、挖掘数据蕴含的行为模式、信用特征,发展出预测行的模式,结合信用卡评分的构建原理,完成数据的清洗,主要包括缺失数据的填充、异常的删除和数据的分箱;调用Logistic回归模型建立信用卡评分的基础模型,借助自变量的证据权重转换(WOE)创建信用卡评分卡,并开发一个简单的信用评分系统。
在开发信用风险模型之前,首先要明确我们需要解决的问题,确定评分卡模型的类别。下面将对申请评分卡建模(主要目的是区分好坏客户)流程进行简单的介绍。

①数据获取
除了企业内部自有的数据外,还有第三方机构数据支持,比如芝麻信用,征信局等。通过大数据分析用户的各种数据来达到最终目的,数据维度很广,可以包括:用户基础属性,用户行为,用户网购,用户APP行为等。在数据质量不差的情况下,数量越多越好,能留的一个不落下,后续再进行甄别筛选。
这部分的技术栈主要有:Mysql,Hive,Hbase,Spark,Python等。

②EDA数据探索
数据探索也是很重要的一步,主要考察数据的质量,包括:数据缺失值,数据异常值,数据一致性,数据分布特征,以及数据之间的关联性等。通常可以使用描述性统计指标,如均值,中位数,众数,方差/标准差等进行宏观上的度量,也可以使用可视化方法辅助进行数据分布,以及关联性等的初步分析工作。
1)缺失值处理
根据缺失情况(是否随机,以及缺失量等)可选择使用均值,众数,中位数等填充,也可以用机器学习模型来填充缺失值(常见算法有随机森林,决策树,kNN等)。
2)异常值处理
可根据异常的情况考虑使用3∂原则,箱线图,散点图,基于距离,基于密度,基于聚类等一系列的方法进行离群点检测。对于异常值的处理可以采用移除,平均值修正,视为缺失值,或者不处理等。
3)数据分布以及关联性
可以考虑结合可视化的方法进一步的观察:数据分布是否均衡,数据特征之间的联系,以及数据特征与目标变量之间的联系等进行了解

③数据预处理
数据预处理主要包括特征转换,特征编码,特征选择,特征共线性处理,以及创建衍生变量等一系列的处理方法。
1)特征转换和编码
在信用评分模型的变量选择中,如果使用逻辑回归模型,那么就需对所有特征进行分箱离散化(一般是先细分再粗分),这样可以增加模型对非线性的表达,让模型更稳定。然后再进行woe编码,因为woe的转换公式与逻辑回归模型上非常相似,便于生成评分系统。
2)特征选择
特征选择,在数据中是非常中重要,目的在于帮助我们挑选出最有意义的特征。选择特征最终目的是挑选能区分好用户或坏用户的强相关特征。
根据所使用的模型可以通过基尼系数或信息价值IV找到显著特征项,也可以通过LASSO、LR、RF模型等对特征做重要性的筛选。当然,还有很多其它的方法,这里仅介绍这几种。
a)IV:基于woe编码,可以衡量特征信息重要程序;
b)LASSO:主要适合基于L1的正则惩罚过滤对区分好坏用户不重要的特征;
c)LR:通过拟合的参数排序得到特征的重要性程度;
d)RF:集成学习(bagging),依据算法的附加功能进行特征的重要性排序;
最后要说的是,特征选择要结合业务,根据业务的理解挑选解释型强且权重较大的特征变量。

④模型建立
模型建立会根据实际情况进行选择,比如是否要使用单模型,或者在单模型中各种模型好坏的比较而最终确认。
在信用评分卡建模中,用到最常用的方法就是逻辑回归(LR)。虽然是传统的模型,但是由于其自身特点,加上自变量进行了证据权重转换(WOE),Logistic回归的结果可以直接转换为一个汇总表,即所谓的标准评分卡格式,这对于区分好坏用户以及评分卡的建立非常适用。目前对于它的使用和部署上线等已经非常成熟,是很多企业的不二选择。
除了LR外,神经网络,Xgboost等高级模型也会被使用,不过综合考虑LR目前能够满足大部分的需求且部署上线容易。

⑤模型评估(对于离散型因变量)
针对信用评分卡应用的评估模型有很多,包括:ROC/AUC,KS,PSI,LIFT等一些评估方法,下面着重介绍两个ROC和KS值。

1)混淆矩阵
以肿瘤为例,对于实际的数据集存在两种分类,即良性和恶性,基于Logistic回归模型会预测出样本所属的类别,得到两列数据:真实地;预测的,将两个序列得到一个汇总的列联表,即混淆矩阵。

0表示良性(负例),1表示恶性(正例,一般被理解为研究者所感兴趣或者关心的那个分类),

A:表示正确预测负例的样本个数,TN
A+B:表示预测负例的样本个数,PN
准确率:(A+D)/(A+B+C+D),用来衡量模型对整体数据的预测效果,用Accuracy表示
正例覆盖率:D/(B+D),反映模型能够在多大程度上覆盖所关心的类别,即TPR=TP/P,称为灵敏度(sensitivity)/召回率(recall)
负例覆盖率:A/(A+C),即TNR=TN/N称为特指度(specicity)
正例命中率:D/(C+D),即(TP+TN)/T ,表示分类器预测正确的比例称正确率(accuracy)
一般准确率、正例覆盖率、负例覆盖率越高,模型越理想

还有,FPR=FP/P称误警率(Fallout),FNR=FP/N称为漏查率(miss).
分类器预测错误的比例称错误率(error rate):(FP+FN)/T

又可以定义下面两个比率: TPR又可称为查全率,表示正确分类的正例占实际正例(TP/(TP+FN))的比例,用于衡量分类器预测正例的可信程度。 相对应的概念有查准率(precision),表示正确分类的正例占全部预测正例的比例(TP/(TP+FP))。

2)ROC/AUC
通常一个二值分类器可以通过ROC(Receiver Operating Characteristic)曲线和AUC值来评价优劣。

很多二元分类器会产生一个概率预测值,而非仅仅是0-1预测值。我们可以使用某个临界点/阈值(例如0.5),以划分哪些预测为1,哪些预测为0。得到二元预测值后,可以构建一个混淆矩阵来评价二元分类器的预测效果。所有的训练数据都会落入这个矩阵中,而对角线上的数字代表了预测正确的数目,即true positive + true nagetive。同时可以相应算出TPR(正例覆盖率或称为真正率或称为灵敏度)和TNR(负例覆盖率或称为真负率或称为特异度)。我们主观上希望这两个指标越大越好,但可惜二者是一个此消彼涨的关系。除了分类器的训练参数,临界点的选择,也会大大的影响TPR和TNR。有时可以根据具体问题和需要,来选择具体的临界点。

如果我们选择一系列的临界点/阈值,就会得到一系列的TPR和TNR,将这些值对应的点连接起来,就构成了ROC曲线。ROC曲线可以帮助我们清楚的了解到这个分类器的性能表现,还能方便比较不同分类器的性能。在绘制ROC曲线的时候,习惯上是使用1-TNR作为横坐标即FPR(false positive rate),TPR作为纵坐标。这是就形成了ROC曲线。

而AUC(Area Under Curve)被定义为ROC曲线下的面积,显然这个面积的数值不会大于1。又由于ROC曲线一般都处于y=x这条直线的上方,所以AUC的取值范围在0.5和1之间。使用AUC值作为评价标准是因为很多时候ROC曲线并不能清晰的说明哪个分类器的效果更好,而作为一个数值,对应AUC更大的分类器效果更好。

ROC基于混淆矩阵,对于数据类别不均衡有很好的效果。ROC曲线使用两个指标值进行绘制,其中x轴为1-Specificity,即负例错判率;y轴为Sensitivity,即正例覆盖率。ROC值一般在0.5-1.0之间。值越大表示模型判断准确性越高(曲线越偏左上方越好),即越接近1越好。ROC=0.5表示模型的预测能力与随机结果没有差别,AUC代表曲线下的面积,不依赖于阈值,AUC值越高,模型的风险区分能力越强。通常AUC在0.8以上时,模型基本可以接受了。

提升度和提升曲线
分类器分类为正例的比例称为深度(depth):(TP+FP)/T,T是全部待判样本数量。

提升度(lift)等于TPR/depth 以深度为横轴,以提升度为纵轴绘制曲线,得到提升曲线。 绘制提升曲线的思路和ROC类似。当阈值为0的时候,所有的样本都会判为正例,此时深度为1,提升为1;随阈值增大,深度减小,提升随之逐渐增大。一个好的模型要在大的深度下得到尽量大的提升。

3)KS值
KS值表示了模型正负区分开来的能力。值越大,模型的预测准确性越好。一般,KS>0.4即可认为模型有比较好的预测准确性,KS值只能反映出哪个分段是区分最大的,而不能总体反映出所有分段的效果。

具体步骤为:
按照模型计算score值,从大到小排序;
取出10%,20%,..,90%所对应的分位数,并以此作为score的阈值,计算Sensitivity和1-Specificity的值,将10%、20%、..、90%这样的分位点用作绘图的x轴,将Sensitivity和1-Specifity两个指标值用作绘图的y轴,进而得到两条曲线;
分别代表各分位点下的正例覆盖率和1-负例覆盖率,一般选用最大的KS值作为衡量指标;
KS=Sensitivity-(1-Specificity),通常KS>0.4即可认为模型有比较好的预测准确性

下面是一个真实的在线授信产品的风控建模的流程图,可参考进行理解:



二、数据获取

1.数据描述
数据属于个人消费类贷款,只考虑评分卡最终实施时能够使用到的数据应从如下一些方面获取数据:
基本属性:包括了借款人当时的年龄。
偿债能力:包括了借款人的月收入、负债比率。
信用往来:两年内35-59天逾期次数、两年内60-89天逾期次数、两年内90天或高于90天逾期的次数。
财产状况:包括了开放式信贷和贷款数量、不动产贷款或额度数量。
贷款属性:暂无。
其他因素:包括了借款人的家属数量(不包括本人在内)。

2.数据导入以及给列重命名

rm(list = ls())
setwd('F:\\kaggle\\评分卡模型')
getwd()
data<-read.csv('cs-training.csv',header = T,stringsAsFactors = F)
str(data)
colnames(data)<-c("id","y","x0","x1","x2","x3","x4","x5","x6","x7","x8","x9")
attach(data)
summary(data)

通过summary了解数据的整体情况,可以看到x4和x9变量有缺失值,即MonthlyIncome变量和NumberOfDependents两个变量存在缺失值,monthlyincome列共有缺失值29731个,numberofdependents有3924个。

三、数据预处理

1.缺失值分析及处理
在正式分析前,我们先通过图形进行对观测字段的缺失情况有一个直观的感受。

library(mice)
md.pattern(data)


同样得出monthlyincome(X4)列共有缺失值29731个,numberofdependents(X9)有3924个。由于MonthlyIncome(X4)缺失值达到29731条数据,比例较大,因此不能直接将缺失值删除,选择随机森林法。而NumberOfDependents(X9)的缺失较少,对数据影响不大,因此直接删除。对于缺失值的处理方法非常多,例如基于聚类的方法,基于回归的方法,基于均值的方法,在这里,我们使用mean方法对缺失值进行填补。

#保留x9中不是缺失值的数据
data<- data[!is.na(x9),]
x4_var<-c(var="x4",mean=mean(x4,na.rm=TRUE),median=median(x4,na.rm=TRUE),quantile(x4,c(0,0.01,0.1,0.25,0.5,0.75,0.9,0.99,1),na.rm=TRUE),max=max(x4,na.rm=TRUE),missing=sum(is.na(x4)))
View(t(x4_var))
#用mean填补x4的缺失值
x4<-ifelse(is.na(x4)==T,6670.2,x4)
#或者
library(Hmisc)
impute(x4, mean)  # 均值替代

2.异常值处理
异常值是指明显偏离大多数抽样数据的数值,比如个人客户的年龄大于100或小于0时,通常认为该值为异常值。找出样本总体中的异常值,通常采用离群值检测的方法。 离群值检测的方法有单变量离群值检测、局部离群值因子检测、基于聚类方法的离群值检测等方法。在本数据集中,采用单变量离群值检测来判断异常值,采用箱线图。常把低于 Q1-1.5IQR的值和高于Q3+1.5IQR的值作为异常值。通过绘制箱型图能很明显的看到异常值,
处理异常值:通常采用盖帽法,即用数据分布在1%的数据覆盖在1%以下的数据,用在99%的数据覆盖99%以上的数据。

block<-function(x,lower=T,upper=T){
  if(lower){
    q1<-quantile(x,0.01)
    x[x<=q1]<-q1
  }
  if(upper){
    q99<-quantile(x,0.99)
    x[x>q99]<-q99
  }
  return(x)
}

对于age变量而言,我们认为大于100岁小于等于0岁的为异常值,由箱线图可知,异常值样本不多,故直接删除。

par(mfrow=c(2,1))
boxplot(x1,data=data,horizontal=T,frame=F,col="lightgray")
x1<-block(x1)
boxplot(x1,data=data,horizontal=T,frame=F,col="lightgray")
#col : 箱体的填充
#border : 箱体中线条的颜色,默认为黑色

可以看出经过盖帽法,大于100,小于等于0的值被删除
对于RevolvingUtilizationOfUnsecuredLines(可用额度比值x0)及DebtRatio(负债率x3)而言,箱线图如下图:

par(mfrow=c(1,2))
boxplot(x0,frame=F,ylab ="%")
abline(h=1,col="red")
data<-data[which(data$x0<=1),]
boxplot(x3,frame=F,ylab ="%")
abline(h=1,col="red")
data<-data[which(data$x3<=1),]

因为上述两变量的数值型为百分比,故大于1的值全部删除。
对于变量x2(逾期30-59天笔数)、x6(逾期90天笔数)、x8(逾期60-89天笔数做箱线图,由图可知,有两异常值点,数值为96、98,删除。
同时会发现剔除其中一个变量的96、98值,其他变量的96、98两个值也会相应被剔除。

par(mfrow=c(1,1))
boxplot(x2,x6,x8,data=data,frame=F)
data<-data[-which(data$x2==96),]
data<-data[-which(data$x2==98),]

四、探索性分析切分数据集

在建立模型之前,我们一般会对现有的数据进行 探索性数据分析(Exploratory Data Analysis) 。 EDA是指对已有的数据(特别是调查或观察得来的原始数据)在尽量少的先验假定下进行探索。常用的探索性数据分析方法有:直方图、散点图和箱线图等。
探索数据主要是为了分析各变量对输出结果的影响,在本项目中,主要关注的是违约客户与各变量间的关系。
1.单变量分析

hist(x1,freq = F,col="lightgreen")
lines(density(x1),col="red")

可以看到年龄变量大致呈正态分布,符合统计分析的假设。

library(ggplot2)
ggplot(data, aes(x = x4, y = ..density..)) + geom_histogram(fill = "blue", colour = "grey60", size = 0.2, alpha = 0.2) + geom_density() + xlim(1, 20000)


月收入也大致呈正态分布,符合统计分析的需要。
2.多变量分析
我们会用经过清洗后的数据看一下变量间的相关性。注意,这里的相关性分析只是初步的检查,进一步检查模型的IV(证据权重)作为变量筛选的依据。此处较简单,在此不赘述。
总之,数据处理的过程是占据整个标准评分卡构建的最大的工作量,整体的目标是:排除异常值对模型训练的干扰,将所有变量进行量化处理,自变量对因变量有明显的解释性,变量之间无明显相关性。
建模之前需要先检验变量之间的相关性,,如果变量之间具有强相关性,则会影响模型的准确性.调用R中的cor()函数来计算不同变量之间的相关系数,同时,调用corrplot包中的corrplot()函数来将相关系数可视化

cor1<-cor(data[,2:12])
library(corrplot)
corrplot(cor1)
corrplot(cor1,method = "number")

由上图可知:各个变量之间的相关系数较小,相关性较弱,不存在明显的多重共线问题,采用logistic回归需要考虑多重共线问题,不过此处由于各变量之间的相关性较小,可以初步判断不存在多重共线问题.在建模之后也可以通过VIF(方差膨胀因子)来检验多重共线问题.如果存在多重共线性,即有可能存在两个变量高度相关,需要降维或剔除处理,需要进行降维或剔除处理.

五、切分数据集

table(y)

由上表看出,对于响应变量SeriousDlqin2yrs,存在明显的类失衡问题,SeriousDlqin2yrs等于1的观测为9879,仅为所有观测值的6.6%。数据正负比例不平衡,因此我们需要对非平衡数据进行处理,在这里可以采用SMOTE算法,smote算法的思想是合成新的少数类样本,合成的策略是对每个少数类样本a,从它的最近邻中随机选一个样本b,然后在a、b之间的连线上随机选一点作为新合成的少数类样本。用R对稀有事件进行超级采样。
非平衡样本的解决方法
过采样 容易过拟合
欠采样 容易丢失信息
SMOTE算法 不能对有缺失值和类别变量做处理
SMOTE算法介绍:
采样K近邻
从K近邻中随机挑选N个样本进行随机线性插值
new=xi+rand(0,1)*(yj-xi),j=1…N
其中xi为少类中的一个观测点,yj为从K近邻中随机抽取的样本。

我们利用caret包中的createDataPartition(数据分割功能)函数将数据随机分成相同的两份。

set.seed(1234) 
library(caret)
splitIndex<-createDataPartition(y,time=1, p=0.5,list=FALSE) 
train<-data[splitIndex,] 
test<-data[-splitIndex,]
prop.table(table(train$y))
prop.table(table(test$y))

两者分类后的结果是平衡的,y等于1的概率均为6.0%左右,处于良好的水平,因此,可以采用切割后的数据进行建模和预测分析。

六、建立模型

Logistic回归在信用评分卡开发中起到核心作用。由于其特点,以及对自变量进行了证据权重转换(WOE),Logistic回归的结果可以直接转换为一个汇总表,即所谓的标准评分卡格式。
1.模型解释


2.建立模型
①首先利用glm函数对所有变量进行Logistic回归建模,模型如下

fit<-glm(y~.,train,family = "binomial")
summary(fit)

②可以看出,利用全变量进行回归,模型拟合效果并不是很好,其中id,x7三个变量的p值未能通过检验,在此直接剔除这两个变量,利用剩余的变量对y进行回归。

fit2<-glm(y~x0+x1+x2+x3+x4+x5+x6+x8+x9,train,family = "binomial")
summary(fit2)

第二个回归模型所有变量都通过了检验,所有模型的拟合效果更好些。
③使用逐步法剔除变量。

step(fit2, direction = "both")

可以看到没有变量被剔除
④VIF多重共线性检验

library(car)
library(carData)
vif(fit2)

一般认为VIF值大于2的话,表明变量间存在共线性。此时没有大于2的值,各个变量间相互独立
⑤预测

train_pred <- predict(fit2, data = train , type = "response")
test_pred <- predict(fit2, data = test , type = "response")

⑥模型评估
下面首先利用模型对test数据进行预测,生成概率预测值

test_prob <- predict(fit2, test)

调用R语言中pROC包中的roc函数计算分类器的AUC值,可以方便的比较两个分类器,并且自动标注出最优的临界点。

install.packages("pROC")
library(pROC)
modelroc <- roc(test$y,test_prob)
plot(modelroc, print.auc=TRUE, auc.polygon=TRUE, grid=c(0.1, 0.2),
     grid.col=c("green", "red"), max.auc.polygon=TRUE,
     auc.polygon.col="skyblue", print.thres=TRUE)

如图所示:最优点FPR=1-TNR=0.778,TPR=0.732,AUC值为0.832,说明该模型的预测效果不错,正确率较高。

(到这里为用户贷款预测模型)

七、特征变量选择

特征选择非常重要,好的特征能够构造出较好的模型,在此,我们采用信用卡评分模型常用的IV值筛选。
1.特征分箱
特征分箱指的是将连续变量离散化或将多状态的离散变量合并成少状态。离散特征的增加和减少都很容易,易于模型的快速迭代,离散化后的特征对异常数据有很强的鲁棒性,能够减少未离散化之前异常值对模型的干扰,同时离散化后可以进行特征交叉。此外本文所选的模型算法为逻辑回归,逻辑回归属于广义线性模型,表达能力受限;单变量离散化为N个后,每个变量有单独的权重,相当于为模型引入非线性,提升模型表达能力,加大拟合,同时也降低了模型过拟合的风险。特征分箱常用的有以下几种方法:有监督的有Best-KS,ChiMerge(卡分分箱),无监督的包括等频、等距、聚类。根据数据特征,针对不同数据采用不同分箱方式。
信用评分卡开发中一般有常用的等距分段、等深分段、最优分段。
如年龄,在外面的业务场景中年龄越小和年龄越大,违约概率都会偏大,所以这块需要做好分箱处理。

cutx1= c(-Inf,30,35,40,45,50,55,60,65,75,Inf)
plot(cut(train$x1,cutx1))
cutx2 = c(-Inf,0,1,3,5,Inf)
plot(cut(train$x2,cutx2))
cutx4 = c(-Inf,1000,2000,3000,4000,5000,6000,7500,9500,12000,Inf)
plot(cut(train$x4,cutx4))
cutx6 = c(-Inf,0,1,3,5,10,Inf)
plot(cut(train$x6,cutx6))
cutx8 = c(-Inf,0,1,3,5,Inf)
plot(cut(train$x8,cutx8))
cutx9 = c(-Inf,0,1,2,3,5,Inf)
plot(cut(train$x9,cutx9))


2.WOE值计算
在分箱的过程中,同时计算了WOE(Weight of Evidence)和IV(Information Value),前者在建立逻辑回归模型是需要将所有的变量转为WOE,而后者则可以很好的展示变量的预测能力。这两个值的计算方式如下:

证据权重(Weight of Evidence,WOE)转换可以将Logistic回归模型转化为标准评分卡格式,引入WOE转换的目的并不是为了提高模型质量,而是由于一些变量不应该被纳入模型,或者是因为它们不能增加模型值,或者是因为与其模型相关系数有关的误差较大,其实建立标准信用评分卡也可以不采用WOE转换。这种情况下,Logistic回归模型需要处理更大数量的自变量。尽管这样会增加建模程序的复杂性,但最终得到的评分卡都是一样的。
用WOE(x)替换变量x,WOE()=ln[(违约/总违约)/(正常/总正常)]。由于模型中剔除id,x7两个变量,因此对剩下的变量进行WOE转换。
WOE分箱原则:
1.分箱数量适中,不宜过多和过少。
2.各个分箱内的记录数应该合理,不应过多或者或过少。
3.结合目标变量,分箱应该表现出明显的趋势。
4.相邻分箱的目标变量分布差异尽可能大。

#计算WOE的函数
totalgood = as.numeric(table(train$y))[1]
totalbad = as.numeric(table(train$y))[2]
getWOE <- function(a,p,q)
{
  Good <- as.numeric(table(train$y[a > p & a <= q]))[1]
  Bad <- as.numeric(table(train$y[a > p & a <= q]))[2]
  WOE <- log((Bad/totalbad)/(Good/totalgood),base = exp(1))
  return(WOE)
}

如age变量(x1)

Agelessthan30.WOE=getWOE(train$x1,-Inf,30)
Age30to35.WOE=getWOE(train$x1,30,35)
Age35to40.WOE=getWOE(train$x1,35,40)
Age40to45.WOE=getWOE(train$x1,40,45)
Age45to50.WOE=getWOE(train$x1,45,50)
Age50to55.WOE=getWOE(train$x1,50,55)
Age55to60.WOE=getWOE(train$x1,55,60)
Age60to65.WOE=getWOE(train$x1,60,65)
Age65to75.WOE=getWOE(train$x1,65,75)
Agemorethan.WOE=getWOE(train$x1,75,Inf)
age.WOE=c(Agelessthan30.WOE,Age30to35.WOE,Age35to40.WOE,Age40to45.WOE,Age45to50.WOE,
          Age50to55.WOE,Age55to60.WOE,Age60to65.WOE,Age65to75.WOE,Agemorethan.WOE)
age.WOE


NumberOftIME30-59Days(x4)


以上截图中的x10指x9,其他类推
3.对变量进行WOE变换
如age变量(x1)

tmp.age <- 0
for(i in 1:nrow(train)) {
  if(train$x1[i] <= 30)
    tmp.age[i] <- Agelessthan30.WOE
  else if(train$x1[i] <= 35)
    tmp.age[i] <- Age30to35.WOE
  else if(train$x1[i] <= 40)
    tmp.age[i] <- Age35to40.WOE
  else if(train$x1[i] <= 45)
    tmp.age[i] <- Age40to45.WOE
  else if(train$x1[i] <= 50)
    tmp.age[i] <- Age45to50.WOE
  else if(train$x1[i] <= 55)
    tmp.age[i] <- Age50to55.WOE
  else if(train$x1[i] <= 60)
    tmp.age[i] <- Age55to60.WOE
  else if(train$x1[i] <= 65)
    tmp.age[i] <- Age60to65.WOE
  else if(train$x1[i] <= 75)
    tmp.age[i] <- Age65to75.WOE
  else
    tmp.age[i] <- Agemorethan.WOE
}
table(tmp.age)
tmp.age[1:10]
train$x1[1:10]





4.WOE DataFrame构建

trainWOE =cbind.data.frame(tmp.age,tmp.NumberOfTime30.59DaysPastDueNotWorse,tmp.MonthlyIncome,tmp.NumberOfTime60.89DaysPastDueNotWorse<br data-filtered="filtered">                           ,tmp.NumberOfTimes90DaysLate,tmp.NumberRealEstateLoansOrLines,tmp.NumberOfDependents)

5.看一下各个变量的IV值


八、评分卡的创建和实施



逻辑回归建模

#因为数据中“1”代表的是违约,直接建模预测,求的是“发生违约的概率”,log(odds)即为“坏好比”。为了符合常规理解,分数越高,信用越好,所有就调换“0”和“1”,使建模预测结果为“不发生违约的概率”,最后log(odds)即表示为“好坏比”。
trainWOE$y = 1-train$y
glm.fit = glm(y~.,data = trainWOE,family = binomial(link = logit))
summary(glm.fit)
coe = (glm.fit$coefficients)
p <- 20/log(2)
q <- 600-20*log(15)/log(2)
Score=q + p*{as.numeric(coe[1])+as.numeric(coe[2])*tmp.age +as.numeric(coe[3])*tmp.NumberOfTime30.59DaysPastDueNotWorse+p*as.numeric(coe[4])*tmp.MonthlyIncome+p*as.numeric(coe[5])*tmp.NumberOfTime60.89DaysPastDueNotWorse+p*as.numeric(coe[6])*tmp.NumberOfTimes90DaysLate+p*as.numeric(coe[7])*tmp.NumberRealEstateLoansOrLines+p*as.numeric(coe[8])*tmp.NumberOfDependents

个人总评分=基础分+各部分得分
基础分为:

base <- q + p*as.numeric(coe[1])
base

对各变量进行打分

Agelessthan30.SCORE = p*as.numeric(coe[2])*Agelessthan30.WOE
Age30to35.SCORE = p*as.numeric(coe[2])*Age30to35.WOE
Age35to40.SCORE = p*as.numeric(coe[2])*Age35to40.WOE
Age40to45.SCORE = p*as.numeric(coe[2])*Age40to45.WOE
Age45to50.SCORE = p*as.numeric(coe[2])*Age45to50.WOE
Age50to55.SCORE = p*as.numeric(coe[2])*Age50to55.WOE
Age55to60.SCORE = p*as.numeric(coe[2])*Age55to60.WOE
Age60to65.SCORE = p*as.numeric(coe[2])*Age60to65.WOE
Age65to75.SCORE = p*as.numeric(coe[2])*Age65to75.WOE
Agemorethan.SCORE=p*as.numeric(coe[2])*Agemorethan.WOE
Age.SCORE =c(Age30to35.SCORE,Age35to40.SCORE,Age40to45.SCORE,Age45to50.SCORE,Age50to55.SCORE,Age55to60.SCORE,Age60to65.SCORE,Age65to75.SCORE,Agemorethan.SCORE)
Age.SCORE


计算各变量分箱得分:

Agelessthan30.SCORE = getscore(2,Agelessthan30.WOE)
Age30to35.SCORE = getscore(2,Age30to35.WOE)
Age35to40.SCORE = getscore(2,Age35to40.WOE)
Age40to45.SCORE = getscore(2,Age40to45.WOE)
Age45to50.SCORE = getscore(2,Age45to50.WOE)
Age50to55.SCORE = getscore(2,Age50to55.WOE)
Age55to60.SCORE = getscore(2,Age55to60.WOE)
Age60to65.SCORE = getscore(2,Age60to65.WOE)
Age65to75.SCORE = getscore(2,Age65to75.WOE)
Agemorethan.SCORE = getscore(2,Agemorethan.WOE)
Age.SCORE = c(Agelessthan30.SCORE,Age30to35.SCORE,Age35to40.SCORE,Age40to45.SCORE,Age45to50.SCORE,Age50to55.SCORE,Age55to60.SCORE,Age60to65.SCORE,Age65to75.SCORE,Agemorethan.SCORE)
Age.SCORE


最终生成的评分卡如下:



个人评分计算案例:

所以这个人的总评分 = 基础分(base)+ 各特征分数
总评分 = 446.2841+7+47+10+38-3+62+2 = 609.2841
建立自动评分系统:

#计算每一个借款人的信用评分
#age
score.age <- 0
for(i in 1:nrow(train)) {
  if(train$x2[i] <= 30)
    score.age[i] <- Agelessthan30.SCORE
  else if(train$x2[i] <= 35)
    score.age[i] <- Age30to35.SCORE
  else if(train$x2[i] <= 40)
    score.age[i] <- Age35to40.SCORE
  else if(train$x2[i] <= 45)
    score.age[i] <- Age40to45.SCORE
  else if(train$x2[i] <= 50)
    score.age[i] <- Age45to50.SCORE
  else if(train$x2[i] <= 55)
    score.age[i] <- Age50to55.SCORE
  else if(train$x2[i] <= 60)
    score.age[i] <- Age55to60.SCORE
  else if(train$x2[i] <= 65)
    score.age[i] <- Age60to65.SCORE
  else if(train$x2[i] <= 75)
    score.age[i] <- Age65to75.SCORE
  else
    score.age[i] <- Agemorethan.SCORE
}
for(i in 1:nrow(train)){
creditScore[i]<-score.age[i]+score.PastDue[i]+score.MonthIncome[i]+
score.Days90PastDue[i]+score.RealEstate[i]+score.Days60.89PastDue[i]+score.Dependents[i]+baseScore
}
train$creditScore<-round(creditScore,0)


本文通过对于Kaggle上项目的数据进行分析,利用逻辑回归制作了一个简单的评分卡。在建立评分卡的过程中,首先进行了数据清洗,对缺失值和异常值进行了处理并对数据分布进行了宏观展示。然后对特征值进行了处理,将连续的变量分箱,同时计算了woe和iv值,并保留了iv值较高的变量对其woe转化。最后将woe转化后的数据进行逻辑回归分析,利用得到变量系数并自行拟定了评分标准建立了评分卡。
本项目还有许多不足之处,比如分箱应当使用最优分箱或卡方分箱,减少人为分箱的随机性,此外模型采用的是逻辑回归算法,还可以多多尝试其他模型。
PS:
信用卡评分模型学习笔记总结(转)
https://blog.csdn.net/huipingx/article/details/85225711
其他代码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容