京东-优惠雷达
新人页面
精选商品
首月0月租体验,领12个月京东PLUS
自营热卖

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

小曲儿 1年前   阅读数 557 0

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

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

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)ROC/AUC
ROC基于混淆矩阵,对于数据类别不均衡有很好的效果。ROC值一般在0.5-1.0之间。值越大表示模型判断准确性越高(曲线越偏左上越好),即越接近1越好。ROC=0.5表示模型的预测能力与随机结果没有差别,AUC系数代表曲线下的面积,不依赖于阈值,AUC值越高,模型的风险区分能力越强。
2)KS值
KS值表示了模型正负区分开来的能力。值越大,模型的预测准确性越好。一般,KS>0.4即可认为模型有比较好的预测准确性,KS值只能反映出哪个分段是区分最大的,而不能总体反映出所有分段的效果。
下面是一个真实的在线授信产品的风控建模的流程图,可参考进行理解:


15866579-9cdbdc835a7bfcee.png

15866579-51e6054ec13c649b.png

二、数据获取

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

15866579-8392eb999f05363a.png

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

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

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

三、数据预处理

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

library(mice)
md.pattern(data)
15866579-c435a8e59ba59bc2.png

15866579-8f6ea399031cc5d3.png

同样得出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))
15866579-e748ca40c984ede1.png
#用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 : 箱体中线条的颜色,默认为黑色
15866579-3f2cdbf1ee04e046.png

可以看出经过盖帽法,大于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),]
15866579-230d897277823f56.png

因为上述两变量的数值型为百分比,故大于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),]
15866579-41101183a16b7944.png

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

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

hist(x1,freq = F,col="lightgreen")
lines(density(x1),col="red")
15866579-533d638ca164bde9.png

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

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)

15866579-7819a3a3d204cda4.png

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

cor1<-cor(data[,2:12])
library(corrplot)
corrplot(cor1)
15866579-3119b8d2e0c92a56.png
corrplot(cor1,method = "number")
15866579-4adff15e474ea7cb.png

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

五、切分数据集

table(y)
15866579-7abe980ddd360dfd.png

由上表看出,对于响应变量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))
15866579-90144560a35fe8cd.png

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

六、建立模型

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

15866579-aa9316d919c1bdfb.png

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

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

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

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

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

step(fit2, direction = "both")
15866579-db6126bf9cc88bc2.png

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

library(car)
library(carData)
vif(fit2)
15866579-45b62726c36212a8.png

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

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

⑥模型评估
通常一个二值分类器可以通过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更大的分类器效果更好。
下面首先利用模型对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)
15866579-1e273c9f8f467bc1.png

如图所示:最优点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))
15866579-8e9b972721a16b73.png
cutx2 = c(-Inf,0,1,3,5,Inf)
plot(cut(train$x2,cutx2))
15866579-ad42ebbc116d0414.png
cutx4 = c(-Inf,1000,2000,3000,4000,5000,6000,7500,9500,12000,Inf)
plot(cut(train$x4,cutx4))
15866579-74cda30fc8671227.png
cutx6 = c(-Inf,0,1,3,5,10,Inf)
plot(cut(train$x6,cutx6))
15866579-908f0c8b75581e55.png
cutx8 = c(-Inf,0,1,3,5,Inf)
plot(cut(train$x8,cutx8))
15866579-adbe0dbfcc1e8f4a.png
cutx9 = c(-Inf,0,1,2,3,5,Inf)
plot(cut(train$x9,cutx9))

15866579-65b65bc9835a3b63.png

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

证据权重(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

15866579-4fc92ee2ca48576e.png

NumberOftIME30-59Days(x4)
15866579-b3468381a47639e6.png

15866579-b8b747307ca224ee.png

以上截图中的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)
15866579-260db82a9dfcf905.png
tmp.age[1:10]
15866579-e3da350feb5e540a.png
train$x1[1:10]

15866579-55201df476df6d96.png

15866579-d2c1b5a5ca1c362d.png

15866579-d6a6e1c12a1fe885.png

15866579-899ff04475d05100.png

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值

15866579-b999b2bf0d5f1ed3.png

15866579-e1e07c0079ec9d0b.png

八、评分卡的创建和实施

15866579-d32e18ab6bfd66f9.png

15866579-5313841860320f91.png

逻辑回归建模

#因为数据中“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
15866579-c9772a25e3ff3864.png

15866579-cef3ffa8fe12dc0f.png

计算各变量分箱得分:

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
15866579-5c412b787614e016.png

15866579-c0a8433a11a4b3d0.png

最终生成的评分卡如下:


15866579-f3c7752d8b3018a3.png

个人评分计算案例:
15866579-6c42edd9439e4b39.png

所以这个人的总评分 = 基础分(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)

15866579-372c13e2d71498c5.png

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

15866579-f279184003d83fe5.png


注意:本文归作者所有,未经作者允许,不得转载

全部评论: 0

    我有话说: