赞
踩
决策树(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=1∑n−pilogpi
其中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=1∑kDCklogDCk
信息增益是相对于特征而言的,信息增益越大,特征对最终的分类结果影响也就越大,我们就应该选择对最终分类结果影响最大的那个特征作为我们的分类特征。
在讲解信息增益定义之前,我们还需要明确一个概念,条件熵。
条件熵 H ( Y ∣ X ) H(Y|X) H(Y∣X)表示在已知随机变量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(Y∣X)=i=1∑npiH(Y∣X=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(D∣A)之差:
g ( D , A ) = H ( D ) − H ( D ∣ A ) g(D,A)=H(D)-H(D|A) g(D,A)=H(D)−H(D∣A)
一般地,熵与条件熵之差称为互信息(mutual information)。决策树学习中的信息增益等价于训练数据集中类与特征的互信息。
决策树构建
从数据集构造决策树算法所需要的子功能模块,包括经验熵的计算和最优特征的选择,其工作原理如下:得到原始数据集,然后基于最好的属性值划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分之后,数据集被向下传递到树的分支的下一个结点。在这个结点上,我们可以再次划分数据。因此我们可以采用递归的原则处理数据集。
决策树生成算法递归地产生决策树,直到不能继续下去未为止。这样产生的树往往对训练数据的分类很准确,但对未知的测试数据的分类却没有那么准确,即出现过拟合现象。过拟合的原因在于学习时过多地考虑如何提高对训练数据的正确分类,从而构建出过于复杂的决策树。解决这个问题的办法是考虑决策树的复杂度,对已生成的决策树进行简化(剪枝)。
ID3算法的核心是在决策树各个结点上对应信息增益准则选择特征,递归地构建决策树。具体方法是:
ID3相当于用极大似然法进行概率模型的选择。
递归创建决策树时,递归有两个终止条件:第一个停止条件是所有的类标签完全相同,则直接返回该类标签;第二个停止条件是使用完了所有特征,仍然不能将数据划分仅包含唯一类别的分组,即决策树构建失败,特征不够用。
剪枝(pruning)是决策树学习算法对付"过拟合"的主要手段。在决策树学习中,为了尽可能正确分类训练样本,结点划分过程将不断重复,有时会造成决策树分支过多,这时就可能因训练样本学得"太好"了,以致于把训练集自身的一些特点当作所有数据都具有的一般性质而导致过拟合。因此,可通过主动去掉一些分支来降低过拟合的风险。
决策树剪枝的基本策略有"预剪枝" (prepruning)和"后剪枝"(postpruning) 。
预剪枝的一般方法有以下几种:
控制决策树最大深度:如果决策树的层数已经达到指定深度,则停止生长;
控制树中父结点和子结点的最少样本量或比例。
后剪枝决策树通常比预剪枝决策树保留了更多的分支。一般情形下,后剪枝决策树的欠拟合风险很小,泛化性能往往优于预剪枝决策树。但后剪枝过程是在生成完全决策树之后进行的,并且要自底向上地对树中的所有非叶结点进行逐一考察,因此其训练时间开销比预剪枝决策树要大得多。
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)
checking_balance | months_loan_duration | credit_history | purpose | amount | savings_balance | employment_length | installment_rate | personal_status | other_debtors | ... | property | age | installment_plan | housing | existing_credits | job | dependents | telephone | foreign_worker | default | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | < 0 DM | 6 | critical | radio/tv | 1169 | unknown | > 7 yrs | 4 | single male | none | ... | real estate | 67 | none | own | 2 | skilled employee | 1 | yes | yes | 1 |
1 | 1 - 200 DM | 48 | repaid | radio/tv | 5951 | < 100 DM | 1 - 4 yrs | 2 | female | none | ... | real estate | 22 | none | own | 1 | skilled employee | 1 | none | yes | 2 |
2 | unknown | 12 | critical | education | 2096 | < 100 DM | 4 - 7 yrs | 2 | single male | none | ... | real estate | 49 | none | own | 1 | unskilled resident | 2 | none | yes | 1 |
3 | < 0 DM | 42 | repaid | furniture | 7882 | < 100 DM | 4 - 7 yrs | 2 | single male | guarantor | ... | building society savings | 45 | none | for free | 1 | skilled employee | 2 | none | yes | 1 |
4 | < 0 DM | 24 | delayed | car (new) | 4870 | < 100 DM | 1 - 4 yrs | 3 | single male | none | ... | unknown/none | 53 | none | for free | 2 | skilled employee | 2 | none | yes | 2 |
5 rows × 21 columns
可以看到,该数据集包含1000个样本和21个变量。变量类型同时包括因子变量和数值变量。
使用value_counts()
函数对支票余额变量check_balance
和储蓄账户余额变量savings_balance
进行查看。
credit.checking_balance.value_counts()
unknown 394
< 0 DM 274
1 - 200 DM 269
> 200 DM 63
Name: checking_balance, dtype: int64
credit.savings_balance.value_counts()
< 100 DM 603
unknown 183
101 - 500 DM 103
501 - 1000 DM 63
> 1000 DM 48
Name: savings_balance, dtype: int64
上述两个变量的单位都是德国马克(Deutsche Mark, DM)。 直观来看,支票余额和储蓄账户余额越大,贷款违约的可能性越小。
该贷款数据集还有一些数值型变量,例如贷款期限(months_loan_duration)
和贷款申请额度(amount)
。
credit[["months_loan_duration","amount"]].describe()
months_loan_duration | amount | |
---|---|---|
count | 1000.000000 | 1000.000000 |
mean | 20.903000 | 3271.258000 |
std | 12.058814 | 2822.736876 |
min | 4.000000 | 250.000000 |
25% | 12.000000 | 1365.500000 |
50% | 18.000000 | 2319.500000 |
75% | 24.000000 | 3972.250000 |
max | 72.000000 | 18424.000000 |
credit[["months_loan_duration","amount"]].median()
months_loan_duration 18.0
amount 2319.5
dtype: float64
可见,贷款期限为4~72个月,中位数为18个月。贷款申请额度在250~18420马克之间,中位数为2320马克。
变量default表示贷款是否违约,也是我们需要预测的目标变量。 在1000个贷款中,30%贷款申请者有违约行为。
credit.default.value_counts()
1 700
2 300
Name: default, dtype: int64
银行不太希望贷款给违约率高的客户,因为这些客户会给银行带来损失。 如果建模的目标是识别可能违约的客户,从未减少违约数量。
在正式建模之前,我们需要将数据集分为训练集和测试集两部分。其中训练集用来构建决策树模型,测试集用来评估模型性能。
我们将使用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)
checking_balance | months_loan_duration | credit_history | purpose | amount | savings_balance | employment_length | installment_rate | personal_status | other_debtors | ... | property | age | installment_plan | housing | existing_credits | job | dependents | telephone | foreign_worker | default | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 6 | 0 | 0 | 1169 | 0 | 4 | 4 | 0 | 0 | ... | 0 | 67 | 0 | 0 | 2 | 2 | 1 | 0 | 0 | 1 |
1 | 2 | 48 | 1 | 0 | 5951 | 1 | 2 | 2 | 1 | 0 | ... | 0 | 22 | 0 | 0 | 1 | 2 | 1 | 1 | 0 | 2 |
2 | 0 | 12 | 0 | 1 | 2096 | 1 | 3 | 2 | 0 | 0 | ... | 0 | 49 | 0 | 0 | 1 | 1 | 2 | 1 | 0 | 1 |
3 | 1 | 42 | 1 | 2 | 7882 | 1 | 3 | 2 | 0 | 1 | ... | 1 | 45 | 0 | 1 | 1 | 2 | 2 | 1 | 0 | 1 |
4 | 1 | 24 | 2 | 3 | 4870 | 1 | 2 | 3 | 0 | 0 | ... | 2 | 53 | 0 | 1 | 2 | 2 | 2 | 1 | 0 | 2 |
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)
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')
我们验证一下训练集和测试集中,违约贷款的比例是否接近。
print (y_train.value_counts()/len(y_train))
print (y_test.value_counts()/len(y_test))
print(y_train.value_counts())
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
可见,训练集和测试集中违约贷款比例均接近30%。
如果输入的数据是一个L维空间特征,考虑一个M分类问题,那么分类器将会把这个L维空间的特征点分为M个区域。每个区域显然就属于一个类别,如果输入一个点x落在第i个区域,那么x就属于第i类。分割成这些区域的边界就称为决策面。
决策区域是由决策面决定的两两特征相决策,这里可以简单地对于数据集中的前四个特征checking_balance
、months_loan_duration
、credit_history
、purpose
作出由训练样本推断出的简单阈值规则组合而成的决策边界。其他特征也可以如此做。
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.0, 9.98000000000001, -1.0, 9.98000000000001)
我们将使用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)
DecisionTreeClassifier(criterion='entropy', min_samples_leaf=6, random_state=1)
现在,credit_model
就是我们训练得到的决策树模型。
决策树模型中的feature_importances
保存了变量的重要性(本例中为熵),我们将它输出:
list(zip(X_train.columns,credit_model.feature_importances_))#变量重要性指标
[('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)]
可以看到,最重要的变量是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
第一层中,可以看到决策树根结点输出了分割标准(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)
现在,我们得到了决策树模型在测试数据上的预测结果,通过将预测结果和真实结果进行对比可以评估模型性能。 可以使用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))
回归评估报告:
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
在300个贷款申请测试数据中,模型的预测正确率(Accuracy)为71.7%。 214个未违约贷款中,模型正确预测了82%。
回归评估报告中,对于违约贷款数据,实际有86个违约贷款,而召回率recall=0.43
,86个违约贷款中,模型正确预测出了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))
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
由于设置了标签2(default=2,违约)的样本权重为4,因此分类为2的样本数 = y_train中标签2的样本数 × 3 = 1458个,以此来增加识别标签为2的概率)
在300个贷款申请测试数据中,模型的预测正确率(Accuracy)为55.7%。
回归评估报告中,对于违约贷款数据,实际有86个违约贷款,而召回率recall=0.85
,86个违约贷款中,模型正确预测出了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
建立模型最主要的就是设置模型的超参数,所谓超参数就是需要建模人员设定好,软件才可以根据数据得到的模型参数。比如岭回归中系数平方前面的惩罚项,设定好之后才可以计算模型的回归系数。决策树的超参数就是预剪枝算法中提到的那几项,比如
max_depth
、max_leaf_nodes
、class_weight
GridSearchCV()
我们可以使用scikit-learn提供的参数搜索函数GridSearchCV
。GridSearchCV
使用交叉验证,对指定的参数网格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 × 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
是采用交叉验证的模型评估工具,GridSearchCV
有scoring
参数:
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:
在上例中我们采用了第二种方法,即调用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_
{'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}
可以看到,将标签2即违约数的召回率(即混淆矩阵中得到的特异性)作为参数评估调优标准时,标签2的类权重越大,违约贷款中的正确识别率越高。当类权重class_weight
为{1: 1, 2: 5},叶子节点数为5时在以上20种建模种的拟合效果好,实际有86个违约贷款,而召回率recall=0.91
,86个违约贷款中,模型正确预测出了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
决策树的一些优点是:
决策树的缺点包括:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。