1. 项目准备
1.1. 问题导入
随着技术的不断革新以及个人消费意识的改变,从互联网上借贷来满足生活需求或者实现个人增值,越来越受到大众的接受。请利用以往的信贷数据利用决策树构建一个风险控制模型,使其在后续的借贷业务中,能够提前识别出信贷风险,供投资者选择是否投资此项贷款。
1.2. 数据集简介
数据集中的数据是美国最大的P2P网贷交易平台Lending Club的历史数据,一共由9578行、14列数据构成:
(1) credit.policy
客户是否满足Lending Club的授信标准(1为是,0为否)
(2) purpose
表示贷款的目的(例:信用卡还款,债务处理,教育,购买大件,中小企业经营等等)
(3) int.rate
贷款利率(较高的贷款利率意味着较高的风险)
(4) installment
每月分期还款额
(5) log.annual.inc
借贷者的年收入的自然对数
(6) dti
借贷者的债务收入比(贷款收入比例,Delt-to-income)
(7) fico
美国个人评分系统的评分。FICO得出的分数在300-850分之间,分数越高说明客户的信用风险越小,但分数本身不能说明一个客户是好是坏,贷款方通常会将分数作为参考,来进行贷款决策。每个贷款方都有自己的贷款策略和标准,并且每种产品都会有自己的风险水平,从而决定了可以接受的信用分数水平。
(8) days.with.cr.line
借贷者有信用额度的天数
(9) revol.bal
借贷者的账户余额(尚未结清的金额)
(10) revol.util
借贷者的信用账户利用率(使用的金额/授信的金额)
(11) inq.last.6mths
借贷者在过去6个月被借款者咨询的次数
(12) delinq.2yrs
借贷者在过去2年逾期还款超过30天的次数
(13) pub.rec
借贷者公共事业记录差评的次数
(14) not.fully.paid
表示不完全支付,要预测这个因变量,可供投资者选择是否投资此项贷款(0贷款,1不贷款)
这是数据集的下载链接:Lending Club借贷数据 - AI Studio
2. 实验步骤
2.0. 导入模块
1 2 3 4 5 6 import pandas as pdfrom sklearn.preprocessing import LabelEncoderfrom sklearn.model_selection import train_test_splitfrom sklearn.tree import DecisionTreeClassifierfrom sklearn.metrics import accuracy_score, classification_report, confusion_matrixfrom collections import defaultdict
2.1. 数据预处理
1 2 3 df = pd.read_csv('loan_data.csv' ) x = df.drop("not.fully.paid" , axis=1 ) y = df['not.fully.paid' ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 count mean std min \ credit.policy 9578.0 0.804970 0.396245 0.000000 int.rate 9578.0 0.122640 0.026847 0.060000 installment 9578.0 319.089413 207.071301 15.670000 log.annual.inc 9578.0 10.932117 0.614813 7.547502 dti 9578.0 12.606679 6.883970 0.000000 fico 9578.0 710.846314 37.970537 612.000000 days.with.cr.line 9578.0 4560.767197 2496.930377 178.958333 revol.bal 9578.0 16913.963876 33756.189557 0.000000 revol.util 9578.0 46.799236 29.014417 0.000000 inq.last.6mths 9578.0 1.577469 2.200245 0.000000 delinq.2yrs 9578.0 0.163708 0.546215 0.000000 pub.rec 9578.0 0.062122 0.262126 0.000000 not.fully.paid 9578.0 0.160054 0.366676 0.000000 25% 50% 75% max credit.policy 1.000000 1.000000 1.000000 1.000000e+00 int.rate 0.103900 0.122100 0.140700 2.164000e-01 installment 163.770000 268.950000 432.762500 9.401400e+02 log.annual.inc 10.558414 10.928884 11.291293 1.452835e+01 dti 7.212500 12.665000 17.950000 2.996000e+01 fico 682.000000 707.000000 737.000000 8.270000e+02 days.with.cr.line 2820.000000 4139.958333 5730.000000 1.763996e+04 revol.bal 3187.000000 8596.000000 18249.500000 1.207359e+06 revol.util 22.600000 46.300000 70.900000 1.190000e+02 inq.last.6mths 0.000000 1.000000 2.000000 3.300000e+01 delinq.2yrs 0.000000 0.000000 0.000000 1.300000e+01 pub.rec 0.000000 0.000000 0.000000 5.000000e+00 not.fully.paid 0.000000 0.000000 0.000000 1.000000e+00
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 credit.policy int.rate installment log.annual.inc \ credit.policy 1.000000 -0.294089 0.058770 0.034906 int.rate -0.294089 1.000000 0.276140 0.056383 installment 0.058770 0.276140 1.000000 0.448102 log.annual.inc 0.034906 0.056383 0.448102 1.000000 dti -0.090901 0.220006 0.050202 -0.054065 fico 0.348319 -0.714821 0.086039 0.114576 days.with.cr.line 0.099026 -0.124022 0.183297 0.336896 revol.bal -0.187518 0.092527 0.233625 0.372140 revol.util -0.104095 0.464837 0.081356 0.054881 inq.last.6mths -0.535511 0.202780 -0.010419 0.029171 delinq.2yrs -0.076318 0.156079 -0.004368 0.029203 pub.rec -0.054243 0.098162 -0.032760 0.016506 not.fully.paid -0.158119 0.159552 0.049955 -0.033439 dti fico days.with.cr.line revol.bal \ credit.policy -0.090901 0.348319 0.099026 -0.187518 int.rate 0.220006 -0.714821 -0.124022 0.092527 installment 0.050202 0.086039 0.183297 0.233625 log.annual.inc -0.054065 0.114576 0.336896 0.372140 dti 1.000000 -0.241191 0.060101 0.188748 fico -0.241191 1.000000 0.263880 -0.015553 days.with.cr.line 0.060101 0.263880 1.000000 0.229344 revol.bal 0.188748 -0.015553 0.229344 1.000000 revol.util 0.337109 -0.541289 -0.024239 0.203779 inq.last.6mths 0.029189 -0.185293 -0.041736 0.022394 delinq.2yrs -0.021792 -0.216340 0.081374 -0.033243 pub.rec 0.006209 -0.147592 0.071826 -0.031010 not.fully.paid 0.037362 -0.149666 -0.029237 0.053699 revol.util inq.last.6mths delinq.2yrs pub.rec \ credit.policy -0.104095 -0.535511 -0.076318 -0.054243 int.rate 0.464837 0.202780 0.156079 0.098162 installment 0.081356 -0.010419 -0.004368 -0.032760 log.annual.inc 0.054881 0.029171 0.029203 0.016506 dti 0.337109 0.029189 -0.021792 0.006209 fico -0.541289 -0.185293 -0.216340 -0.147592 days.with.cr.line -0.024239 -0.041736 0.081374 0.071826 revol.bal 0.203779 0.022394 -0.033243 -0.031010 revol.util 1.000000 -0.013880 -0.042740 0.066717 inq.last.6mths -0.013880 1.000000 0.021245 0.072673 delinq.2yrs -0.042740 0.021245 1.000000 0.009184 pub.rec 0.066717 0.072673 0.009184 1.000000 not.fully.paid 0.082088 0.149452 0.008881 0.048634 not.fully.paid credit.policy -0.158119 int.rate 0.159552 installment 0.049955 log.annual.inc -0.033439 dti 0.037362 fico -0.149666 days.with.cr.line -0.029237 revol.bal 0.053699 revol.util 0.082088 inq.last.6mths 0.149452 delinq.2yrs 0.008881 pub.rec 0.048634 not.fully.paid 1.000000
2.2. 数据预处理
数据的预处理包括:数据的清洗、数据的采样、数据集划分、特征选择、特征降维、特征编码、规范化等过程。
下面重点强调一下“特征编码”和“数据集划分”这两个步骤:
特征编码
在构建决策树时,每一个特征都应该是数值类型的,但是我们可以看出,purpose等列的取值都是字符型的(类别,categorical)。所以,必须经过一些转换,把这些类别都映射成为某个数值,才能进行后续步骤。
1 2 3 dic = defaultdict(LabelEncoder) x_trans = x.apply(lambda x: dic[x.name].fit_transform(x)) print (x_trans.head())
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 credit.policy purpose int.rate installment log.annual.inc dti fico \ 0 1 2 76 4665 1462 1887 25 1 1 1 50 2116 1143 1380 19 2 1 2 115 3240 348 1115 14 3 1 2 38 1375 1462 767 20 4 1 1 132 759 1410 1446 11 days.with.cr.line revol.bal revol.util inq.last.6mths delinq.2yrs \ 0 1663 6606 530 0 0 1 632 6849 778 0 0 2 1363 1628 260 1 0 3 609 6852 743 1 0 4 1132 2115 403 0 1 pub.rec 0 0 1 0 2 0 3 0 4 0
数据集划分
训练集和测试集的划分 将整个数据集拆分成:训练集和测试集。不过,如果我们将其直接划分为训练集和数据集,那么就会造成数据分布不均的问题。好在sklearn为我们提供了划分训练集和数据集的方法。
1 2 3 [x_train, x_test, y_train, y_test ] = train_test_split(x_trans, y, random_state=1 )
2.3. 模型训练与预测
sklearn 中使用 sklearn.tree.DecisionTreeClassifier 类来实现决策树算法,其构造方法包含许多参数,比较常用的有以下几个:
(1)特征选择标准 criterion
可选值:“gini”(基尼系数,默认)或 “entropy”(信息熵)
两种算法差异不大对准确率无影响,信息熵效率低一点,因为它有对数运算。一般说使用默认的基尼系数“gini”就可以了,即CART算法。
(2)随机状态数 random_state
可选值:None(默认),int 或 RandomState
如果传入值为整数,则它指定了随机数生成器的种子;如果传入值为RandomState实例,则指定了随机数生成器;如果传入值为None,则使用默认的随机数生成器。
(3)特征划分标准 splitter
可选值:“best”(最佳,默认)或 “random”(随机)
前者在特征的所有划分点中找出最优的划分点。后者是随机的在部分划分点中找局部最优的划分点。 默认的“best”适合样本量不大的时候,而如果样本数据量非常大,此时决策树构建推“random”。
(4)决策树最大深度 max_depth
可选值:None(默认)或 int
一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间。常用来防止模型出现过拟合。
(5)叶节点最少样本数 min_samples_leaf
可选值:int(默认为1)或 float
如果值是 int型,则取传入值本身作为最小样本数;如果值是 float型,则取 ceil(min_samples_leaf * 样本数量) 的值作为最小样本数(ceil 函数的作用是向上取整)。min_samples_leaf 主要用于对决策树进行修剪,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。
(6)最大特征个数 max_features
可选值:None(默认),int,float 或 string(“auto”, “sqrt”, “log2”)
max_features 一般配合 max_depth 使用,用作树的“精修”,它限制分枝时考虑的特征个数,超过限制个数的特征都会被舍弃。
(7)节点划分最小不纯度 min_impurity_decrease
可选值:float(默认为0)
min_impurity_decrease 限制了决策树的增长,如果某节点的不纯度(基尼系数,信息增益,均方差,绝对差)小于这个阈值,则该节点不再生成子节点。
(8)内部节点再划分所需最小样本数 min_samples_split
可选值:int(默认为2)或 float
如果值是 int型,则取传入值本身作为最小样本数;如果值是 float型,则取 ceil(min_samples_split * 样本数量) 的值作为最小样本数(ceil 函数的作用是向上取整)。
我们这里暂时先采用默认参数来构造决策树模型:
1 2 3 4 5 dt = DecisionTreeClassifier() dt.fit(x_train, y_train) dt_infer = dt.predict(x_test) dt_truth = list (y_test)
2.4. 模型评价
1 print ("Accuracy \t %.8f" % accuracy_score(dt_truth, dt_infer))
1 print (confusion_matrix(dt_truth, dt_infer))
1 2 [[1668 324] [ 301 102]]
1 print (classification_report(dt_truth, dt_infer))
1 2 3 4 5 6 7 8 precision recall f1-score support 0 0.85 0.84 0.84 1992 1 0.24 0.25 0.25 403 micro avg 0.74 0.74 0.74 2395 macro avg 0.54 0.55 0.54 2395 weighted avg 0.74 0.74 0.74 2395
2.5. 模型调优
模型的结果还可以通过优化来提升,在刚才的整个机器学习过程中,用的都是决策树的缺省值,下面通过设置DecisionTreeClassifier() 训练过程中的参数进行调优:
1 2 3 4 5 6 7 8 ndt = DecisionTreeClassifier( max_depth=5 , min_samples_leaf=0.3 , max_features=1 ) ndt.fit(x_train, y_train) ndt_infer = ndt.predict(x_test) ndt_truth = list (y_test)
1 print ("Accruacy \t %.8f" % accuracy_score(ndt_truth, ndt_infer))
1 print (confusion_matrix(ndt_truth, ndt_infer))
写在最后