贷款客户违约预测模型
背景:根据已有贷款客户的还款情况数据,以预测客户是否违约,提前预警。
数据:某融资担保公司所拥有的数据,包括基本身份信息,每月还款与支出情况,行业、缴款方式、缴款记录、时间、频率等信息。
目标:建立多个机器学习模型(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
比较后模型后,优化参数,提高准确性。
【四】 分析结果
训练模型得分不太满意,只有随机森林得分较高。特征工程程度加深的化,可能有助于提高模型得分。
可尝试模型融合,不同模型投票后,集成学习有助于值的预测。
百分比数据量的结果明显优于等比例数据量。