阿喽哈~小伙伴们,今天我们来唠一唠决策树 ♣ ♣ ♣
决策树应该是很多小伙伴入门机器学习的时候最先接触到的分类算法之一,决策树分为分类树和回归树,今天我们只说分类树~
简单回顾一下分类树的算法原理:分类树的底层算法分为三种,分别是ID3, ID4.5和CART树。
- ID3算法以信息增益来决定每一次分裂的节点,它不能处理连续型变量,也不可以剪枝;
- ID4.5算法为了克服ID3算法的弊端(信息增益倾向划分拥有较多属性的特征,举个极端的例子:比如身份证号列),以信息增益率来决定划分的特征节点,它可以处理连续变量(二分法),也可以进行简单的剪枝;
- 以上两种算法只能做分类算法,不可以做回归算法,CART算法可以做分类也可以做回归,做分类问题时,以GINI系数为标准,决定划分的节点,可以处理连续变量,可以剪枝。
具体更深入的算法原理这里就不多说啦,本篇的重点是演示如何使用Sklearn实现分类树以及简单的调参,上代码~
1、导入各种包一般在敲代码之前可以先导入需要用到的包,这样代码比较好看,而且也方便管理,是一个良好的编码习惯哦
import pandas as pd import numpy as np from sklearn import tree # 导入模型 from sklearn.model_selection import train_test_split # 制作数据集和测试集 from sklearn.preprocessing import LabelEncoder import graphviz from sklearn.metrics import roc_curve, auc import matplotlib.pyplot as plt %matplotlib inline plt.rcParams['font.size'] = 242、数据准备
本篇使用的数据是汽车信贷数据,目的是预测借款人在未来是否会发生逾期行为。很明显,这是个二分类问题,不会逾期/会逾期,label分别用0/1来表示,特征包括车辆价格、借款人职业、借款人婚姻状态等6个特征
data = pd.read_csv('D:/emma/data/learn/信贷.csv') # 读取数据
# 划分训练集和测试集
x_train_orig, y_train_orig, x_test_orig, y_test_orig = train_test_split(x_bl.drop('label', axis=1), x_bl['label'], test_size=0.2)
# 查看训练集的特征列 x_train_orig.head(5)
本文共用到6个特征,具体数据情况如上图,有连续性数据和离散型数据,婚姻状态列有缺失值和字符串,SKlearn的决策树算法是无法处理字符串数据的,会报错,因此在训练模型之前,需要进行数据预处理
le = LabelEncoder() # 为婚姻状态列序列化 x_train_orig['MARITALSTATUS'] = le.fit_transform(x_train_orig['MARITALSTATUS']) x_test_orig['MARITALSTATUS'] = le.fit_transform(x_test_orig['MARITALSTATUS']) x_train_orig
可以看到,编码后婚姻状态列全部变成数值,ok,现在可以把我们的数据集扔进决策树算法去跑一跑啦
3、训练模型"""
# sklearn封装的决策树算法及所有超参数
sklearn.tree.DecisionTreeclassifier (criterion=gini, splitter='best', max_depth=None, min_samples_split=2, min_samples_leaf=1,
min_weight_fraction_leaf=0.0, max_features=None, random _state=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None, class_weight=None, presort=False)
"""
决策树的剪枝操作可以很好地防止模型过拟合,因为在用训练集训练模型时,如果不控制决策树的深度或叶子结点个数等参数,树就会一直生长下去,直到每个结点都无法再划分下去,算法才会停止,这时候模型在训练集上的预测精度会很高,但是会把训练集中的随机因素或是噪音点都当做信息学习到模型里,这样我们的模型就会过于片面,面对其他数据集的时候可能预测能力会很差。
接下来我会训练两个模型,同样的数据集,第一个不剪枝,第二个进行剪枝处理,然后在测试集上测试预测精度,让我看一看两个模型的精度如何吧~
# 载入决策树分类模型,不剪枝
clf = tree.DecisionTreeClassifier(class_weight='balanced', max_depth=None)
clf = clf.fit(x_train_orig, y_train_orig) # 决策树拟合,得到模型
score = clf.score(x_test_orig, y_test_orig) #返回预测的准确度
# 载入决策树分类模型,剪枝处理,设置树的最大深度为10
clf_new = tree.DecisionTreeClassifier(class_weight='balanced', max_depth=10)
clf_new = clf_new.fit(x_train_orig, y_train_orig) # 决策树拟合,得到模型
score_new = clf_new.score(x_test_orig, y_test_orig) #返回预测的准确度
# 对比两个模型的准确度
print(" 决策树_未剪枝:{} n".format(score), "决策树_剪枝:{}".format(score_new))
看一下结果,未剪枝的决策树在测试集上的准确度反而更高,别慌,遇到事情不要慌哈哈哈,我们来想一想:有时模型准确度会受到原数据的label分布影响,尤其像信贷数据这种,逾期样本(label为1)的个数往往只占数据集十分之一,甚至更少。所以我们往往对于分类模型来说,会使用AUC值来判断模型的预测能力,AUC可以很好地解决数据不平衡带来的问题,那我们下面再来看一看两个模型的AUC值吧
4、模型效果# 未剪枝决策树 预测测试集
y_test_proba = clf.predict_proba(x_test_orig)
false_positive_rate, recall, thresholds = roc_curve(y_test_orig, y_test_proba[:, 1])
# 未剪枝决策树AUC
roc_auc = auc(false_positive_rate, recall)
# 剪枝决策树 预测测试集
y_test_proba_new = clf_new.predict_proba(x_test_orig)
false_positive_rate_new, recall_new, thresholds_new = roc_curve(y_test_orig, y_test_proba_new[:, 1])
# 剪枝决策树AUC
roc_auc_new = auc(false_positive_rate_new, recall_new)
# 画出两个模型ROC曲线
plt.plot(false_positive_rate, recall, color='blue', label='AUC_orig=%0.3f' % roc_auc)
plt.plot(false_positive_rate_new, recall_new, color='orange', label='AUC_jianzhi=%0.3f' % roc_auc_new)
plt.legend(loc='best', fontsize=15, frameon=False)
plt.plot([0, 1], [0, 1], 'r--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.0])
plt.ylabel('Recall')
plt.xlabel('Fall-out')
plt.show()
从图上就可以看出,黄色实线代表剪枝决策树,AUC值为0.653,大于未剪枝的决策树,而且ROC面积也明显高于未剪枝的决策树,和我们预想的是一样的,小伙伴们在用决策树的时候一定要记得剪枝哦~
5、调参sklearn中的决策树算法有很多超参数,通过对超参数的可以在一定程度上提高模型的性能,我们这里尝试调整max_depth最大深度这个参数
# 定义空列表,用来存放每一个树的深度所对应的AUC值
auc_test = []
for i in range(20):
clf = tree.DecisionTreeClassifier(class_weight='balanced', max_depth = i + 1)
clf = clf.fit(x_train_orig,y_train_orig)
y_test_proba = clf.predict_proba(x_test_orig)
false_positive_rate, recall, thresholds = roc_curve(y_test_orig, y_test_proba[:, 1])
roc_auc = auc(false_positive_rate, recall)
auc_test.append(roc_auc)
plt.plot(range(1,21),auc_test,color="red",label="max_depth")
plt.legend()
plt.show()
随着树的增加,AUC值是逐渐下降的,根据这个图就可以选择最合适的max_depth
6、特征重要性clf = tree.DecisionTreeClassifier(class_weight='balanced', max_depth=4) clf = clf.fit(x_train_orig, y_train_orig) # 决策树拟合,得到模型 #特征重要性 feature_name = ['ALLMONEY', 'CAPARTY01', 'DJK5NNSYYFS', 'FINAL_SCORE', 'LOANCARDLEDGER', 'MARRIAGESTATUS'] clf.feature_importances_ [*zip(feature_name,clf.feature_importances_)]
# 横向柱状图
plt.barh(np.arange(len(feature_name)), clf.feature_importances_, align='center')
plt.yticks(np.arange(len(feature_name)), feature_name, fontsize=15)
plt.xticks(fontsize=10)
plt.xlabel('Importances', fontsize=15)
plt.xlim(0,1)
plt.show()
可以看到,并不是所有的特征都会被用到,婚姻状态的特征重要性为0,也就是说在整颗树中没有这个结点,这也是由于我们剪枝的原因,一些不重要的特征可能就不会出现在决策树中
7、画出决策树并保存dot_data = tree.export_graphviz(clf, feature_names=feature_name, class_names=['good', 'bad'], filled=True, rounded=True) graph = graphviz.Source(dot_data) graph
树有点大哈哈哈放不下了,这时候我们可以以图片或者pdf的形式把树保存到电脑里,便于观察~
mytree = graphviz.Source(dot_data, format='pdf')
mytree.render('D:/weiyu/data/代码产出数据/mytree.pdf')
ok!感恩的心~



