当前位置:   article > 正文

使用决策树进行个人信用风险评估_使用uci上的德国信用数据集。该数据集包含了1000个贷款信息,每一个贷款有20个自变

使用uci上的德国信用数据集。该数据集包含了1000个贷款信息,每一个贷款有20个自变

决策树方法介绍

决策树简介

决策树(decision tree)是一种基本的分类与回归方法。如下图所示的流程图就是一个决策树,长方形代表判断模块(decision block),椭圆形成代表终止模块(terminating block),表示已经得出结论,可以终止运行。从判断模块引出的左右箭头称作为分支(branch),它可以达到另一个判断模块或者终止模块。我们还可以这样理解,分类决策树模型是一种描述对实例进行分类的树形结构。决策树由结点(node)和有向边(directed edge)组成。结点有两种类型:内部结点(internal node)和叶结点(leaf node)。内部结点表示一个特征或属性,叶结点表示一个类。

决策树的预测过程

我们可以把决策树看成一个if-then规则的集合,将决策树转换成if-then规则的过程是这样的:由决策树的根结点(root node)到叶结点(leaf node)的每一条路径构建一条规则;路径上内部结点的特征对应着规则的条件,而叶结点的类对应着规则的结论。决策树的路径或其对应的if-then规则集合具有一个重要的性质:互斥并且完备。这就是说,每一个实例都被一条路径或一条规则所覆盖,而且只被一条路径或一条规则所覆盖。这里所覆盖是指实例的特征与路径上的特征一致或实例满足规则的条件。

使用决策树做预测需要以下过程:

  • 收集数据:可以使用任何方法。比如想构建一个相亲系统,我们可以从媒婆那里,或者通过采访相亲对象获取数据。根据他们考虑的因素和最终的选择结果,就可以得到一些供我们利用的数据了。
  • 准备数据:收集完的数据,我们要进行整理,将这些所有收集的信息按照一定规则整理出来,并排版,方便我们进行后续处理。
  • 分析数据:可以使用任何方法,决策树构造完成之后,我们可以检查决策树图形是否符合预期。
  • 训练算法:这个过程也就是构造决策树,同样也可以说是决策树学习,就是构造一个决策树的数据结构。
  • 测试算法:使用经验树计算错误率。当错误率达到了可接收范围,这个决策树就可以投放使用了。
  • 使用算法:此步骤可以使用适用于任何监督学习算法,而使用决策树可以更好地理解数据的内在含义。

决策树的构建的准备工作

使用决策树做预测的每一步骤都很重要,数据收集不到位,将会导致没有足够的特征让我们构建错误率低的决策树。数据特征充足,但是不知道用哪些特征好,将会导致无法构建出分类效果好的决策树模型。从算法方面看,决策树的构建是我们的核心内容。

特征选择

特征选择在于选取对训练数据具有分类能力的特征。这样可以提高决策树学习的效率,如果利用一个特征进行分类的结果与随机分类的结果没有很大差别,则称这个特征是没有分类能力的。经验上扔掉这样的特征对决策树学习的精度影响不大。通常特征选择的标准是信息增益(information gain)或信息增益比

直观上,如果一个特征具有更好的分类能力,或者说,按照这一特征将训练数据集分割成子集,使得各个子集在当前条件下有最好的分类,那么就更应该选择这个特征。信息增益就能够很好地表示这一直观的准则。

划分数据集之后信息发生的变化称为信息增益,知道如何计算信息增益,我们就可以计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。

香农熵

集合信息的度量方式称为香农熵或者简称为熵(entropy),这个名字来源于信息论之父克劳德·香农。

熵定义为信息的期望值。在信息论与概率统计中,熵是表示随机变量不确定性的度量。如果待分类的事物可能划分在多个分类之中,则符号 x i x_i xi的信息定义为 :
l ( x i ) = − l o g 2 p ( x i ) l(x_i) = -log_2p(x_i) l(xi)=log2p(xi)

其中 p ( x i ) p(x_i) p(xi)是选择该分类的概率。通过上式,我们可以得到所有类别的信息。为了计算熵,我们需要计算所有类别所有可能值包含的信息期望值(数学期望),通过下面的公式得到:

H ( U ) = E [ − log ⁡ p i ] = ∑ i = 1 n − p i log ⁡ p i H(U)=E[-\log p_i]=\sum_{i=1}^n -p_i \log p_i H(U)=E[logpi]=i=1npilogpi

其中n是分类的数目。熵越大,随机变量的不确定性就越大。当熵中的概率由数据估计(特别是最大似然估计)得到时,所对应的熵称为经验熵(empirical entropy)

计算数据集D的经验熵

H ( D ) = − ∑ k = 1 k C k D log ⁡ C k D H(D)=-\sum_{k=1}^k{\frac{C_k}{D}\log{\frac{C_k}{D}}} H(D)=k=1kDCklogDCk

信息增益

信息增益是相对于特征而言的,信息增益越大,特征对最终的分类结果影响也就越大,我们就应该选择对最终分类结果影响最大的那个特征作为我们的分类特征。

在讲解信息增益定义之前,我们还需要明确一个概念,条件熵。

条件熵 H ( Y ∣ X ) H(Y|X) H(YX)表示在已知随机变量X的条件下随机变量Y的不确定性,随机变量X给定的条件下随机变量Y的条件熵(conditional entropy),定义为X给定条件下Y的条件概率分布的熵对X的数学期望:

H ( Y ∣ X ) = ∑ i = 1 n p i H ( Y ∣ X = x i ) H(Y|X)=\sum_{i=1}^np_iH(Y|X=x_i) H(YX)=i=1npiH(YX=xi)

当条件熵中的概率由数据估计(特别是极大似然估计)得到时,所对应的条件熵称为条件经验熵(empirical conditional entropy)。

特征 A A A对训练数据集 D D D的信息增益 g ( D , A ) g(D,A) g(D,A),定义为集合 D D D的经验熵 H ( D ) H(D) H(D)与特征 A A A给定条件下 D D D的经验条件熵 H ( D ∣ A ) H(D|A) H(DA)之差:

g ( D , A ) = H ( D ) − H ( D ∣ A ) g(D,A)=H(D)-H(D|A) g(D,A)=H(D)H(DA)

一般地,熵与条件熵之差称为互信息(mutual information)。决策树学习中的信息增益等价于训练数据集中类与特征的互信息。
决策树构建

从数据集构造决策树算法所需要的子功能模块,包括经验熵的计算最优特征的选择,其工作原理如下:得到原始数据集,然后基于最好的属性值划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分之后,数据集被向下传递到树的分支的下一个结点。在这个结点上,我们可以再次划分数据。因此我们可以采用递归的原则处理数据集。

决策树生成算法递归地产生决策树,直到不能继续下去未为止。这样产生的树往往对训练数据的分类很准确,但对未知的测试数据的分类却没有那么准确,即出现过拟合现象。过拟合的原因在于学习时过多地考虑如何提高对训练数据的正确分类,从而构建出过于复杂的决策树。解决这个问题的办法是考虑决策树的复杂度,对已生成的决策树进行简化(剪枝)。

ID3算法简介

ID3算法的核心是在决策树各个结点上对应信息增益准则选择特征,递归地构建决策树。具体方法是:

  1. 从根结点(root node)开始,对结点计算所有可能的特征的信息增益,选择信息增益最大的特征作为结点的特征,由该特征的不同取值建立子节点;
  2. 对子结点递归地调用以上方法,构建决策树;直到所有特征的信息增益均很小或没有特征可以选择为止。最后得到一个决策树。

ID3相当于用极大似然法进行概率模型的选择。

递归创建决策树时,递归有两个终止条件:第一个停止条件是所有的类标签完全相同,则直接返回该类标签;第二个停止条件是使用完了所有特征,仍然不能将数据划分仅包含唯一类别的分组,即决策树构建失败,特征不够用。

剪枝处理

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

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

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

预剪枝的一般方法有以下几种:

  1. 控制决策树最大深度:如果决策树的层数已经达到指定深度,则停止生长;

  2. 控制树中父结点和子结点的最少样本量或比例。

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

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

使用Sklearn构建决策树

sklearn.tree模块提供了决策树模型,用于解决分类问题和回归问题。官方文档

DecisionTreeClassifier是一个能够对数据集执行多类分类的类。与其他分类器一样,DecisionTreeClassifier将两个数组作为输入:稀疏或密集的形状X数组,用于保存训练样本;以及形状为整数的数组Y,shape ,用于保存训练样本的类标签:(n_samples,n_features)(n_samples,),拟合后,该模型可用于预测样本类别,如果存在多个具有相同且最高概率的类别,则分类器将预测那些类别中索引最低的类别。

个人信用风险评估

背景

2007-2008年的全球金融危机凸显了透明度和严密性在银行业务中的重要性。由于信贷供应收到了限制,所以银行正日益紧缩其贷款体系,转向机器学习来更准确地识别高风险贷款。

决策树模型准确性高可解释性好,所以被广泛地应用在银行业。在很多国家,政府机构会密切监控贷款业务,所以银行需要明确地解释为什么一个申请者的贷款申请被拒绝或者批准。这种可解释性对于贷款申请者也是很重要的,申请者需要知道为什么自己的信用级别不符合银行的要求

通过构建自动化的信用评分模型,以在线方式进行即时的信贷审批能够为银行节约很多人工成本。 本案例,我们将使用C5.0决策树算法建立一个简单的个人信用风险评估模型。

模型构建

数据源

使用UCI上的德国信用数据集。该数据集包含了1000个贷款信息,每一个贷款有20个自变量和一个类变量记录该笔贷款是否违约。
我们将使用该数据集构建模型来预测贷款是否违约。

数据探索和预处理

首先,使用Pandas的read_csv()函数读入数据。

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

credit = pd.read_csv("./input/credit.csv")
credit.head(5)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
checking_balancemonths_loan_durationcredit_historypurposeamountsavings_balanceemployment_lengthinstallment_ratepersonal_statusother_debtors...propertyageinstallment_planhousingexisting_creditsjobdependentstelephoneforeign_workerdefault
0< 0 DM6criticalradio/tv1169unknown> 7 yrs4single malenone...real estate67noneown2skilled employee1yesyes1
11 - 200 DM48repaidradio/tv5951< 100 DM1 - 4 yrs2femalenone...real estate22noneown1skilled employee1noneyes2
2unknown12criticaleducation2096< 100 DM4 - 7 yrs2single malenone...real estate49noneown1unskilled resident2noneyes1
3< 0 DM42repaidfurniture7882< 100 DM4 - 7 yrs2single maleguarantor...building society savings45nonefor free1skilled employee2noneyes1
4< 0 DM24delayedcar (new)4870< 100 DM1 - 4 yrs3single malenone...unknown/none53nonefor free2skilled employee2noneyes2

5 rows × 21 columns

可以看到,该数据集包含1000个样本和21个变量。变量类型同时包括因子变量和数值变量。
使用value_counts()函数对支票余额变量check_balance和储蓄账户余额变量savings_balance进行查看。

credit.checking_balance.value_counts()
  • 1
unknown       394
< 0 DM        274
1 - 200 DM    269
> 200 DM       63
Name: checking_balance, dtype: int64
  • 1
  • 2
  • 3
  • 4
  • 5
credit.savings_balance.value_counts()
  • 1
< 100 DM         603
unknown          183
101 - 500 DM     103
501 - 1000 DM     63
> 1000 DM         48
Name: savings_balance, dtype: int64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

上述两个变量的单位都是德国马克(Deutsche Mark, DM)。 直观来看,支票余额和储蓄账户余额越大,贷款违约的可能性越小。

该贷款数据集还有一些数值型变量,例如贷款期限(months_loan_duration)贷款申请额度(amount)

credit[["months_loan_duration","amount"]].describe()
  • 1
months_loan_durationamount
count1000.0000001000.000000
mean20.9030003271.258000
std12.0588142822.736876
min4.000000250.000000
25%12.0000001365.500000
50%18.0000002319.500000
75%24.0000003972.250000
max72.00000018424.000000
credit[["months_loan_duration","amount"]].median()
  • 1
months_loan_duration      18.0
amount                  2319.5
dtype: float64
  • 1
  • 2
  • 3

可见,贷款期限为4~72个月,中位数为18个月。贷款申请额度在250~18420马克之间,中位数为2320马克。

变量default表示贷款是否违约,也是我们需要预测的目标变量。 在1000个贷款中,30%贷款申请者有违约行为。

credit.default.value_counts()
  • 1
1    700
2    300
Name: default, dtype: int64
  • 1
  • 2
  • 3

银行不太希望贷款给违约率高的客户,因为这些客户会给银行带来损失。 如果建模的目标是识别可能违约的客户,从未减少违约数量。

划分训练集和测试集

在正式建模之前,我们需要将数据集分为训练集测试集两部分。其中训练集用来构建决策树模型,测试集用来评估模型性能。

我们将使用70%数据作为训练数据,30%作为训练数据。 在划分数据之前,我们还需要将数据中字符串形式的变量使用整数进行编码

col_dicts = {}
cols = ['checking_balance','credit_history', 'purpose', 'savings_balance', 'employment_length', 'personal_status', 
        'other_debtors','property','installment_plan','housing','job','telephone','foreign_worker']

col_dicts = {'checking_balance': {'1 - 200 DM': 2,
  '< 0 DM': 1,
  '> 200 DM': 3,
  'unknown': 0},
 'credit_history': {'critical': 0,
  'delayed': 2,
  'fully repaid': 3,
  'fully repaid this bank': 4,
  'repaid': 1},
 'employment_length': {'0 - 1 yrs': 1,
  '1 - 4 yrs': 2,
  '4 - 7 yrs': 3,
  '> 7 yrs': 4,
  'unemployed': 0},
 'foreign_worker': {'no': 1, 'yes': 0},
 'housing': {'for free': 1, 'own': 0, 'rent': 2},
 'installment_plan': {'bank': 1, 'none': 0, 'stores': 2},
 'job': {'mangement self-employed': 3,
  'skilled employee': 2,
  'unemployed non-resident': 0,
  'unskilled resident': 1},
 'other_debtors': {'co-applicant': 2, 'guarantor': 1, 'none': 0},
 'personal_status': {'divorced male': 2,
  'female': 1,
  'married male': 3,
  'single male': 0},
 'property': {'building society savings': 1,
  'other': 3,
  'real estate': 0,
  'unknown/none': 2},
 'purpose': {'business': 5,
  'car (new)': 3,
  'car (used)': 4,
  'domestic appliances': 6,
  'education': 1,
  'furniture': 2,
  'others': 8,
  'radio/tv': 0,
  'repairs': 7,
  'retraining': 9},
 'savings_balance': {'101 - 500 DM': 2,
  '501 - 1000 DM': 3,
  '< 100 DM': 1,
  '> 1000 DM': 4,
  'unknown': 0},
 'telephone': {'none': 1, 'yes': 0}}

for col in cols:
    credit[col] = credit[col].map(col_dicts[col])
    
credit.head(5)
  • 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
checking_balancemonths_loan_durationcredit_historypurposeamountsavings_balanceemployment_lengthinstallment_ratepersonal_statusother_debtors...propertyageinstallment_planhousingexisting_creditsjobdependentstelephoneforeign_workerdefault
01600116904400...06700221001
124810595112210...02200121102
201201209613200...04900112101
314212788213201...14501122101
412423487012300...25301222102

5 rows × 21 columns

现在,我们从数据集中随机选取其中70%作为训练数据,30%作为测试数据。

注明:
pandas以类似字典的方式来获取某一列的值,比如df[A],这会得到df的A列。如果我们要获取到行值,这个时候有两种方法,一种是iloc方法,另一种方法是loc方法。loc是指location的意思,iloc中的i是指integer。这两者的区别如下:
loc:works on labels in the index.
iloc:works on the positions in the index (so it only takes integers).
也就是说loc是根据index来索引,比如下边的df定义了一个index,那么loc就根据这个index来索引对应的行。iloc并不是根据index来索引,而是根据行号来索引,行号从0开始,逐次加1。

from sklearn import model_selection

y = credit['default']
#del credit['default']
X  = credit.loc[:,'checking_balance':'foreign_worker']
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, test_size=0.3, random_state=1)
print(X_train.columns)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
Index(['checking_balance', 'months_loan_duration', 'credit_history', 'purpose',
       'amount', 'savings_balance', 'employment_length', 'installment_rate',
       'personal_status', 'other_debtors', 'residence_history', 'property',
       'age', 'installment_plan', 'housing', 'existing_credits', 'job',
       'dependents', 'telephone', 'foreign_worker'],
      dtype='object')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们验证一下训练集和测试集中,违约贷款的比例是否接近。

print (y_train.value_counts()/len(y_train))
print (y_test.value_counts()/len(y_test))
print(y_train.value_counts())
  • 1
  • 2
  • 3
1    0.694286
2    0.305714
Name: default, dtype: float64
1    0.713333
2    0.286667
Name: default, dtype: float64
1    486
2    214
Name: default, dtype: int64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

可见,训练集和测试集中违约贷款比例均接近30%。

绘制决策区域

如果输入的数据是一个L维空间特征,考虑一个M分类问题,那么分类器将会把这个L维空间的特征点分为M个区域。每个区域显然就属于一个类别,如果输入一个点x落在第i个区域,那么x就属于第i类。分割成这些区域的边界就称为决策面

  • 一维特征空间:决策面是一个点
  • 二维特征空间:决策面是一条线
  • 三维特征空间:决策面是超平面(Hyperplane)

决策区域是由决策面决定的两两特征相决策,这里可以简单地对于数据集中的前四个特征checking_balancemonths_loan_durationcredit_historypurpose作出由训练样本推断出的简单阈值规则组合而成的决策边界。其他特征也可以如此做。

from sklearn import tree
from sklearn.tree import DecisionTreeClassifier
# Parameters
n_classes = 3
plot_colors = "ryb"
plot_step = 0.02

for pairidx, pair in enumerate([[0, 1], [0, 2], [0, 3],
                                [1, 2], [1, 3], [2, 3]]):
    # We only take the two corresponding features
    X = credit.iloc[:, pair]
    y = credit['default']

    # Train
    clf = DecisionTreeClassifier().fit(X, y)

    # Plot the decision boundary
    plt.subplot(2, 3, pairidx + 1)

    x_min, x_max = X.values[:, 0].min() - 1, X.values[:, 1].max() + 1
    y_min, y_max = X.values[:, 0].min() - 1, X.values[:, 1].max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, plot_step),
                         np.arange(y_min, y_max, plot_step))
    plt.tight_layout(h_pad=0.5, w_pad=0.5, pad=2.5)

    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    cs = plt.contourf(xx, yy, Z, cmap=plt.cm.RdYlBu)

    plt.xlabel(X.columns[0])
    plt.ylabel(X.columns[1])

    # Plot the training points
    for i, color in zip(range(n_classes), plot_colors):
        idx = np.where(y == i)
        plt.scatter(X.values[idx, 0], X.values[idx, 1], c=color,label = credit.columns[i],
                    cmap=plt.cm.RdYlBu, edgecolor='black', s=15)

plt.suptitle("Decision surface of a decision tree using paired features")
plt.legend(loc='lower right', borderpad=0, handletextpad=0)
plt.axis("tight")
  • 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
(-1.0, 9.98000000000001, -1.0, 9.98000000000001)
  • 1

在这里插入图片描述

模型训练

我们将使用Scikit-learn中的DecisionTreeClassifier算法来训练决策树模型。

DecisionTreeClassifier算法位于sklearn.tree包,首先将其导入,然后调用fit()方法进行模型训练,用tree.plot_tree()可以初步简略地绘出决策树。

重要参数:criterion{“gini”, “entropy”},衡量分割质量的功能。支持的标准是对基尼杂质的“gini”和对信息增益的“entropy”。在本例中选择重要衡量标准为信息熵entropy

from sklearn import tree
from sklearn.tree import DecisionTreeClassifier
credit_model = DecisionTreeClassifier(criterion='entropy',min_samples_leaf = 6,random_state=1)
credit_model.fit(X_train, y_train)
  • 1
  • 2
  • 3
  • 4
DecisionTreeClassifier(criterion='entropy', min_samples_leaf=6, random_state=1)
  • 1

现在,credit_model就是我们训练得到的决策树模型。

查看特征选择依据

决策树模型中的feature_importances保存了变量的重要性(本例中为熵),我们将它输出:

list(zip(X_train.columns,credit_model.feature_importances_))#变量重要性指标

  • 1
  • 2
[('checking_balance', 0.1784263521005156),
 ('months_loan_duration', 0.06531501926631156),
 ('credit_history', 0.07396106738202872),
 ('purpose', 0.04628571645014363),
 ('amount', 0.13497578880485295),
 ('savings_balance', 0.05368602270859771),
 ('employment_length', 0.053266897842204876),
 ('installment_rate', 0.032551832085956384),
 ('personal_status', 0.010488591098276568),
 ('other_debtors', 0.0911632319197171),
 ('residence_history', 0.004046006705158203),
 ('property', 0.06152610458217522),
 ('age', 0.1179438710337618),
 ('installment_plan', 0.028054660763281884),
 ('housing', 0.013923676945832424),
 ('existing_credits', 0.01970767408219164),
 ('job', 0.0),
 ('dependents', 0.0),
 ('telephone', 0.014677486228993656),
 ('foreign_worker', 0.0)]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

可以看到,最重要的变量是checking_balance (重要性为 0.1784263521005156)。某些变量特征在该模型中并未用到,重要性皆为0。

决策树的可视化

现通过可视化将其展示出来,我们引入graphviz可视化工具包。可以使用导出器以Graphviz格式导出树export_graphviz 。如果使用conda软件包管理器,则可以使用安装Graphviz二进制文件和python软件包。conda install python-graphviz

另外,可以从graphviz项目主页下载graphviz的二进制文件,并使用pypi安装Python包装器。pip install graphviz

export_graphviz出口也支持多种美学选项,包括可以通过类着色节点(或值回归)和如果需要的话使用显式的变量和类名称。Jupyter笔记本还会自动内联渲染这些图:

import graphviz 
dot_data = tree.export_graphviz(credit_model, out_file=None,
                                feature_names=X_train.columns,  
                                class_names=['no default','default'],  
                     filled=True, rounded=True,  
                     special_characters=True) 
graph = graphviz.Source(dot_data) 
graph 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述

第一层中,可以看到决策树根结点输出了分割标准(checking_balance ≤ 0.5),全体样本的信息熵为entropy = 0.888 ,在700个样本中被预测变量为0的有486个,为1的有214个。由于预测为0的样本多,所以根结点预测所有样本为0。 满足分隔标准(True)的276个样本被分到了左子结点,其余的被分到了右子结点。其他结点的分析与此类似。

模型性能评估

指标和评分:量化预测的质量

sklearn.metrics模块实现了多种损失,评分和实用功能,以衡量分类性能。某些指标可能需要对正类的概率估计,置信度值或二元决策值。大多数实现都允许每个样本通过sample_weight参数为总得分提供加权贡献。
分类评估指标

对本例使用混淆矩阵与准确率进行评估

为了将我们训练好的决策树模型应用于测试数据,我们使用predict()函数,代码如下:

credit_pred = credit_model.predict(X_test)
  • 1

现在,我们得到了决策树模型在测试数据上的预测结果,通过将预测结果和真实结果进行对比可以评估模型性能。 可以使用sklearn.metrics包中的classification_report()confusion_matrix()函数,展示模型分类结果:

from sklearn import metrics
print ('回归评估报告:')
print (metrics.classification_report(y_test, credit_pred))
print ('混淆矩阵:')
print (metrics.confusion_matrix(y_test, credit_pred))
print ('正确率:')
print (metrics.accuracy_score(y_test, credit_pred))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
回归评估报告:
              precision    recall  f1-score   support

           1       0.78      0.84      0.81       214
           2       0.51      0.42      0.46        86

    accuracy                           0.72       300
   macro avg       0.64      0.63      0.63       300
weighted avg       0.70      0.72      0.71       300

混淆矩阵:
[[179  35]
 [ 50  36]]
正确率:
0.7166666666666667
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在300个贷款申请测试数据中,模型的预测正确率(Accuracy)为71.7%。 214个未违约贷款中,模型正确预测了82%。

回归评估报告中,对于违约贷款数据,实际有86个违约贷款,而召回率recall=0.4386个违约贷款中,模型正确预测出了43%。

下面,我们看看是否能够进一步改善模型的性能。

模型性能提升

在实际应用中,模型的预测正确率不高,很难将其应用到实时的信贷评审过程。 在本案例中,如果一个模型将所有的贷款都预测为“未违约”,此时模型的正确率将为72%,而该模型是一个完全无用的模型。 上节中我们建立的模型,正确率为70.7%,但是对于违约贷款的识别性能很差。 我们可以通过创建一个代价矩阵定义模型犯不同错误时的代价。 假设我们认为一个贷款违约者给银行带来的损失是银行错过一个不违约的贷款带来损失的4倍,则未违约和违约的代价权重可以定义为:class_weights = {1:1, 2:4},在DecisionTreeClassifier分类器中设置代价权重class_weight = class_weights,可得到新的决策树:

class_weights = {1:1, 2:4}
credit_model_cost = DecisionTreeClassifier(criterion='entropy',max_depth=6,class_weight = class_weights)
credit_model_cost.fit(X_train, y_train)
credit_pred_cost = credit_model_cost.predict(X_test)

print (metrics.classification_report(y_test, credit_pred_cost))
print (metrics.confusion_matrix(y_test, credit_pred_cost))
print (metrics.accuracy_score(y_test, credit_pred_cost))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
              precision    recall  f1-score   support

           1       0.88      0.44      0.59       214
           2       0.38      0.85      0.52        86

    accuracy                           0.56       300
   macro avg       0.63      0.64      0.55       300
weighted avg       0.74      0.56      0.57       300

[[ 94 120]
 [ 13  73]]
0.5566666666666666
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

由于设置了标签2(default=2,违约)的样本权重为4,因此分类为2的样本数 = y_train中标签2的样本数 × 3 = 1458个,以此来增加识别标签为2的概率)

在300个贷款申请测试数据中,模型的预测正确率(Accuracy)为55.7%。

回归评估报告中,对于违约贷款数据,实际有86个违约贷款,而召回率recall=0.8586个违约贷款中,模型正确预测出了85%。

整体而言,模型的整体正确率下降为59%,但是此时的模型能将86个违约贷款中的73个正确识别,识别率为85%。

dot_data_cost = tree.export_graphviz(credit_model_cost, out_file=None,
                                feature_names=X_train.columns,  
                                class_names=['no default','default'],  
                     filled=True, rounded=True,  
                     special_characters=True) 
graph = graphviz.Source(dot_data_cost) 
graph 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在这里插入图片描述

参数搜索调优

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

  • 树的深度max_depth
  • 叶子结点中样本量max_leaf_nodes
  • 代价权重class_weight
    等。
参数搜索函数GridSearchCV()

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

from sklearn.model_selection import ParameterGrid,GridSearchCV
max_depth = [None,]
max_leaf_nodes = np.arange(5,10,1)
#设置未违约:违约的类权重分别为1:1、1:3、1:4、1:5
class_weight = ['balanced',{1:1,2:3}, {1:1,2:4},{1:1,2:5}]
param_grid = {'max_depth': max_depth,
              'max_leaf_nodes': max_leaf_nodes,
              'class_weight': class_weight}

from sklearn.metrics import make_scorer

#定义特异性(specificity)计算函数
def specificity(y, y_hat): ##真值、预测值
#confusion matrix->a numpy.ndarray object
    mc = metrics.confusion_matrix(y, y_hat) ##构造混淆矩阵
#’’negative’’ is the first row (index 0) of the matrix import numpy
    res = mc[1,1]/np.sum(mc[1,:]) ##计算指标
    return res

#将上述函数作为评价函数
specificite = metrics.make_scorer(specificity, greater_is_better = True)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

这里的训练实际上会构建1 × 5 × 4 × 5 = 100个决策树模型,其中第一个"5"是max_leaf_nodes参数的可能取值个数,“1” 是max_depth的可能取值个数,“4” 是代价权重class_weight的取值个数,这三个参数的可能取值进行组合,共产生1 × 5 × 4 = 20种参数组合,最后乘以5是因为使用了5折交叉验证,相当于每种组合都要建模5次,因此总共要建模100次。

GridSearchCV在训练后返回的是采用最优参数所有训练集进行重新训练过的决策树模型。

scoring参数的说明: Scoringparameter是采用交叉验证的模型评估工具,GridSearchCVscoring参数:

scoring:str, callable, list, tuple or dict, default=None

Strategy to evaluate the performance of the cross-validated model on the test set.If scoring represents a single score, one can use:

  • a single string (see The scoring parameter: defining model evaluation rules);
  • a callable (see Defining your scoring strategy from metric functions) that returns a single value.

在上例中我们采用了第二种方法,即调用make_scorer()函数自行创建一个评估标准,此处为特异性(specificity),再将这个评估标准作为scoring的参数进行参数调优。

参数调优
#param_grid中参数个数为参数数量相乘,即1*3*5=15种参数组合
for i in list(ParameterGrid(param_grid)):
    print(i)
    
clf_cv = GridSearchCV(estimator=credit_model, param_grid=param_grid, cv=5,scoring=specificite)
clf_cv.fit(X_train, y_train)
print(metrics.classification_report(y_test, clf_cv.predict(X_test)))
clf_cv.best_params_

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
{'class_weight': 'balanced', 'max_depth': None, 'max_leaf_nodes': 5}
{'class_weight': 'balanced', 'max_depth': None, 'max_leaf_nodes': 6}
{'class_weight': 'balanced', 'max_depth': None, 'max_leaf_nodes': 7}
{'class_weight': 'balanced', 'max_depth': None, 'max_leaf_nodes': 8}
{'class_weight': 'balanced', 'max_depth': None, 'max_leaf_nodes': 9}
{'class_weight': {1: 1, 2: 3}, 'max_depth': None, 'max_leaf_nodes': 5}
{'class_weight': {1: 1, 2: 3}, 'max_depth': None, 'max_leaf_nodes': 6}
{'class_weight': {1: 1, 2: 3}, 'max_depth': None, 'max_leaf_nodes': 7}
{'class_weight': {1: 1, 2: 3}, 'max_depth': None, 'max_leaf_nodes': 8}
{'class_weight': {1: 1, 2: 3}, 'max_depth': None, 'max_leaf_nodes': 9}
{'class_weight': {1: 1, 2: 4}, 'max_depth': None, 'max_leaf_nodes': 5}
{'class_weight': {1: 1, 2: 4}, 'max_depth': None, 'max_leaf_nodes': 6}
{'class_weight': {1: 1, 2: 4}, 'max_depth': None, 'max_leaf_nodes': 7}
{'class_weight': {1: 1, 2: 4}, 'max_depth': None, 'max_leaf_nodes': 8}
{'class_weight': {1: 1, 2: 4}, 'max_depth': None, 'max_leaf_nodes': 9}
{'class_weight': {1: 1, 2: 5}, 'max_depth': None, 'max_leaf_nodes': 5}
{'class_weight': {1: 1, 2: 5}, 'max_depth': None, 'max_leaf_nodes': 6}
{'class_weight': {1: 1, 2: 5}, 'max_depth': None, 'max_leaf_nodes': 7}
{'class_weight': {1: 1, 2: 5}, 'max_depth': None, 'max_leaf_nodes': 8}
{'class_weight': {1: 1, 2: 5}, 'max_depth': None, 'max_leaf_nodes': 9}
              precision    recall  f1-score   support

           1       0.89      0.32      0.47       214
           2       0.35      0.91      0.50        86

    accuracy                           0.49       300
   macro avg       0.62      0.61      0.49       300
weighted avg       0.74      0.49      0.48       300






{'class_weight': {1: 1, 2: 5}, 'max_depth': None, 'max_leaf_nodes': 5}
  • 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

可以看到,将标签2即违约数的召回率(即混淆矩阵中得到的特异性)作为参数评估调优标准时,标签2的类权重越大,违约贷款中的正确识别率越高。当类权重class_weight为{1: 1, 2: 5},叶子节点数为5时在以上20种建模种的拟合效果好,实际有86个违约贷款,而召回率recall=0.9186个违约贷款中,模型正确预测出了91%。

绘制出新的决策树如下图:

credit_model_pro = DecisionTreeClassifier(criterion='entropy',min_samples_leaf = 6,random_state=1,class_weight= {1: 1, 2: 5},max_depth= None, max_leaf_nodes= 5)
credit_model_pro.fit(X_train, y_train)

dot_data_pro = tree.export_graphviz(credit_model_pro, out_file=None,
                                feature_names=X_train.columns,  
                                class_names=['no default','default'],  
                     filled=True, rounded=True,  
                     special_characters=True) 
graph = graphviz.Source(dot_data_pro) 
graph 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

决策树总结

决策树的一些优点是:

  • 易于理解和解释。树木可以可视化。
  • 需要很少的数据准备。其他技术通常需要数据规范化,需要创建伪变量并删除空白值。但是请注意,此模块不支持缺少的值。
  • 使用树的成本(即预测数据)与用于训练树的数据点数量成对数。
  • 能够处理数字和分类数据。但是,scikit-learn实现暂时不支持分类变量。其他技术通常专用于分析仅具有一种类型的变量的数据集。有关更多信息,请参见算法。
  • 能够处理多输出问题。
  • 使用白盒模型。如果模型中可以观察到给定的情况,则可以通过布尔逻辑轻松解释条件。相反,在黑匣子模型中(例如,在人工神经网络中),结果可能更难以解释。
  • 可以使用统计测试来验证模型。这使得考虑模型的可靠性成为可能。
  • 即使生成数据的真实模型在某种程度上违背了它的假设,也可以表现良好。

决策树的缺点包括:

  • 决策树学习者可能会创建过于复杂的树,从而无法很好地概括数据。这称为过度拟合。为避免此问题,必须使用诸如修剪,设置叶节点处所需的最小样本数或设置树的最大深度之类的机制。
  • 决策树可能不稳定,因为数据中的细微变化可能会导致生成完全不同的树。通过使用集成中的决策树可以缓解此问题。
  • 决策树的预测既不是平滑的也不是连续的,而是分段恒定的近似值,如上图所示。因此,他们不擅长外推。
  • 在最优性的几个方面,甚至对于简单的概念,学习最优决策树的问题都被认为是NP完全的。因此,实用的决策树学习算法基于启发式算法(例如贪婪算法),其中在每个节点上做出局部最优决策。这样的算法不能保证返回全局最优决策树。可以通过在整体学习器中训练多棵树来缓解这种情况,在该学习器中,特征和样本将通过替换随机抽样。
  • 有些概念很难学习,因为决策树无法轻松表达它们,例如XOR,奇偶校验或多路复用器问题。
  • 如果某些类别占主导地位,决策树学习者会创建有偏见的树。因此,建议在与决策树拟合之前平衡数据集
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/157704
推荐阅读
相关标签
  

闽ICP备14008679号