【项目实战】金融风控之信用评分卡模型

📅 2026/7/15 20:01:58
【项目实战】金融风控之信用评分卡模型
一、背景介绍信用风险计量体系包括主体评级模型和债项评级两部分。主体评级和债项评级均有一系列评级模型组成其中主体评级模型可用“四张卡”来表示分别是A卡贷前申请评分卡、B卡贷中行为评分、C卡贷后评分卡和F卡贷前反欺诈评分卡债项评级模型通常按照主体的融资用途分为企业融资模型、现金流融资模型和项目融资模型等。 我们主要讨论主体评级模型的开发过程。二、项目流程主要开发流程如下1数据获取包括机构客户和个人客户2数据预处理包括数据清洗、缺失值处理、异常值处理3探索性数据分析获取样本总体的大概情况描述样本总体情况的指标主要有直方图、箱型图等4变量选择该步骤主要通过统计学的方法筛选出对违约状态影响最显著的指标。主要有单变量特征选择方法和基于机器学习模型的方法5模型开发该步骤主要包括变量分段、变量的WOE证据权重变换和逻辑回归估算三部分6模型评估该步骤主要是评估模型的区分能力、预测能力、稳定性并形成模型评估报告得出模型是否可以使用的结论7信用评分根据逻辑回归的系数和WOE等确定信用评分的方法。将logistic模型转换为标准评分的形式8建立评分系统根据信用评分方法简历自动信用评分系统三、数据集介绍以下给出该数据集的前三行是一个信用卡数据集各个特征及其含义已给出SeriousDlqin2yrsRevolvingUtilizationOfUnsecuredLinesageNumberOfTime30-59DaysPastDueNotWorseDebtRatioMonthlyIncomeNumberOfOpenCreditLinesAndLoansNumberOfTimes90DaysLateNumberRealEstateLoansOrLinesNumberOfTime60-89DaysPastDueNotWorseNumberOfDependents10.7661266094520.802982912013060200.9571510194000.12187620126004000100.658180143810.085113375304221000SeriousDlqin2yrs是否是好客户其中好客户为0违约客户为1RevolvingUtilizationOfUnsecuredLines无担保循环信贷额度使用率age 年龄NumberOfTime30-59DaysPastDueNotWorse逾期30-59天且未恶化次数DebtRatio 负债比率NumberOfOpenCreditLinesAndLoans未结清信贷额度与贷款数量NumberOfTimes90DaysLate逾期90天的次数NumberRealEstateLoansOrLines房地产贷款或额度数量NumberOfTime60-89DaysPastDueNotWorse逾期60-89天次数未恶化NumberOfDependents抚养人数可以看到labelY即是SeriousDlqin2yrs我们需要用剩余的特征去预测一个样本是否延期。3.0 特征解读1、RevolvingUtilizationOfUnsecuredLines无担保循环信贷额度使用率计算公式使用率 (所有循环信贷账户的当前总欠款 / 所有循环信贷账户的总授信额度) × 100%使用率高低有什么影响对个人信用评分高使用率30%会被解读为过度依赖信贷、财务状况紧张从而拉低信用评分。银行会认为你的违约风险较高低使用率30%表明你管理信贷的能力良好是负责人借款人的信号有助于提升信用评分。对带宽审批和额度管理影响新贷款审批即使未使用的额度不计入负债但银行在审批房贷、车贷时会关注你的总授信和近期平均使用率。过高的手心总额或过多的信贷账户都可能让银行认为你“资金紧张”而变得谨慎。触发风控导致“锁额”如果每次都把额度用尽风控系统会判定“资金缺口过大风险过高”可能导致额度被冻结或降低。相反如果额度长期闲置不用平台也可能判定你需求低而主动降低额度。该数据是数值型数据以下展示其训练集的分布2、逾期指标NumberOfTime30-59DaysPastDueNotWorse (30-59天)衡量轻微、早期的逾期行为NumberOfTime60-89DaysPastDueNotWorse (60-89天)衡量中等程度的逾期行为NumberOfTimes90DaysLate (90天及以上)衡量严重的逾期行为这通常被直接定义为“坏账”或“违约”的代理变量。与M0\M1\M2的对应关系如下行业术语对应逾期天数本项目特征名称M0无逾期/逾期1-29天-M1逾期30-59天NumberOfTime30-59DaysPastDueNotWorseM2逾期60-89天NumberOfTime60-89DaysPastDueNotWorseM3逾期90-119天NumberOfTimes90DaysLate其他的指标比如DebtRatio负债比率、NumberOfOpenCreditLinesAndLoans未结清信贷额度与贷款数量、NumberRealEstateLoansOrLines房地产贷款或额度数量、NumberOfDependents受抚养人数较好理解此处不做过多赘述。3.1探索性数据分析拿到一个数据集我习惯是先看看整个数据的大小、分布、以及缺失值情况3.1.1数据集基本信息0数据读取#载入数据datapd.read_csv(cs-training.csv)1数据集大小 探索性数据分析 data.shape data.columns data.info()3.2 数据清洗3.2.1 缺失值处理 缺失值 # 判断哪些列有空值data.isnull().any()# 每列空值的数量data.isnull().sum()可以看到MonthlyIncome、NumberOfDependents有空值。由于NumberOfDependents缺失样本仅占整个训练集的2.6%故直接删除NumberOfDependents缺失的样本。接下来采用随机森林的方法对MonthlyIncome进行缺失值补全# 用随机森林对缺失值预测填充函数defset_missing(df):# 把已有的数值型特征取出来process_dfdf.iloc[:,[5,0,1,2,3,4,6,7,8,9]]# 分成已知该特征和未知该特征两部分knownprocess_df[process_df.MonthlyIncome.notnull()].as_matrix()unknownprocess_df[process_df.MonthlyIncome.isnull()].as_matrix()# X为特征属性值Xknown[:,1:]# y为结果标签值yknown[:,0]# fit到RandomForestRegressor之中rfrRandomForestRegressor(random_state0,n_estimators200,max_depth3,n_jobs-1)rfr.fit(X,y)# 用得到的模型进行未知特征值预测predictedrfr.predict(unknown[:,1:]).round(0)print(predicted)# 用得到的预测结果填补原缺失数据df.loc[(df.MonthlyIncome.isnull()),MonthlyIncome]predictedreturndf dataset_missing(data)#用随机森林填补比较多的缺失值datadata.dropna()#删除比较少的缺失值datadata.drop_duplicates()#删除重复项data.to_csv(MissingData.csv,indexFalse)3.2.2 异常值处理缺失值处理完毕后我们还需要进行异常值处理。异常值是指明显偏离大多数抽样数据的数值比如个人客户的年龄为0时通常认为该值为异常值。找出样本总体中的异常值通常采用离群值检测的方法。首先我们发现变量age中存在0显然是异常值直接剔除# 年龄等于0的异常值进行剔除dataata[data[age]0]对于变量NumberOfTime30-59DaysPastDueNotWorse、NumberOfTimes90DaysLate、NumberOfTime60-89DaysPastDueNotWorse这三个变量均存在异常值且由unique函数可以得知均存在96、98两个异常值因此予以剔除。同时会发现剔除其中一个变量的96、98值其他变量的96、98两个值也会相应被剔除。剔除变量NumberOfTime30-59DaysPastDueNotWorse、NumberOfTimes90DaysLate、NumberOfTime60-89DaysPastDueNotWorse的异常值。另外数据集中好客户为0违约客户为1考虑到正常的理解能正常履约并支付利息的客户为1所以我们将其取反。#剔除异常值datadata[data[NumberOfTime30-59DaysPastDueNotWorse]90]#变量SeriousDlqin2yrs取反data[SeriousDlqin2yrs]1-data[SeriousDlqin2yrs]3.2.3 数据切分from sklearn.cross_validation import train_test_splitYdata[SeriousDlqin2yrs]Xdata.ix[:,1:]#测试集占比30%X_train,X_test,Y_train,Y_testtrain_test_split(X,Y,test_size0.3,random_state0)# print(Y_train)trainpd.concat([Y_train,X_train],axis1)testpd.concat([Y_test,X_test],axis1)clasTesttest.groupby(SeriousDlqin2yrs)[SeriousDlqin2yrs].count()train.to_csv(TrainData.csv,indexFalse)test.to_csv(TestData.csv,indexFalse)3.3 探索性数据分析在建立模型前通常会对现有数据进行探索性数据分析这样可以帮助我们初步了解数据分布在尽量少的先验假定下进行探索。常用的探索性数据分析方法有直方图、散点图、箱线图等。以上是对客户年龄分布的可视化可以看到大体是呈现正态分布的。3.4变量选择3.4.1分箱处理变量分箱是连续变量离散化的一种方法。信用评分卡开发中一般常用等距分段、等深分段和最优分段等距分段分段的区间宽度一致比如年龄以十年为一个分段等深分段先确定分段数量然后令每个分段中的样本数量大致相等最优分段监督离散化利用目标变量的信息将连续变量转化为能最大化其预测能力的离散变量等距分段和等深分段很好理解此处着重介绍最优分段。常见的最优分段算法基于决策树的方法利用决策树的生长和剪枝过程来寻找最佳分割点其中最小描述长度原则是常用的一种它能自动平衡模型复杂度与拟合效果避免过拟合。基于合并的方法先进行非常细致的预分箱然后根据某种准则如卡方检验的P值迭代地合并相邻箱子直到满足停止条件。最优化方法将分箱问题转化为一个有约束的数学优化问题通过算法如分支定界法搜索在满足约束如单调性下使用目标函数如信息值Ⅳ最大的分箱方案。为什么使用最优分箱提升模型预测能力可更好捕捉特征与目标之间的非线性关系增强模型可解释性处理数据异常值满足特定弄醒要求在本项目中首先选择对连续变量进行最优分段在连续变量的分布不满足最优分段的要求时再考虑对连续变量进行等距分段# 定义自动分箱函数defmono_bin(Y,X,n20):r0goodY.sum()badY.count()-goodwhilenp.abs(r)1:d1pd.DataFrame({X:X,Y:Y,Bucket:pd.qcut(X,n)})d2d1.groupby(Bucket,as_indexTrue)r,pstats.spearmanr(d2.mean().X,d2.mean().Y)nn-1d3pd.DataFrame(d2.X.min(),columns[min])d3[min]d2.min().X d3[max]d2.max().X d3[sum]d2.sum().Y d3[total]d2.count().Y d3[rate]d2.mean().Y d3[woe]np.log((d3[rate]/(1-d3[rate]))/(good/bad))d3[goodattribute]d3[sum]/good d3[badattribute](d3[total]-d3[sum])/bad iv((d3[goodattribute]-d3[badattribute])*d3[woe]).sum()d4(d3.sort_index(bymin))print(*60)print(d4)cut[]cut.append(float(-inf))foriinrange(1,n1):quaX.quantile(i/(n1))cut.append(round(qua,4))cut.append(float(inf))woelist(d4[woe].round(3))returnd4,iv,cut,woe此段代码用于分箱的核心是通过暴力搜索直到分箱后的X和分箱后的Y的Spearman相关系数的绝对值为1。之后其关键的几个步骤是1计算证据权重WOE若指标越大代表风险越低资质越好即指标越大好客户越多则WOE随指标增大而增大若指标越大代表风险越大资质越差即指标越大好客户越少则WOE随指标增大而减小。趋势特征含义说明斜率的绝对值越大区分能力越强指标值的小幅变化就能引起WOE的大幅波动说明该指标能很好区分好/坏客户斜率小或接近水平区分能力弱WOE值几乎不随指标变化说明无论指标取值如何客户风险分布差不多这个指标没用近似直线通常较好表示指标与风险之间存在稳定的线性关系便于评分卡解释。若斜率太小则区分力弱2计算信息值IVIV值的作用衡量特征“含金量”的最直接标尺可以根据数值大小将特征划分为不同等级直接决定是否剔除该变量。IV值范围预测能力处理建议 0.02无预测能力直接剔除0.02~0.10弱预测能力保留备用0.10~0.30中等预测能力有效可用常用于模型组合0.30~0.50强预测能力优质特征模型首选 0.50预测能力极强建议谨慎/暂缓可能存在过拟合或数据泄露风险需人工审查注意事项IV值只能用于二分类分箱数量影响IV值分箱越多IV值越高易过拟合。因此计算IV值时必须先进行合理的预分箱不能用原始值直接算极端IV值0.5往往有问题我们将数据集中RevolvingUtilizationOfUnsecuredLines到NumberOfDependents这10个自变量分别定义为x1-x10通过以下代码计算他们的IV值并绘制条形图corrdata.corr()#计算各变量的相关性系数xticks[x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10]#x轴标签ytickslist(corr.index)#y轴标签figplt.figure()ax1fig.add_subplot(1,1,1)sns.heatmap(corr,annotTrue,cmaprainbow,axax1,annot_kws{size:9,weight:bold,color:blue})#绘制相关性系数热力图ax1.set_xticklabels(xticks,rotation0,fontsize10)ax1.set_yticklabels(yticks,rotation0,fontsize10)plt.show()根据前述基于IV值筛选变量的标准删除了IV值小于0.1的5个变量即DebtRatio、MonthlyIncome、NumberOfOpenCreditLinesAndLoans、NumberRealEstateLoansOrLines、NumberOfDependents。好坏用户居然跟负债率和月收入的关系较低很出乎意料四、模型分析4.1 WOE转换# 替换成woecol_names[RevolvingUtilizationOfUnsecuredLines,age,NumberOfTime30-59DaysPastDueNotWorse,DebtRatio,MonthlyIncome,NumberOfOpenCreditLinesAndLoans,NumberOfTimes90DaysLate,NumberRealEstateLoansOrLines,NumberOfTime60-89DaysPastDueNotWorse,NumberOfDependents]cut_list[cutx1,cutx2,cutx3,cutx4,cutx5,cutx6,cutx7,cutx8,cutx9,cutx10]woe_list[woex1,woex2,woex3,woex4,woex5,woex6,woex7,woex8,woex9,woex10]forcol,cut,woeinzip(col_names,cut_list,woe_list):data[col]pd.cut(data[col],binscut,labelswoe,include_lowestTrue)data.to_csv(WoeData.csv,indexFalse)4.2 逻辑回归模型建立# 逻辑回归模型建立importstatsmodels.apiassm datapd.read_csv(WoeData.csv)#应变量Ydata[SeriousDlqin2yrs]#自变量剔除对因变量影响不明显的变量Xdata.drop([SeriousDlqin2yrs,DebtRatio,MonthlyIncome,NumberOfOpenCreditLinesAndLoans,NumberRealEstateLoansOrLines,NumberOfDependents],axis1)X1sm.add_constant(X)logitsm.Logit(Y,X1)resultlogit.fit()print(result.summary())由上图知逻辑回归各变量都通过显著性检验满足要求