贷款客户违约预测模型

背景:根据已有贷款客户的还款情况数据,以预测客户是否违约,提前预警。
数据:某融资担保公司所拥有的数据,包括基本身份信息,每月还款与支出情况,行业、缴款方式、缴款记录、时间、频率等信息。
目标:建立多个机器学习模型(SVM,逻辑回归,随机森林等)。选择出稳定得分率较高的模型,以预测违约情况。

违约分析


【一】 建模思路

确定本次项目的流程。
思路流程

本次已有贷款客户数据,需要选择出相关性最强的维度,对客户是否违约进行二分类。建立模型后,预测客户是否违约,以提前预警。


【二】 数据清洗

从数据库中提取出相应的数据,观察数据。

客户数据(脱敏)

观察数据集后发现部分数据是半结构化数据,需要进行特征抽象。部分数据可能存在缺失,我们判定贷款申请人是否违约是一个 二元分类 问题。可以通过一个分类算法来处理。

1>查看数据集合维度
data.dtypes.value_counts()

(110292, 42)

2>查看每列属性缺失值的比例

因为数据集缺失值较多的特征对模型预测意义不大,故应先处理。

check_null = data.isnull().sum().sort_values(ascending=False)/float(len(data)) 
print(check_null[check_null > 0.2]) # 查看缺失比例大于20%的属性。

处理后可发现,本次数据集数据量完整不需要删除缺失较多的数据。
如果缺失值对属性来说是有意义的,还得细分缺失值对应的属性是数值型变量或是分类类型变量。

3>同值化处理

如果某个变量大部分的观测都是相同的特征,那么这个特征或者输入变量就无法用来区分预测值。

data = data.loc[:,data.apply(pd.Series.nunique) != 1]
data.shape

(110292, 32)

4>数据过滤

结合业务知识,将一般无关或对构建预测模型没有意义的属性整合入drop_list,后删除drop_list以提高数据置信度。

data.drop(drop_list, axis=1, inplace=True)

(110292, 22)

5>将数据做预处理

将所有字符串形式的属性转换为数字形式,将部分连续变量转换为离散变量,再对部分数据做归一化。

def Sigmoid (X):
    return (1.0 / (1 + np.exp(-float(X)));

def Replace (X,columns):
    a = X.groupby([columns],as_index=False)[columns].agg({'cnt':'count'})
    for i in a[columns]:
        X[columns] = X[columns].replace(i,a[(a[columns]== i )].index.tolist()[0])
    return (X)

def Quota (X):
    X['信用额度']=X['信用额度']*(10**-4)
    return (X)

def Age (X):
    a = X.groupby(['年龄'],as_index=False)['年龄'].agg({'cnt':'count'})
    for i in a['年龄']:
        if i < 30:
            X['年龄'].replace(i,0)
        if 30 <= i <40:
            X['年龄'].replace(i,1)
        if 40 <= i :
            X['年龄'].replace(i,2)
    return (X)

清洗完后数据:

各个维度方差验证

【二】 特征工程

1>特征衍生

为了提高对贷款客户的预测,根据业务经验生成新的特征。
例如:将每月分期应还金额减去上一月应还金额,再除以当月实际归还的天数,生成新特征,代表客户每月还款支出的变化,值越大意味着贷款人的偿债压力越来越大,违约的可能性越大。

2>特征缩放

采用的是标准化方法,调用scikit-learn模块preprocessing的子模块StandardScaler。
preprocessing.StandardScaler类可以用来计算数据矩阵的均值和标准差,而且这个类用起来更方便

from sklearn import preprocessing
sc =StandardScaler()  # 初始化缩放器
data_ml_df[col] =sc.fit_transform(data_ml_df[col])  #对数据进行标准化
3>特征选择

选出与目标变量相关性较高的特征。递归特征消除 (RFE)筛选15个与目标变量相关性最强的特征,去除不相关特征以降低学习的难度而达到首次降维.

def RFE (X,Y,n):
    from sklearn.linear_model import LogisticRegression
    from sklearn.feature_selection import RFE
    model = LogisticRegression()
    # 建立递归特征消除筛选器
    rfe = RFE(model, n_features_to_select=n)     #通过递归选择特征,选择n个特征
    rfe = rfe.fit(X,Y)
    return (rfe.n_features_ , rfe.estimator_ , rfe.support_ , rfe.ranking_)   #ranking 为 1代表被选中,其他则未被代表未被选中

运用PCA进行主成分分析。PCA可能会提高模型准确率,但是可解释性会下降,根据具体情况选择使用。

def PCA(data):
    from sklearn.decomposition import PCA
    pca=PCA(n_components=None, copy=True, whiten=False)
    pca.fit(data)
    return(pca.components_ ,pca.explained_variance_ratio_)

#pca.components_返回模型的各个特征向量
#pca.explained_variance_ratio_返回各个成为各自的方差百分比(贡献率)
4>相关性分析

选择出是否违约相关性最强的24个维度进行分析。

import matplotlib.pyplot as plt  
import seaborn as sns 
import numpy as np
import pandas as pd

plt.rcParams['font.sans-serif']=['SimHei']                
plt.rcParams['axes.unicode_minus']=False
corrmat = data.corr()  
k = 24
plt.figure(figsize=(12,9))
cols = corrmat.nlargest(k, '是否违约')['是否违约'].index                       
cm = np.corrcoef(data[cols].values.T)
sns.set(font_scale=1.25,font='SimHei')                                   
hm = sns.heatmap(cm, cbar=True, annot=True, square=True, fmt='.2f',cmap='BuGn_r',annot_kws={'size': 10}, yticklabels=cols.values, xticklabels=cols.values)
plt.show()
相关性较强的维度

【三】 模型训练

1>数据选择

在观察预测值Y之后发现,违约与按时还款差值过大,这时带入模型考虑是否准备不同的数据集合以提高准确性。
准备data1 违约量按照百分比存在,data2违约量等同于按时还款量。

def Breach(IO):          #违约的数据
    a = X.groupby(['是否违约'],as_index=False)['是否违约'].agg({'cnt':'count'})
    X=pd.read_excel(IO)
    X['是否违约'] = X['是否违约'].replace(0,np.NaN)
    X_breach= X.dropna(axis=0,how='any')
    return (X_breach)

def Abiding(IO):         #按时还款的数据
    X=pd.read_excel(IO)   
    X['是否违约'] = X['是否违约'].replace(1,np.NaN)
    X_abiding = X.dropna(axis=0,how='any')
    return (X_abiding)
2>构建分类器训练

采用交叉验证法划分数据集,将数据划分为3部分:训练集、验证集和测试集。

from sklearn import cross_validation 

def Data1(IO):           #原始数据
    X=pd.read_excel(IO)
    Y1 = X['是否违约']
    X1 = X.drop(['是否违约'],axis = 1)
    X1_train, X1_test, y1_train, y1_test = \
        cross_validation.train_test_split( X1, Y1, test_size=0.3, random_state=0)
    return (X1_train, X1_test, y1_train, y1_test)

def Data2(X_breach,X_abiding):          #同样数量  
    X_abiding = X_abiding.sample(n=len(X_breach))
    S = pd.concat([X_breach,X_abiding],axis = 0)
    S = S.sample(frac=1).reset_index(drop=True)
    Y2 = S['是否违约']
    X2 = S.drop(['是否违约'],axis = 1)
    X2_train, X2_test, y2_train, y2_test = \
        cross_validation.train_test_split( X2, Y2, test_size=0.3, random_state=0)
    return (X2_train, X2_test, y2_train, y2_test)  

本次交叉验证的比例为7:3

3>建立模型

二分类问题,首先选择逻辑回归,随机森林与支持向量机进行验证。
后根据具体情况可以考虑Adaboost,朴素贝叶斯等。

from sklearn.metrics import accuracy_score
       
def RF(X_train, X_test, y_train, y_test):    #随机森林 
    from  sklearn.ensemble  import  RandomForestClassifier
    model= RandomForestClassifier(n_estimators=100)
    model.fit(X_train, y_train)
    predicted= model.predict(X_test)
    score = accuracy_score(y_test, predicted)
    return (score)

def Svm(X_train, X_test, y_train, y_test):   #支持向量机
    from sklearn import svm
    model = svm.SVC(kernel='rbf')
    model.fit(X_train, y_train)    
    predicted= model.predict(X_test)
    score = accuracy_score(y_test, predicted)
    return (score)    

def LR(X_train, X_test, y_train, y_test):   #逻辑回归
    from sklearn.linear_model import LogisticRegression
    lor = LogisticRegression(penalty='l1',C=100,multi_class='ovr') 
    lor.fit(X_train, y_train)
    predicted= lor.predict(X_test)
    score = accuracy_score(y_test, predicted)
    return (score)    

def SGD(X_train, X_test, y_train, y_test):      #随机梯度下降
    from sklearn.linear_model import SGDClassifier
    sgdv = SGDClassifier(penalty='l1')
    sgdv.fit(X_train,y_train)
    predicted = sgdv.predict(X_test)
    score = accuracy_score(y_test, predicted)
    return (score) 

尝试不同算法后得分如下

data1 : 百分比       data2 : 同等数量 
#随机森林得分: data1 : 0.8192222222222222 data2 : 0.6958814665996986 
#支持向量机 rbf 得分: data1 : 0.7856666666666666 data2 : 0.5228528377699648
#逻辑回归得分: data1 : 0.7845555555555556 data2 : 0.5979407332998493 
#随机梯度下降得分: data1 : 0.7457777777777778 data2 : 0.5507282772476143 

比较后模型后,优化参数,提高准确性。


【四】 分析结果

训练模型得分不太满意,只有随机森林得分较高。特征工程程度加深的化,可能有助于提高模型得分。
可尝试模型融合,不同模型投票后,集成学习有助于值的预测。
百分比数据量的结果明显优于等比例数据量。

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

推荐阅读更多精彩内容