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 pd
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
from collections import defaultdict

2.1. 数据预处理

1
2
3
df = pd.read_csv('loan_data.csv')
x = df.drop("not.fully.paid", axis=1) # 提取特征值(除“not.fully.paid”外的所有字段的值)
y = df['not.fully.paid'] # 提取目标值(字段“not.fully.paid”对应的字段值)
  • 数据的描述性统计分析
1
2
print(df.describe().T)
# 表头:特征数据个数,平均值,标准差,最小值,1/4中位数,1/2中位数,3/4中位数,最大值
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
# 相关系数绝对值越大,表示相关性越大;相关系数为正,表示正相关;相关系数为负,表示负相关
print(df.corr())
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()) # 输出映射后的前5行数据
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,    # 特征值x的训练集和测试集
y_train, y_test # 目标值y的训练集和测试集
] = 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
Accuracy 	 0.73903967
  • 混淆矩阵
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
Accruacy 	 0.83173278
  • 混淆矩阵
1
print(confusion_matrix(ndt_truth, ndt_infer))
1
2
[[1992    0]
[ 403 0]]

写在最后

  • 如果您发现项目存在问题,或者如果您有更好的建议,欢迎在下方评论区中留言讨论~
  • 这是本项目的链接:实验项目 - AI Studio,点击fork可直接在AI Studio运行~
  • 这是我的个人主页:个人主页 - AI Studio,来AI Studio互粉吧,等你哦~