决策树以及拓展
一.决策树(判定树)
1.依据树结构进行决策,目的是产生一棵泛化能力强的判定树,其中叶子结点为决策结果,其他节点对应一个属性测试,根节点包含所有样本,从根节点到叶子结点的路径表示判定的决策路径。
2.首先我们给定一个训练样本集,以及他们的属性集(特征集),然后由根结点开始,每个节点代表一个属性测试,对样本集进行分类,用节点的左右子树代表分的两类,递归分类,最后的叶子结点代表最终的决策类别,判定树的目标是通过该树尽可能使得最后的分类结果的正确性,也就是说分支结点所包含的样本类别尽可能一致。
3.优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据,可以处理多值输出变量问题,对于特征的归一化不敏感,对特征的要求不高
4.缺点:对数据敏感,容易产生过拟合和欠拟合问题,不稳定(决策边界相互正交),
二.如何选择划分最优属性
1.信息增益
(1)熵:代表混乱度,熵越低代表越纯粹,混乱度越低。信息熵:表示信源的不确定性,一个信源发送出什么符号是不确定的,衡量它可以根据其出现的概率来度量。概率大,出现机会多,不确定性小;反之不确定性就大。
(2)用信息熵来度量样本集合纯度(信息熵越小,样本越可能只有一类,纯度越高),对于样本集合,用类别代表信源的中的种类,用每类样本所占总体的比例来代表各个信源出现的概率,则有样本纯度表示:
(3)信息增益表示用某个属性对样本进行分类后的总的信息熵与样本未分类时的信息熵的差异。依据某个属性对样本集进行分类后,肯定比原有未分类时要低,这说明依属性把样本集进行分类后,所含的混乱信息要低。
(4)某个属性的信息熵:一个属性有n个取值,每个取值对应把样本集分为一类,则一个属性可以把样本集分为n个小样本集,我们可以计算每个小样本集的信息熵,由于小样本集的样本数是不同的,样本集越大,影响越大,因此对每个小样本集赋予权重(样本数/总样本数),所以某个属性的信息熵为所有小样本集信息熵的线性组合
(5)于是我们计算每个属性的信息增益,选择其中最大的作为本次的划分标准,把样本集划分为各个子样本集,选择其中信息增益最大的作为下一次的划分样本集。依次下去
2.增益率
(1)若一个属性有太多的取值,这样把样本集也划分地太细了,划分地越细,信息熵越小,信息增益越小。也就是说信息增益总是偏向于选择取值较多的属性。则定义增益率:信息熵/分裂节点数据量的信息增益。
(2)先从候选划分属性中选出信息增益高于平均水平的属性,在从中选择增益率最高的
3.基尼系数:反映了从数据集中随机抽取两个样本,其类别标记不一致的概率,因此,基尼系数越小,样本集纯度越高。(如果只有一个类别,则为0,若各个类别均匀分配,为达到最大)
4.Gini 指数更偏向于连续属性,熵更偏向于离散属性,Gini 指数的计算不需要对数运算,更加高效;
5.总结:对于一个节点,先计算划分完成后各个子节点的不纯度,在把这些子节点的不纯度加权平均作为该节点的不纯度
三.决策树的过拟合
1.决策树学习样本太好,划分的分支太多,以致于学习到训练集的特点而导致过拟合
2.剪枝:去除一些分支
(1)预剪枝:在决策树生成过程中,对每个节点在划分前进行估计,若当前节点的划分不能带来泛化性能的提升,则停止划分,把该节点作为叶子结点
【1】优点:降低过拟合,减少了训练时间和测试时间的开销
【2】缺点:由于采取的是贪心策略,所以可能导致欠拟合。还引入了一些超参数:最大深度(网点搜寻)
(2)后剪枝:生成一个决策树后,自底向上对非叶子结点进行考察,若该节点对应的子树代替叶子结点能提高泛化性能,则把该非叶子结点作为叶子结点
【1】优点:后剪枝比预剪枝保留更多的分支,比之欠拟合风险小,泛化性能更好
【2】缺点:训练开销很大
3.一般会在划分一个进行剪枝的数据集
1 # # 决策树模型 2 #from sklearn.tree import DecisionTreeClassifier 3 # 编译决策树模型,默认cart二叉型,可以修改参数从而进行模型的修改和剪枝操作 4 # tree_classifier = DecisionTreeClassifier( 5 # criterion="gini", # 判别标准(不纯度计算) 6 # splitter="best", # 划分规则 7 # max_depth=None, #最大深度 8 # min_samples_split=2, #节点在被分裂之前必须具有的最小样本数 9 # min_samples_leaf=1, #叶节点必须具有的最小样本数 10 # min_weight_fraction_leaf=0., #加权总数的一小部分实例 11 # max_features=None, #在每个节点被评估是否分裂的时候,具有的最大特征数量 12 # random_state=None, 13 # max_leaf_nodes=None, #叶节点的最大数量 14 # min_impurity_decrease=0., 15 # min_impurity_split=None, 16 # class_weight=None, 17 # presort=False 18 # ) 19 # tree_classifier.fit(X,y) #训练数据,设定训练时的相关参数
1 #在鸢尾花数据集上进行一个决策树分类器的训练 2 from sklearn.datasets import load_iris 3 from sklearn.tree import DecisionTreeClassifier 4 from sklearn.tree import export_graphviz 5 import os 6 from sklearn import tree 7 import graphviz 8 import pydotplus 9 10 os.environ["PATH"] += os.pathsep + 'C:/Program Files (x86)/Graphviz2.38/bin' 11 12 iris = load_iris() 13 X = iris.data 14 y = iris.target 15 tree_clf = DecisionTreeClassifier() # 建模 16 tree_clf.fit(X,y) # 训练 17 print(tree_clf.predict_proba([[5,1.5,4,2]])) #预测 18 print(tree_clf.predict([[5,1.5,1,2]])) 19 # 可视化 20 # 保存模型 21 with open(r"E:\python_work\machine_learning\results\iris_tree.dot", 'w') as f: 22 f = tree.export_graphviz(tree_clf, out_file=f) 23 24 # 画图,保存到pdf文件 25 # 设置图像参数 26 dot_data = tree.export_graphviz(tree_clf, out_file=None, 27 feature_names=iris.feature_names, 28 class_names=iris.target_names, 29 filled=True, rounded=True, 30 special_characters=True) 31 graph = graphviz.Source(dot_data) 32 graph.render(filename ="iris", directory ='./', format='pdf',view=True)
四.树的集成
1.把树模型进行联结组合集成新的模型
2.平均方法---随机森林(Random Forest,BF)
(1)有n个带有随机性的决策树组成,根据投票或均值得到结果。对于分类问题,模型结果等于决策树预测结果中出现次数最多的类别;对于回归问题,模型结果等于决策树预测结果的平均值
(2)随机性:【1】随机选取数据训练决策树(bagging--求同存异或pasting-太偏向),【2】在决策树划分的每一步,特征集是随机选择的,【3】在划分的每一步,从特征集中选择特征可以是随机的(一般是选择特征集中最好的)
(3)错误率:【1】森林中任意两棵树的相关性:相关性越大,错误率越大;【2】森林中每棵树的分类能力:每棵树的分类能力越强,整个森林的错误率越低。
(4)减小特征选择个数m,树的相关性和分类能力也会相应的降低;增大m,两者也会随之增大。所以关键问题是如何选择最优的m(或者是范围)
(5)随机森林可以用于非监督学习:
【1】生成数据:对于每一维,打乱值的顺序,得到一个人工合成的数据集,
【2】训练模型:把原始数据和合成数据作为一组数据来训练模型
【3】目的:分析数据变量之间的相关关系;将低维数据映射到高维空间(在树的联结上面再封装一层),方便与其他模型联结
(6)优点:犯错率比单个决策树更低,精度更高;可以较好地解决分类和回归问题;拥有树的优点,相当于树的并行;模型稳定,因为它是由所有决策树联合表示的,对数据(缺失数据等)不敏感;抗过拟合性强;(随机性)能够处理具有高维特征的输入样本,而且不需要降维;没有必要对它进行交叉验证,因为oob误分率是随机森林泛化误差的一个无偏估计,它的结果近似于需要大量计算的k折交叉验证。
(7)缺点:仍然会出现过拟合问题;更复杂,单个树无剪枝,计算代价更高,耗费时间长;
1 ###随机森林 2 from sklearn import ensemble #集成方法模块 3 rnd_clf = ensemble.RandomForestClassifier(n_estimators=500, max_leaf_nodes=16, n_jobs=-1) 4 ############## 5 # __init__(self, 6 # n_estimators=10, #森林中树的数量 7 # criterion="gini", #不纯度指标 8 # max_depth=None, 9 # min_samples_split=2, 10 # min_samples_leaf=1, 11 # min_weight_fraction_leaf=0., 12 # max_features="auto", 13 # max_leaf_nodes=None, 14 # min_impurity_decrease=0., 15 # min_impurity_split=None, 16 # bootstrap=True, #是否是袋装 17 # oob_score=False, #袋外数据 18 # n_jobs=1, 19 # random_state=None, #控制的是生成森林的模式,而非让一个森林中只有一棵树 20 # verbose=0, 21 # warm_start=False, 22 # class_weight=None): #完成样本标签平衡的参数 23 #随机森林中有三个非常重要的属性:.estimators_,.oob_score_以及.feature_importances_ 24 # 有四个常用接口:apply, fit, predict和score 25 ############# 26 rnd_clf.fit(X, y) 27 y_pred_rf = rnd_clf.predict(X)
3.提升方法---GBDT(Gradient Boosting Decision Tree) 梯度提升迭代决策树
(1)基本思想和步骤:给定数据集,我们的目标是得出一个模型使得拟合性尽可能好,但是我们不可能一开始就会构建最好的模型,怎么办?关键:模型是用数学形式表达的,说明一个模型可以由多个模型联合表示,我们的最优模型可以是一组非最优模型的组合。首先,先给定一个没那么好的模型,用模型f0训练数据,得到预测值,这个预测值说明模型f0学得了数据的一部分特点,因为模型f0是‘不好’的,不存在过拟合问题。我们把预测值和真实值的差距(损失函数)表示未学得的数据特点,则此时我们的训练集为{训练样本,差值}表示还没有学到的数据特点,对此我们再设计一个模型去学习它的一部分数据特点。就这样一步步学习下去,最终,得到许多个包含不同数据特点的模型,把它们组合作为最终的模型。总结:我们把数据特点分成一个个小块进行学习,集成方法就是这样(随机森林和GBDT)。
(2)GBDT:我们假设的是存在某个决策树F(m)可以完美训练数据集,但是我们如果用任意决策树来拟合训练集得到的任意Fi都有较大可能不为F(m),由于任意一个数学模型都可以被分为多个模型的组合,这些模型体现该模型的各个特性,我们把理想的F(m)可以分为多个决策树模型的组合,即有F(m)= F(0)+F(1)+...。已知我们不知道理想模型F(m),只能预先假设随意一个决策树模型F(m-1)来训练数据,若F(m)能完美拟合真实数据集(X,y)并且预测的也是真实数据集(X,y),假设F(m-1)训练数据集(X,y)预测出的结果为(X,ym-1),那么只要找到另一个模型预测出F(m)和F(0)的差距就好了,因此我们期望用模型h(m-1)来完美训练(X,y-(ym-1)),这样训练又会导致以上的问题,因此h(m-1)也不一定能完美预测,这样继续对h(m-1)进行划分,就像f(m)划分f(m-1)和h一样。那么问题来了:h(m-1)预测出的是什么?我们又知道模型h(m-1)是用来学习关于训练集的F(m-1)与F(m)之间差距的,但真实是只能学习一部分。
对于一个函数f(x,y)来说,其上任意一点与其他点的差距为x方向差值和y方向差值的平方和,当距离小到0时,引申出导数的意义,导数表示该点在函数上行进的最大梯度。梯度下降法就是让参数沿着梯度的方向下降,乘以一个系数-学习率作为步长,这样得到函数上的下一个参数。
我们用的是决策树来训练模型,我们把所有决策树组成的空间作为一个新的模型(函数),给定一个初值F(0),为了得到最终的F(m),我们让F(0)在这个函数上进行梯度下降似的搜寻。而无论是F(0)还是F(m)都是衡量真实值与预测数据集的差距(任何模型都是为了表现拟合能力,即真实值和预测值的差距,但是是关于数据集的损失函数),则有关于数据集的损失函数对应所有决策树组成的模型空间(函数),即L。把L对数据集的求导(L表现为模型预测值与真实值的差距,因为真实值一定,预测值表现模型拟合能力,所以L在预测值方向求导,L是关于预测值的函数)作为梯度,再乘以一个步长,在决策树空间中对最优决策树进行搜索。
因此具体训练步骤如下:先用一个树模型f0训练真实数据集(X,y)得到预测值y0,对损失函数在该预测值上求导,得到预测值接近真实值最快的方向,乘以学习率得到损失变化,并用模型h1对其进行学习(因为损失要降低,所以预测值与真实值的差距要降低,所以用减号,也就是负梯度),又效果要增加,把模型f0和h1进行组合得到新模型f2,在用模型f2进行训练真实集(X,y),同上步骤继续下去,得到最终模型。
(3)总结:GBDT用了集成的思想,模型表现了数据集上预测值和真实值的差距,所有模型的差距组成了关于数据集的损失函数,通过梯度搜索的方法使得损失函数的值最快地下降,从而求得更好的模型(增强拟合能力,预测值逼近真实值),因为模型效果是递增的,所以为梯度提升决策树。
(4)优点:可以产生特征,预测能力更强,能处理各种类型的数据,稳定,
(5)缺点:因为训练是串行的,所以能以并行训练数据,复杂度大,而且关于数据集的损失函数里没有惩罚项,容易过拟合
1 # 梯度提升树 2 import numpy as np 3 import pandas as pd 4 import tensorflow as tf 5 from IPython.display import clear_output 6 import matplotlib.pyplot as plt 7 8 # 1.加载数据集。 9 dftrain = pd.read_csv(r'E:\python_work\machine_learning\datas\titanic\train.csv') 10 dfeval = pd.read_csv(r'E:\python_work\machine_learning\datas\titanic\eval.csv') 11 y_train = dftrain.pop('survived') 12 y_eval = dfeval.pop('survived') 13 14 # 2.建模 15 # (1)特征--工程和声明 16 CATEGORICAL_COLUMNS = ['sex', 'n_siblings_spouses', 'parch', 'class', 'deck', 17 'embark_town', 'alone'] 18 feature_columns = [] 19 20 21 def one_hot_cat_column(feature_name, vocab): #先把特征中所有的类映射数字,在对其进行独热编码 22 return tf.feature_column.indicator_column( 23 tf.feature_column.categorical_column_with_vocabulary_list(feature_name,vocab)) 24 25 26 for feature_name in CATEGORICAL_COLUMNS: 27 vocabulary = dftrain[feature_name].unique() # 唯一值,每个特征的所有类 28 feature_columns.append(one_hot_cat_column(feature_name, vocabulary)) 29 30 NUMERIC_COLUMNS = ['age', 'fare'] 31 for feature_name in NUMERIC_COLUMNS: 32 feature_columns.append(tf.feature_column.numeric_column(feature_name, dtype=tf.float32)) 33 34 35 # (2)设置训练和评估的相关参数,把数据转为dataset, 36 NUM_EXAMPLES = len(y_train) 37 38 39 def make_input_fn(X, y, n_epochs=None, shuffle=True): 40 def input_fn(): 41 dataset = tf.data.Dataset.from_tensor_slices((dict(X), y)) 42 if shuffle: 43 dataset = dataset.shuffle(NUM_EXAMPLES) 44 dataset = dataset.repeat(n_epochs) 45 dataset = dataset.batch(NUM_EXAMPLES) 46 return dataset 47 return input_fn 48 49 50 # 训练与评估的输入函数。 51 train_input_fn = make_input_fn(dftrain, y_train) 52 eval_input_fn = make_input_fn(dfeval, y_eval, shuffle=False, n_epochs=1) 53 54 # (3)建模 55 # 提升树 56 est = tf.estimator.BoostedTreesClassifier(feature_columns, 57 n_batches_per_layer=1) #每层树都是一批次,即每次都是把所有数据输入训练 58 59 # 3.训练模型 60 est.train(train_input_fn, max_steps=100) #指定树的最大数量 61 62 # 4.评估。 63 result = est.evaluate(eval_input_fn) 64 clear_output() 65 print(pd.Series(result)) 66 67 # 5.预测 68 pred_dicts = list(est.predict(eval_input_fn)) 69 probs = pd.Series([pred['probabilities'][1] for pred in pred_dicts]) 70 probs.plot(kind='hist', bins=20, title='predicted probabilities') 71 plt.show()
五.补充
1.若属性的取值为连续,则可以将该区间分成两个,取中位数作为判定(>和<)
2.缺失值:对缺失值的样本赋予该属性所有属性值的概率分布,即将缺失值按照其所在属性已知值的相对概率分布来创建决策树,让同一个样本以不同的概率划分到不同的子节点中去。
3.多变量决策树:每个非叶节点不再是对单个属性的测试,而是对属性组合的测试,即每个非叶节点都是一个线性分类器。则每个非叶节点的目标不是找到最优划分属性,而是试图建立一个合适的分类器
4.决策树可以处理分类和回归问题,对于分类问题:叶子结点可以计算属于各个类的概率,对回归问题,计算叶子节点的平均数
5.决策树可以和其他模型进行联结,比如用原有特征经过决策树后得到一系列叶子结点,可以把这些叶子结点作为新特征放入其他模型中。此时决策树的作用相当于特征提取
6.训练决策树的计算复杂度是O(nmlog2(m)) ,m为样本总数,n为比较特征个数,预测要O(log2(m))
7.在最优化问题中,利用梯度的变化由大到小趋于平缓来求得最优解。梯度下降法(gradient descent)指利用该特点使得损失函数下降,而梯度提升决策树(Gradient Boosting)指利用该特定使得模型效果提升