决策树
决策树是什么
一个根节点,若干个内部节点和叶节点
非参数学习算法
天然的分类器
决策树的目标
解决分类和回归问题
决策树的优点
- 决策树易于理解和实现,人们在在学习过程中不需要使用者了解很多的背景知识,这同时是它的能够直接体现数据的特点,只要通过解释后都有能力去理解决策树所表达的意义。
- 对于决策树,数据的准备往往是简单或者是不必要的,而且能够同时处理数据型和常规型属性,在相对短的时间内能够对大型数据源做出可行且效果良好的结果。
- 准确性高: 挖掘出来的分类规则准确性高, 便于理解, 决策树可以清晰的显示哪些字段比较重要, 即可以生成可以理解的规则.
- 适合处理有缺失属性的样本,对缺失值不敏感
决策树的缺点
- 容易发生过拟合(剪枝 随机森林)
- 分类过程中每一步都依据单一特征,忽视了特征之间的关联性,在处理特征关联性强的数据时表现不好
- 对于样本不均衡的数据集表现不好,欠拟合。在特征选择时ID3算法偏好于选取可取值数目较多的属性,C4.5算法偏向选取可取值数目较少的属性(实际中是在算法中采用启发式原则,先从候选属性中选出信息增益高于平均水平的属性,再从中选择增益率最高的属性)
原理
根据信息熵(entropy) or 基尼系数(gini)的大小决定下一个节点怎么分枝,最后生成决策树,而随机森林就是多个决策树的组合
信息熵entropy
超链接:Yjango:什么是信息熵?
熵在信息论中代表随机变量的不确定性的度量
熵越小,数据不确定性越低
熵越大,数据不确定性越高
信息熵
H=
基尼系数
- 基尼值
G = 1
以基尼指数为指标时,应该选择Gini指数最小的
CART决策树使用“基尼指数”来选择划分属性
- 基尼指数
G_index =
基尼指数到0时,即到叶节点,不能再往下划分
AUC ROC tpr fpr
- tpr:Recall,召回率,即当前被分到正样本类别中,真实的正样本占所有正样本的比例,即召回率(召回了多少正样本比例)
- fpr:Precision,正例率,即当前划分到正样本类别中,被正确分类的比例(即正式正样本所占比例),就是我们一般理解意义上所关心的正样本的分类准确率;
- ROC:tpr和fpr决定的曲线
- AUC:ROC曲线下包围的面积,最大值为1,越大拟合性能越好
过拟合的原因及如何防止
对于过拟合现象产生的原因,有以下几个方面,
- 第一:在决策树构建的过程中,对决策树的生长没有进行合理的限制(剪枝);
- 第二:在建模过程中使用了较多的输出变量,变量较多也容易产生过拟合;
- 第三:样本中有一些噪声数据,噪声数据对决策树的构建的干扰很多,没有对噪声数据进行有效的剔除。
对于过拟合现象的预防措施,有以下一些方法,
- 第一:选择合理的参数进行剪枝,可以分为预剪枝后剪枝,我们一般用后剪枝的方法来做;
- 第二:K-folds交叉验证,将训练集分为K份,然后进行K次的交叉验证,每次使用K-1份作为训练样本数据集,另外的一份作为测试集合(作者说反了,应该是份作为测试集,其余k-1份作为训练集);
- 第三:减少特征,计算每一个特征和相应变量的相关性,常见的为皮尔逊相关系数,将相关性较小的变量剔除,当然还有一些其他的方法来进行特征筛选,比如基于决策树的特征筛选,通过正则化的方式来进行特征选取等。
预剪枝:生成决策树的过程中剪枝
基于“贪心”本质,能剪则剪。
如果某个分支的存在并没有提高准确率,or降低了准确率,则剪掉
降低了过拟合风险;
显著减少了决策树训练时间;
但带来了欠拟合的风险
后剪枝:生成决策树之后剪枝
能不剪,则不剪,剪前后若准确率相等,则保留
同样的训练模型,后剪枝的决策树保留了更多的分支
后剪枝的欠拟合风险很小
泛化性能往往优于预剪枝(分的更细,在面对陌生数据时判断更准确)
训练时间长的多(生成决策树之后需要自底向上逐一考察,计算开销大)
“在有噪声的情况下,剪枝操作甚至能将泛化性能提高25%”
连续值:可取连续值的属性
例如:脐部{凹陷,平坦,稍凹}这种是离散值;而密度,xx含量等很多属性值都是连续的
因此在划分分支的时候,需要有一个间断点
间断点的划分方法:二分法,例如有17个排序后的点集,两两之间算中位数,一共算16次,生成16个t值,组成一个t的集合T,用T中的划分点代入Gain算法,计算Gain(D,该属性,t为划分点),取Gain最大值,对应的t即为最终确定的划分点
划分完t之后,如果子分支还需用到更细的判断, 可以使用t的子集:例如:一个节点判断“密度<=0.381”那么后续的子节点可以使用任何"密度<0.381"范围的判断依据
缺失值:某些数据的其中某个数据缺失,若全部弃用则会很浪费
属性a缺失值处理方法:
西瓜书上p88:跳过该属性a的判断,直接判断下一节点的所有可能性,但需要加上训练集中的比例权重
(离散值):众数填充、相关性最高的列(属性b总是与属性a的取值几乎一一对应)填充
(连续值):中位数、相关性最高的列(同上)做线性回归估计
多变量决策树:用线性关系替代多个变量
有些属性之间有一定的线性关系,例如:密度和含糖量之间存在着线性关系,那么就把密度和含糖量分别乘上各自的权重系数,用他俩组成的一个式子<=t 或 >=t 作为分界点来判断
多变量决策树算法:贪心寻找每个属性的最优权值,线性分类器的最小二乘法
随机森林
擅长于解决数据不平衡的分类
实现
步骤
1,导包
import pandas as pd
import numpy as np
2,读取、初步查看分析数据
df = pd.read_csv('broadband.csv')
df.rename(str.lower, axis='columns', inplace=True)#列名全换小写,方便看
print(df.head())#空参则显示前5行数据
# broadband 即可:0-离开(否),1-留存(是)
df.info() #输出行列信息(总体数据特征)
print(df.sample()) # 随机查看一个样本数据
# 查看因变量 broadband 分布情况,看是否存在不平衡
from collections import Counter
print('Broadband: ', Counter(df['broadband']))
#输出结果是Broadband: Counter({0: 131, 1: 49}),数据并不平衡
3,划分训练集 测试集
由于步骤2中info()发现数据集的第一列是用户ID,最后一列是判断标准Broadband,故这两列都不用做数据分析
y = df['broadband'] # y就是标签(结果)
X = df.iloc[:, 1:-1] # 客户 id 没有用,故丢弃 cust_id;标签y也要去掉,故1:-1
#左边冒号左右端为空,表示所有行数据全部都取到X中
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=123)
#40%划分为test集,
# 这里的random_state就是为了保证程序每次运行都分割一样的训练集和测试集。
# 否则,同样的算法模型在不同的训练集和测试集上的效果不一样。
# 因此具体取值多少无所谓,但对结果有影响
4,决策树建模
网格搜索:
因为决策树算法是非参数学习算法,需要自行调参,利用网格搜索则可以自动调参,择优选取
把自己认为好的参数都扔进去,让网格搜索自己跑
import sklearn.tree as tree
# 1. 直接使用交叉网格搜索来优化决策树模型,边训练边优化
from sklearn.model_selection import GridSearchCV
# 2. 网格搜索参数,选择最优参数,该param_grid作为评价指标用于下面的训练模型
param_grid = {'criterion': ['entropy', 'gini'], # 树的深度评估指标,信息熵or基尼
'max_depth': [2, 3, 4, 5, 6, 7, 8], # 可选树的深度
'min_samples_split': [4, 8, 12, 16, 20, 24, 28]} # 可选最小拆分的叶子样本数
模型训练
# 3. 定义一棵树对象
clf = tree.DecisionTreeClassifier()
# 4. 传入模型,网格搜索的参数,评估指标,cv交叉验证的次数
clfcv = GridSearchCV(estimator=clf, param_grid=param_grid, scoring='roc_auc',cv=4)
# roc曲线和auc面积值作为评价标准(一般都用auc直接比较面积)
# cv=?表示交叉验证的次数
# 5. 训练模型
clfcv.fit(X_train, y_train)
# 6. 使用模型来对测试集进行预测
test_result = clfcv.predict(X_test)
模型评估
# 7. 模型评估
import sklearn.metrics as metrics
print("决策树 AUC:")
fpr_test, tpr_test, th_test = metrics.roc_curve(y_test, test_result)
#主要是为了得到fpr,tpr,代入AUC计算公式
print('AUC = %.4f' %metrics.auc(fpr_test, tpr_test))
#输出AUC的值
print("决策树准确度:")
print(metrics.classification_report(y_test,test_result))
#输出准确度表格,参考价值不是很大,一般还是以AUC为准
# 9. 求网格搜索后的最优参数
print(clfcv.best_params_)
#输出最优参数组合,但这个最优并非完全最优,可能还需要在开始的地方再重新调参,可能
#算出的最后参数结果还不同
#假设“最小样本设置{4,5,6,7,8}”,而最优结果是4 或 8,处于边缘,
#就需要往边缘调参
#因为该样本结果是:
#{'criterion': 'entropy', 'max_depth': 3, 'min_samples_split': 12}
#都不是设置的边缘只,所以可以认为这些参数还不错,暂时不调参
5,决策树生成
选择最优参数重新训练
# 将最优参数代入到模型中,重新训练、预测
#{'criterion': 'entropy', 'max_depth': 3, 'min_samples_split': 12}
clf2 = tree.DecisionTreeClassifier(criterion='entropy', max_depth=3, min_samples_split=12)
clf2.fit(X_train, y_train)
test_res2 = clf2.predict(X_test)
绘制决策树,在同目录下生成pdf
# 绘制图形 pip3 install graphviz
import graphviz
dot_data = tree.export_graphviz(clf2, out_file=None)
graph = graphviz.Source(dot_data)
graph.render('决策树')#生成文件名为决策树的pdf图片
随机森林
由于上述决策树生成的AUC值还不到0.7,认为拟合效果不够好,因此尝试使用随机森林算法
1,网格搜索
param_grid = {
'criterion':['entropy','gini'],# 衡量标准
'max_depth':[5, 6, 7, 8], # 每棵决策树的深度
'n_estimators':[11,13,15], # 决策树个数 - 随机森林特有参数
'max_features':[0.3,0.4,0.5], # 每棵决策树使用的变量占比 - 随机森林特有参数
'min_samples_split':[4,8,12,16] # 叶子的最小拆分样本量
}
2,集成学习:随机森林训练
import sklearn.ensemble as ensemble # ensemble learning: 集成学习
rfc = ensemble.RandomForestClassifier()
rfc_cv = GridSearchCV(estimator=rfc, param_grid=param_grid,
scoring='roc_auc', cv=4)
rfc_cv.fit(X_train, y_train)
3,使用随机森林对结果预测,并求AUC(一般都高于决策树)
predict_test = rfc_cv.predict(X_test)
#训练,预测结束之后,方可查看最佳参数配置
print(rfc_cv.best_params_)
#输出AUC和精度表格
print('随机森林 AUC...')
fpr_test, tpr_test, th_test = metrics.roc_curve(predict_test, y_test)
# 构造 roc 曲线
print('AUC = %.4f' %metrics.auc(fpr_test, tpr_test))
print('随机森林精确度...')
print(metrics.classification_report(predict_test, y_test))
总结
调参的思想类似于高中生物实验探究题中的 “寻找最佳浓度”
衡量决策树 or 随机森林 模型好坏的标准一般用AUC的值来判断
非参数学习一般都用网格搜索
例如在进行网格搜索时,有很多参数,哪怕最优结果表明只需要调整一个参数,调整之后其他最优结果可能也会改变(牵一发动全身)
当然,如果电脑性能足够好,可以直接放很多参数去跑,省去了大量的调参花费的精力
网格搜索的参数范围一开始要间隔比较大才好
其他参数相同时,同一个 random_state=?保证了算出来的结果相同
附:
树模型的参数
#
# - 1.criterion gini or entropy
#
# - 2.splitter best or random 前者是在所有特征中找最好的切分点 后者是在部分特征中(数据量大的时候)
#
# - 3.max_features None(所有),log2,sqrt,N 特征小于50的时候一般使用所有的
#
# - 4.max_depth 数据少或者特征少的时候可以不管这个值,如果模型样本量多,特征也多的情况下,可以尝试限制下
#
# - 5.min_samples_split 如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
#
# - 6.min_samples_leaf 这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝,如果样本量不大,不需要管这个值,大些如10W可是尝试下5
#
# - 7.min_weight_fraction_leaf 这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。
#
# - 8.max_leaf_nodes 通过限制最大叶子节点数,可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制具体的值可以通过交叉验证得到。
#
# - 9.class_weight 指定样本各类别的的权重,主要是为了防止训练集某些类别的样本过多导致训练的决策树过于偏向这些类别。这里可以自己指定各个样本的权重如果使用“balanced”,则算法会自己计算权重,样本量少的类别所对应的样本权重会高。
#
# - 10.min_impurity_split 这个值限制了决策树的增长,如果某节点的不纯度(基尼系数,信息增益,均方差,绝对差)小于这个阈值则该节点不再生成子节点。即为叶子节点 。
# - n_estimators:要建立树的个数