(六十五)基于决策树的信用评级

决策树的结构与划分选择

20世纪70年代末至20世纪80年代初,Quinlan开发了一系列决策树模型,最开始是ID3算法,后来是C4.5算法,随后又发布了C5.0算法。1984年,多位统计学家在著名的 (Classification and regression tree) 一书中提出了CART算法。

一棵决策树包含一个根结点、若干个内部结点和若干个叶结点;叶结点对应于决策结果,其他每个结点则对应于一个属性测试;每个结点包含的样本集合根据属性测试的结果被划分到子结点中;根结点包含样本全集;从根结点到每个叶结点的路径对应了一个判定测试序列。如下是根据各种特征判断是否为好瓜的一颗决策树:
在这里插入图片描述

决策树学习的关键是在每一个非叶节点如何选择最优划分属性。一般而言,随着划分过程不断进行,我们希望决策树的分支结点所包含的样本尽可能属于同一类别,即结点的"纯度"(purity)越来越高,主要有信息增益、增益率和基尼系数三种划分指标。

信息增益

在ID3算法中,使用信息增益挑选最有解释力度的属性。要了解信息增益,先要了解信息熵 (Entropy) 的概念。假定当前样本集合D中第 k 类(分类结果标签)样本所占的比例为 pk(k = 1,2,…,|y|),其信息熵的计算公式如下:
在这里插入图片描述
比如17个瓜中有好瓜8个和非好瓜9个,那么y=2,好瓜的概率p1=8/17,非好瓜的概率p2=9/17,代入公式即可。Ent(D)的值越小,则D的纯度越高。

假定离散属性a有V个可能的取值 {a1, a2, … , aV},若使用a来对样本集D进行划分,则会产生V个分支结点,其中第v个分支结点包含了D中所有在属性a上取值为av的样本,记为Dv。那么信息增益为:
在这里插入图片描述
比如属性a是色泽,17个瓜中,|D|=17,|Dv|为17个瓜中色泽 = av(如青绿/乌黑/浅白)的个数,代入公式即可。一般而言,信息增益越大,则意味着使用属性a来进行划分所获得的"纯度提升"越大。

增益率

实际上,信息增益准则对可取值数目较多的属性有所偏好,为减少这种偏好可能带来的不利影响,著名的C4.5决策树算法使用"增益率" (gain ratio) 来选择最优划分属性:
在这里插入图片描述
需注意的是,增益率准则对可取值数目较少的属性有所偏好。因此,C4.5算法并不是直接选择增益率最大的候选划分属性,而是使用了一个启发式:先从候选划分属性中找出信息增益高于平均水平的属性,再从中选择增益率最高的。

基尼系数

CART决策树使用"基尼系数" (Gini index)来选择划分属性:
在这里插入图片描述
下面是两种划分的案例:
在这里插入图片描述
在这里插入图片描述
可以比对一下两种划分下的基尼系数:不划分时基尼系数约为0.444,第一种划分后基尼系数为0.417,第二种划分后基尼系数为0.338,基尼系数越低表示"纯度"越高,所以第二种划分更加优秀。

连续属性的划分

由于连续属性的可取值数目不再有限,因此不能直接根据连续属性的可取值来对结点进行划分。最简单的策略是采用二分法(bi-partition)对连续属性进行处理,这正是C4.5决策树算法中采用的机制。

比如成绩属性有{90,92,94,98}4个数据,那么划分点集合为{91,93,96},然后我们就可以像离散属性值一样使用上述三种方法来考察这些划分点,选取最优的划分点进行样本集合的划分,最后与其他属性比较就可以选出最优划分属性。

需注意的是,与离散属性不同,若当前结点划分属性为连续属性,该属性还可作为其后代结点的划分属性,比如在父结点上使用了“密度>0.381”,不会禁止在子结点上使用“密度<0.294”。

剪枝处理

剪枝(pruning)是决策树学习算法对付"过拟合"的主要手段。在决策树学习中,为了尽可能正确分类训练样本,结点划分过程将不断重复,有时会造成决策树分支过多,这时就可能因训练样本学得"太好"了,以致于把训练集自身的一些特点当作所有数据都具有的一般性质而导致过拟合。因此,可通过主动去掉一些分支来降低过拟合的风险。

决策树剪枝的基本策略有"预剪枝" (prepruning)和"后剪枝"(postpruning)

  • 预剪枝是指在决策树生成过程中,对每个结点在划分前先进行估计,若当前结点的划分不能带来决策树泛化性能提升,则停止划分并将当前结点标记为叶结点;

  • 预剪枝的一般方法有以下几种:1)控制决策树最大深度:如果决策树的层数已经达到指定深度,则停止生长;2) 控制树中父结点和子结点的最少样本量或比例

  • 后剪枝则是先从训练集生成一棵完整的决策树,然后自底向上地对非叶结点进行考察,若将该结点对应的子树替换为叶结点能带来决策树泛化性能提升,则将该子树替换为叶结点。

后剪枝决策树通常比预剪枝决策树保留了更多的分支。一般情形下,后剪枝决策树的欠拟合风险很小,泛化性能往往优于预剪枝决策树。但后剪枝过程是在生成完全决策树之后进行的,并且要自底向上地对树中的所有非叶结点进行逐一考察,因此其训练时间开销比预剪枝决策树要大得多。

基于决策树的信用评级

基本决策树模型

仍然使用(六十二)中的案例数据进行分析。注意scikit-Iearn只能处理数值型输入变量,不能处理缺失值。先对数据进行预处理:

import numpy as np
from sklearn.metrics import classification_report,roc_curve,auc
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
import pandas as pd
import matplotlib.pyplot as plt
accepts = pd.read_csv(r'C:\Users\accepts.csv').dropna(axis=0,how='any')
target=accepts['bad_ind']
data=accepts.loc[:,'bankruptcy_ind':'used_ind']#选取'bankruptcy_ind':'used_ind'列数据
#生成一些有意义的衍生指标
data['lti_temp'] = data['loan_amt']/data['tot_income'] 
data['lti_temp'] = data['lti_temp'].map (lambda x: 10 if x >= 10 else x)
del data['loan_amt']
data['bankruptcy_ind'] = data['bankruptcy_ind'].replace ({'N':0 , 'Y':1})
data.head()
Out[15]: 
   bankruptcy_ind  tot_derog  tot_tr  ...  veh_mileage  used_ind  lti_temp
0               0        7.0     9.0  ...      24000.0         1  2.625954
1               0        0.0    21.0  ...         22.0         0  4.197541
3               0        3.0    10.0  ...      10000.0         1  7.200000
4               0        0.0    10.0  ...         14.0         0  6.353292
5               1        2.0    15.0  ...          1.0         0  4.865319

[5 rows x 19 columns]

接下来,我们初始化一个决策树模型,并使用训练集进行训练。其中criterion = ‘gini’说明采用基尼系数作为树生长的判断依据;同时指定了树的最大深度max_depth为3;由于一个违约带来的损失远超一个非违约带来的收益,因此我们设置class_weight = {0:1,1:3},即bad_ind为0的样本权重为1,bad_ind为1的样本权重为3。

X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2,random_state=1234)
clf = DecisionTreeClassifier(criterion='gini', max_depth=3, class_weight={0:1,1:3},random_state=1234) 
#支持计算Entropy和GINI
clf.fit (X_train, y_train)
print(classification_report(y_test, clf.predict(X_test)))
              precision    recall  f1-score   support

           0       0.87      0.77      0.81       648
           1       0.39      0.57      0.46       173
    accuracy                           0.72       821
   macro avg       0.63      0.67      0.64       821
weighted avg       0.77      0.72      0.74       821

模型对违约用户(bad_ind = 1)识别的f1-score为46%,查全率57%,即可以识别出57%的违约用户。决策树模型中的feature_importances保存了变量的重要性,我们将它输出:

list(zip(data.columns,clf.feature_importances_))#变量重要性指标
Out[25]: 
[('bankruptcy_ind', 0.0),
 ('tot_derog', 0.0),
 ('tot_tr', 0.0),
 ('age_oldest_tr', 0.0),
 ('tot_open_tr', 0.0),
 ('tot_rev_tr', 0.0),
 ('tot_rev_debt', 0.0),
 ('tot_rev_line', 0.1575107029076355),
 ('rev_util', 0.0),
 ('fico_score', 0.6940581964952662),
 ('purch_price', 0.0),
 ('msrp', 0.0),
 ('down_pyt', 0.0),
 ('loan_term', 0.0),
 ('ltv', 0.14843110059709827),
 ('tot_income', 0.0),
 ('veh_mileage', 0.0),
 ('used_ind', 0.0),
 ('lti_temp', 0.0)]

可以看到,最重要的变量是’fico_score’ (重要性为0.694)。多数变量在该模型中并未用到,重要性皆为0。

决策树的可视化

安装graphviz库,可参见linllll:Graphviz安装配置,然后对上述基本决策树模型进行可视化:

#导入graphviz工具
import graphviz
#导入决策树中输出graphviz的接口
from sklearn.tree import export_graphviz
export_graphviz(clf, out_file='accepts.dot', class_names=['0','1'],\
feature_names=data.columns, impurity=True, filled=True)
#打开一个dot文件
with open('accepts.dot') as f:
    dot_graph = f.read()
#显示dot文件中的图形
graphviz.Source(dot_graph)

在这里插入图片描述
第一层中,可以看到决策树根结点输出了分割标准(fico_score <= 683.5),全体样本的基尼系数为0.483 ,在3284个样本中被预测变量为0的有2671个,为1的有1839个(由于设置了标签1的样本权重为3,因此分类为1的样本数 = y_train中1的样本数 × 3 = 1839个,以此来增加识别为1的概率)。由于预测为0的样本多,所以根结点预测所有样本为0。 满足分隔标准(True)的1396个样本被分到了左子结点,其余的被分到了右子结点。其他结点的分析与此类似。

我们可以将树的结构保存到pdf文件当中,执行完下列代码后会在工作目录下出现tree.pdf文件:

import pydotplus
dot_data = export_graphviz(clf, out_file=None) 
graph = pydotplus.graph_from_dot_data(dot_data)
graph.write_pdf ("tree.pdf")

参数搜索调优

建立模型最主要的就是设置模型的超参数,所谓超参数就是需要建模人员设定好,软件才可以根据数据得到的模型参数。比如岭回归中系数平方前面的惩罚项,设定好之后才可以计算模型的回归系数。决策树的超参数就是预剪枝算法中提到的那几项,比如树的深度、叶子结点中最小样本量等。

我们可以使用scikit-learn提供的参数搜索函数GridSearchCV。GridSearchCV使用交叉验证,对指定的参数网格ParameterGrid中所有的参数组合进行建模和效果验证,能够使得模型效果最好的那组参数就是我们要寻找的最优参数。

from sklearn.model_selection import ParameterGrid,GridSearchCV
max_depth = [None,]
max_leaf_nodes = np.arange(5,10,1)
class_weight = [{0:1,1:3}, {0:1,1:4}, 'balanced']
param_grid = {'max_depth': max_depth,'max_leaf_nodes': max_leaf_nodes,\
              'class_weight': class_weight}
#param_grid中参数个数为参数数量相乘,即1*3*5=15种参数组合
print(list(ParameterGrid(param_grid)))
clf_cv = GridSearchCV(estimator=clf, param_grid=param_grid, cv=5,scoring='roc_auc')
clf_cv.fit(X_train, y_train)
print(classification_report(y_test, clf.predict(X_test)))
clf_cv.best_params_
              precision    recall  f1-score   support

           0       0.87      0.77      0.81       648
           1       0.39      0.57      0.46       173
    accuracy                           0.72       821
   macro avg       0.63      0.67      0.64       821
weighted avg       0.77      0.72      0.74       821

Out[28]: {'class_weight': {0: 1, 1: 4}, 'max_depth': None, 'max_leaf_nodes': 6}

这里的训练实际上会构建1 × 5 × 3 × 5 = 75个决策树模型,其中第一个"5"是max_leaf_nodes参数的可能取值个数,“1” 是max_depth的可能取值个数,“3” 是类权重class_weight的取值个数,这三个参数的可能取值进行组合,共产生1 × 5 × 3 = 15种参数组合,最后乘以5是因为使用了5折交叉验证,相当于每种组合都要建模5次,因此总共要建模75次。GridSearchCV在训练后返回的是采用最优参数和所有训练集进行重新训练过的决策树模型。下面绘制该最优模型的ROC曲线:

from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
FPR_test,TPR_test,thr1=roc_curve(y_test,clf_cv.predict_proba(X_test)[:,1],pos_label=1)
FPR_train,TPR_train,thr2=roc_curve(y_train,clf_cv.predict_proba(X_train)[:,1],pos_label=1)
plt.plot(FPR_test,TPR_test,'b--')
plt.plot(FPR_train,TPR_train,'r-')
plt.title('ROC曲线')
plt.ylabel('真正例率')
plt.xlabel('假正例率')
plt.grid()
print(auc(FPR_test, TPR_test))
0.7146845786055807

在这里插入图片描述
可以看到训练集的ROC曲线(实线)与测试集的ROC曲线(虚线)很接近,说明模型没有过拟合。ROC曲线下面积AUC=0.7147。

参考文献

周志华《机器学习》,清华大学出版社
常国珍等《Python数据科学:技术详解与商业实践》,机械工业出版社

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值