GBDT和随机森林的区别

网友投稿 364 2022-09-22

GBDT和随机森林的区别

学习总结

随机森林中的随机主要来自三个方面:

其一为bootstrap抽样导致的训练集随机性,其二为每个节点随机选取特征子集进行不纯度计算的随机性,其三为当使用随机分割点选取时产生的随机性(此时的随机森林又被称为Extremely Randomized Trees)。

一些机器学习的特征工程API(特征提取、特征预处理、特征降维等)要熟悉。如​​DictVectorizer​​​字典特征提取,如果在参数中设定​​sparse = False​​​则​​DictVectorizer​​​默认返回的是一个one hot编码矩阵,如果没设定​​sparse = False​​则默认是返回一个稀疏矩阵,对应每行的值是矩阵中非零元素的坐标位置。

文章目录

​​学习总结​​​​一、模型的误差Error分析​​

​​1.1 估测变量x的偏差和方差​​​​1.2 不同模型情况​​

​​(1)不同模型的方差​​​​(2)不同模型的偏差​​​​(3)方差VS偏差​​

​​二、bagging和boosting的区别​​​​三、决策树基础​​

​​3.1 不同的特征选择方案​​​​3.2 决策树的划分依据之一——信息增益​​​​3.3 决策树的优缺点​​

​​四、GBDT和随机森林的区别​​​​五、随机森林代码部分​​

​​5.0 随机森林原理​​

​​1.1 算法介绍​​​​1.2 Totally Random Trees Embedding​​

​​5.1 随机森林的应用栗子​​​​5.2 随机森林的主要参数​​

​​(1)RF框架的参数​​​​(2)RF决策树的参数​​

​​5.3 手写随机森林​​

​​六、GBDT代码部分​​

​​6.1 GBDT原理及优缺点​​​​6.2 GBDT的应用栗子​​​​6.3 手写GBDT的回归树​​

​​Reference​​

一、模型的误差Error分析

1.1 估测变量x的偏差和方差

【举栗子】 一次打靶实验,目标是为了打到10环,但是实际上只打到了7环,那么这里面的Error就是3。具体分析打到7环的原因,可能有两方面:

一是瞄准出了问题,比如实际上射击瞄准的是9环而不是10环,这里的差值1就是bias,表示模型期望和真实目标的差距;二是枪本身的稳定性有问题,虽然瞄准的是9环,但是只打到了7环,这里的差值2就是variance,表示模型的稳定性,即训练得到的模型之间的变动水平。Error = Bias + Variance,Error反映的是整个模型的准确度。

具体的公式推导如下:

1.2 不同模型情况

(1)不同模型的方差

一次模型的方差就比较小的,也就是是比较集中,离散程度较小。而5次模型的方差就比较大,同理散布比较广,离散程度较大。

所以用比较简单的模型,方差是比较小的(就像射击的时候每次的时候,每次射击的设置都集中在一个比较小的区域内)。如果用了复杂的模型,方差就很大,散布比较开。

这也是因为简单的模型受到不同训练集的影响是比较小的。

(2)不同模型的偏差

(3)方差VS偏差

将系列02中的误差拆分为偏差和方差。

添加其他特征项:如组合、泛化、相关性、上下文特征、平台特征等;添加多项式特征:如线性模型添加二次型模型使得泛化能力更强;减少正则化参数:正则化是用来防止过拟合的,但现在模型欠拟合,则需要减少正则化参数。注意:如果此时强行再收集更多的data去训练,这是没有什么帮助的,因为设计的函数集本身就不好,再找更多的训练集也不会更好。

但是很多时候不一定能做到收集更多的data。也很多种收集(调整)数据的方法,针对对问题的理解对数据集做调整。比如识别手写数字的时候,偏转角度的数据集不够,那就将正常的数据集左转15度,右转15度,类似这样的处理。

二、bagging和boosting的区别

基分类器的错误 = 偏差 + 方差。

而在机器学习中其实有2个任务: 任务一: 如何优化训练数据 —> 主要用于解决欠拟合问题 任务二: 如何提升泛化性能 —> 主要用于解决过拟合问题

Boosting 通过逐步聚集基分类器分错的样本,减少集成分类器的偏差;训练好一个弱分类器后,计算其错误或残差,作为下一个分类器的输入——该过程在不断减小损失函数,使模型不断逼近“靶心”。Bagging 通过分而治之的策略,通过对训练样本多次采用,综合决策多个训练出来的模型,来减少集成分类器的方差。 有点不严谨地说,对n个独立不相关的模型的预测结果取平均, 方差是原来单个模型的1/n。

三、决策树基础

3.1 不同的特征选择方案

ID3树

C4.5树

CART算法

评价标准

信息增益

信息增益比

基尼指数

样本类型

离散型变量

连续型变量

连续型变量

任务

分类

分类

分类and回归(回归树使用最小平方误差)

3.2 决策树的划分依据之一——信息增益

通俗地讲,X(明天下雨)是一个随机变量,X的熵可以算出来, Y(明天阴天)也是随机变量,在阴天情况下下雨的信息熵我们如果也知道的话(此处需要知道其联合概率分布或是通过数据估计)即是条件熵。X的熵减去Y条件下X的熵,就是信息增益。具体解释:原本明天下雨的信息熵是2,条件熵是0.01(因为如果知道明天是阴天,那么下雨的概率很大,信息量少),这样相减后为1.99。在获得阴天这个信息后,下雨信息不确定性减少了1.99,不确定减少了很多,所以信息增益大。也就是说,阴天这个信息对明天下午这一推断来说非常重要。所以在特征选择的时候常常用信息增益,如果IG(信息增益大)的话那么这个特征对于分类来说很关键,决策树就是这样来找特征的。

3.3 决策树的优缺点

优点

易于理解和解释,树木可视化需要很少的数据准备,其他算法模型一般需要进行数据归一化

缺点

可以创建决策树,但是不能很好推广数据过于复杂的树,即会过拟合,复杂的树在测试集中表现不一定好。

改进:

减枝CART算法(sklearn决策树中的api已经实现,参考下面的随机森林调优栗子)随机森林

四、GBDT和随机森林的区别

(一)GBDT=决策树+AdaBoost集成学习。GBDT通过基学习器继续学习上一个学习器拟合的残差(用负梯度去拟合残差),在预测的过程中,我们也需要把所有树的预测值加起来,得到最终的预测结果。

(二)随机森林是以决策树(常用CART树)为基学习器的bagging算法。

(1)随机森林当处理回归问题时,输出值为各学习器的均值;

(2)随机森林当处理分类问题时有两种策略:

五、随机森林代码部分

5.0 随机森林原理

1.1 算法介绍

随机森林是以决策树(常用CART树)为基学习器的bagging算法。简单说就是每次采样不同数据集,分别训练对应的分类器,最后平均投票得到结果。包外估计(Out-of-Bag Estimate)是对集成分类器泛化误差的无偏估计。

在随机森林算法中数据集属性的重要性、 分类器集强度和分类器间相关性计算都依赖于袋外数据。

(1)随机森林当处理回归问题时,输出值为各学习器的均值; (2)随机森林当处理分类问题时有两种策略:

随机森林中的随机主要来自三个方面:

其一为bootstrap抽样导致的训练集随机性,其二为每个节点随机选取特征子集进行不纯度计算的随机性,其三为当使用随机分割点选取时产生的随机性(此时的随机森林又被称为Extremely Randomized Trees)。

随机森林中特征重要性的计算方式为:利用相对信息增益来度量单棵树上的各特征特征重要性(与决策树计算方式一致),再通过对所有树产出的重要性得分进行简单平均来作为最终的特征重要性。

对每一个基学习器训练完毕后,我们都对oob样本进行预测,每个样本对应的​​oob_prediction_​​​值为所有没有采样到该样本进行训练的基学习器预测结果均值,这一部分的逻辑参见​​此处​​的源码实现。

在得到所有样本的​​oob_prediction_​​​后: (1)对于回归问题,使用​​​r2_score​​​来计算对应的​​oob_score_​​​; (2)而对于分类问题,直接使用​​​accuracy_score​​​来计算​​oob_score_​​。

1.2 Totally Random Trees Embedding

介绍一种Totally Random Trees Embedding方法,它能够基于每个样本在各个决策树上的叶节点位置,得到一种基于森林的样本特征嵌入。

5.1 随机森林的应用栗子

随机森林的优点

准确率高,能有效运行在大数据集上;能够处理具有高维特征的输入样本(每颗树只用部分特征),而且不需要降维;能够评估各个特征在分类问题上的重要性。使用的是最常见的泰坦尼克号数据集:

首先基本的数据处理:

确定特征值​​x​​​(简单起见,只选择性别年龄等3个特征)和目标值​​y​​;用​​fillna​​缺失值处理,缺失的年龄用平均年龄值填充;​​train_test_split​​随机划分训练集和测试集

特征工程,常言道~数据和特征决定机器学习的上限,而模型和算法逼近上限。常用的特征工程如特征提取、特征预处理、特征降维等。

特征提取是将任意数据(如文本或者图像)转换为机器学习的数字特征(特征值化是为了让计算机更好的去理解数据),包括字典特征抽取、文本特征提取、图像特征提取(深度学习)等。这里我们可以用​​DictVectorizer​​​进行字典特征提取,如果在参数中设定​​sparse = False​​​则​​DictVectorizer​​​默认返回的是一个one hot编码矩阵,如果没设定​​sparse = False​​则默认是返回一个稀疏矩阵,对应每行的值是矩阵中非零元素的坐标位置(如下面栗子);

#1)实例化一个转换器类 2)调用fir_transform()方法from sklearn.feature_extraction import DictVectorizer#导包#下面的data是数据data=[{'city':'北京','tempreature':100}, {'city':'上海','tempreature':60}, {'city':'深圳','tempreature':30},]#1实例化一个转换器类# transfer=DictVectorizer()"""看一下转换的结果data_new: (0, 1) 1.0 (0, 3) 100.0 (1, 0) 1.0 (1, 3) 60.0 (2, 2) 1.0 (2, 3) 30.0看一下特征的名字: ['city=上海', 'city=北京', 'city=深圳', 'tempreature']"""transfer=DictVectorizer(sparse = False)"""看一下转换的结果data_new: [[ 0. 1. 0. 100.] [ 1. 0. 0. 60.] [ 0. 0. 1. 30.]]看一下特征的名字: ['city=上海', 'city=北京', 'city=深圳', 'tempreature']"""#2调用一fit_transform()方法data_new=transfer.fit_transform(data)print("看一下转换的结果data_new:\n",data_new)print("看一下特征的名字:\n",transfer.get_feature_names())

通过​​GridSearchCV​​​进行网格化搜索确定参数:​​max_depth​​​最大树高、​​n_estimators​​树的个数。

完整代码如下:

# -*- coding: utf-8 -*-import pandas as pdimport numpy as npfrom sklearn.model_selection import train_test_splitfrom sklearn.feature_extraction import DictVectorizerfrom sklearn.tree import DecisionTreeClassifier, export_graphviz# 1.获取数据titan = pd.read_csv("titanic.csv")# 2.数据基本处理# 2.1 确定特征值,目标值x = titan[["pclass", "age", "sex"]]y = titan["survived"]# 2.2 缺失值处理x['age'].fillna(value=titan["age"].mean(), inplace=True)# 2.3 数据集划分x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=22, test_size=0.2)# 3.特征工程(字典特征抽取)x_train = x_train.to_dict(orient="records")x_test = x_test.to_dict(orient="records")transfer = DictVectorizer()x_train = transfer.fit_transform(x_train)x_test = transfer.fit_transform(x_test)# 4.机器学习(决策树)estimator = DecisionTreeClassifier(max_depth=15)estimator.fit(x_train, y_train)# 5.模型评估y_pre = estimator.predict(x_test)ret = estimator.score(x_test, y_test)export_graphviz(estimator, out_file="./tree.dot", feature_names=['age', 'pclass=1st', 'pclass=2nd', 'pclass=3rd', '女性', '男性']) # 实例化一个随机森林from sklearn.ensemble import RandomForestClassifierrf = RandomForestClassifier() # 通过超参数调优from sklearn.model_selection import GridSearchCVparam = {"n_estimators":[100, 120, 300], "max_depth":[3, 7, 11]}# 进行网格搜索gc = GridSearchCV(rf, param_grid = param, cv = 3)gc.fit(x_train, y_train)print("随机森林预测结果是:\n", gc.score(x_test, y_test))

得到的随机森林预测结果为:0.779467680608365。

5.2 随机森林的主要参数

(1)RF框架的参数

RF框架的参数:​​RandomForestClassifier​​​和​​RandomForestRegressor​​​参数绝大部分相同。 (1)​​​n_estimators​​​(重点): 也就是最大的弱学习器的个数。一般来说​​n_estimators​​​太小,容易欠拟合,​​n_estimators​​​太大,计算量会太大,并且​​n_estimators​​​到一定的数量后,再增大​​n_estimators​​获得的模型提升会很小,所以一般选择一个适中的数值。默认是100。

(2)​​oob_score​​ :即是否采用袋外样本来评估模型的好坏。默认识False。个人推荐设置为True,因为袋外分数反应了一个模型拟合后的泛化能力。

(3)​​criterion​​: 即CART树做划分时对特征的评价标准。分类模型和回归模型的损失函数是不一样的

分类RF对应的CART分类树默认是基尼系数gini;另一个可选择的标准是信息增益。回归RF对应的CART回归树默认是均方差mse,另一个可以选择的标准是绝对值差mae。一般来说选择默认的标准就已经很好的。

(2)RF决策树的参数

(1)RF划分时考虑的最大特征数​​max_features​​: 可以使用很多种类型的值,

(2)决策树最大深度​​max_depth​​:默认可以不输入,如果不输入的话,决策树在建立子树的时候不会限制子树的深度。一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间。

(3)内部节点再划分所需最小样本数​​min_samples_split​​:这个值限制了子树继续划分的条件,如果某节点的样本数少于​​min_samples_split​​,则不会继续再尝试选择最优特征来进行划分。 默认是2.如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。

(4)叶子节点最少样本数​​min_samples_leaf​​:这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。 默认是1,可以输入最少的样本数的整数,或者最少样本数占样本总数的百分比。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。

(5)叶子节点最小的样本权重和​​min_weight_fraction_leaf​​:这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝。 默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。

(6)最大叶子节点数​​max_leaf_nodes​​:通过限制最大叶子节点数,可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制,具体的值可以通过交叉验证得到。

(7)节点划分最小不纯度​​min_impurity_split​​:这个值限制了决策树的增长,如果某节点的不纯度(基于基尼系数,均方差)小于这个阈值,则该节点不再生成子节点。即为叶子节点 。一般不推荐改动默认值1e-7。

上面决策树参数中最重要的包括最大特征数​​max_features​​​, 最大深度​​max_depth​​​, 内部节点再划分所需最小样本数​​min_samples_split​​​和叶子节点最少样本数​​min_samples_leaf​​。

5.3 手写随机森林

手写随机森林算法,然后和​​sklearn​​​中的​​RandomForestClassifier​​进行结果的比较,即预测一致性的比例。

# -*- coding: utf-8 -*-import numpy as npfrom sklearn.tree import DecisionTreeClassifier as Treefrom sklearn.datasets import make_classificationfrom sklearn.ensemble import RandomForestClassifier as RF# from RandomForest_Classifier import RandomForestimport numpy as np# 随机森林class RandomForest: def __init__(self, n_estimators, max_depth): self.n_estimators = n_estimators self.max_depth = max_depth self.trees = [] def fit(self, X, y): for tree_id in range(self.n_estimators): indexes = np.random.randint(0, X.shape[0], X.shape[0]) random_X = X[indexes] random_y = y[indexes] tree = Tree(max_depth=3) tree.fit(random_X, random_y) self.trees.append(tree) def predict(self, X): results = [] for x in X: result = [] for tree in self.trees: result.append(tree.predict(x.reshape(1, -1))[0]) # 返回该样本的预测结果,采取方案:多数投票 results.append(np.argmax(np.bincount(result))) return np.array(results)if __name__ == "__main__": X, y = make_classification(n_samples=200, n_features=8, n_informative=4, random_state=0) RF1 = RandomForest(n_estimators=100, max_depth=3) RF2 = RF(n_estimators=100, max_depth=3) RF1.fit(X, y) res1 = RF1.predict(X) RF2.fit(X, y) res2 = RF2.predict(X) print('预测一致的比例', (np.abs(res1 - res2) < 1e-5).mean())# 打印出:预测一致的比例 0.975

六、GBDT代码部分

6.1 GBDT原理及优缺点

简单说就是,先训练第一个分类器,然后调整数据分布(将分类错误的数据“放大”,将正确的数据“缩小”),在此基础上训练第二个分类器,以此类推。显然Boosting的学习是串行的,学习有先后顺序,并且每轮会根据前一轮学习结果调整数据的重要性。总之,就是一个优化训练数据,解决欠拟合的过程。

优点

预测阶段的计算速度快,树与树之间可并行化计算。在分布稠密的数据集上,泛化能力和表达能力都很好。采用决策树作为弱分类器使得GBDT模型具有较好的解释性和鲁棒性, 能够自动发现特征间的高阶关系,并且也不需要对数据进行特殊的预处理如归一化等。

缺点

GBDT在高维稀疏的数据集上,表现不如支持向量机或者神经网络。GBDT在处理文本分类特征问题上,相对其他模型的优势不如它在处理 数值特征时明显。训练过程需要串行训练,只能在决策树内部采用一些局部并行的手段提 高训练速度。

6.2 GBDT的应用栗子

参考:​​【kaggle】基于xgboost的boston房价预测​​

6.3 手写GBDT的回归树

from sklearn.tree import DecisionTreeRegressor as DTfrom sklearn.datasets import make_classificationfrom sklearn.metrics import roc_auc_scorefrom sklearn.model_selection import train_test_splitimport numpy as npclass GBDTClassifier: def __init__(self, max_depth=4, n_estimator=1000, lr=0.2): self.max_depth = max_depth self.n_estimator = n_estimator self.lr = lr self.booster = [] self.best_round = None def record_score(self, y_train, y_val, train_predict, val_predict, i): train_predict = np.exp(train_predict) / (1 + np.exp(train_predict)) val_predict = np.exp(val_predict) / (1 + np.exp(val_predict)) auc_val = roc_auc_score(y_val, val_predict) if (i+1)%10==0: auc_train = roc_auc_score(y_train, train_predict) print("第%d轮\t训练集: %.4f\t" "验证集: %.4f"%(i+1, auc_train, auc_val)) return auc_val def fit(self, X, y): X_train, X_val, y_train, y_val = train_test_split( X, y, test_size=0.25, random_state=0) train_predict, val_predict = 0, 0 # 按照二分类比例的初始化公式计算 fit_val = np.log(y_train.mean() / (1 - y_train.mean())) next_fit_val = np.full(X_train.shape[0], fit_val) last_val_score = - np.infty for i in range(self.n_estimator): cur_booster = DT(max_depth=self.max_depth) cur_booster.fit(X_train, next_fit_val) train_predict += cur_booster.predict(X_train) * self.lr val_predict += cur_booster.predict(X_val) * self.lr next_fit_val = y_train - np.exp( train_predict) / (1 + np.exp(train_predict)) self.booster.append(cur_booster) cur_val_score = self.record_score( y_train, y_val, train_predict, val_predict, i) if cur_val_score < last_val_score: self.best_round = i print("\n训练结束!最佳轮数为%d"%(i+1)) break last_val_score = cur_val_score def predict(self, X): cur_predict = 0 for i in range(self.best_round): cur_predict += self.lr * self.booster[i].predict(X) return np.exp(cur_predict) / (1 + np.exp(cur_predict))if __name__ == "__main__": X, y = make_classification( n_samples=10000, n_features=50, n_informative=20, random_state=1) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.25, random_state=0) model = GBDTClassifier() model.fit(X_train, y_train) prediction = model.predict(X_test) auc = roc_auc_score(y_test, prediction) print("\n测试集的AUC为 %.4f"%(auc))

结果为:

第10轮 训练集: 0.9269 验证集: 0.8911第20轮 训练集: 0.9474 验证集: 0.9129第30轮 训练集: 0.9578 验证集: 0.9238第40轮 训练集: 0.9645 验证集: 0.9322第50轮 训练集: 0.9699 验证集: 0.9383第60轮 训练集: 0.9744 验证集: 0.9443第70轮 训练集: 0.9776 验证集: 0.9480第80轮 训练集: 0.9801 验证集: 0.9517第90轮 训练集: 0.9820 验证集: 0.9539第100轮 训练集: 0.9840 验证集: 0.9568第110轮 训练集: 0.9854 验证集: 0.9584第120轮 训练集: 0.9868 验证集: 0.9598第130轮 训练集: 0.9880 验证集: 0.9612训练结束!最佳轮数为139测试集的AUC为 0.9649

Reference

[1] ​​集成学习(Bagging与Boosting)及案列讲解​​​ [2] ​​传统机器学习算法复习:逻辑回归、因子分解机和梯度提升树​​ [3] ​​字典特征提取DictVectorizer(特征工程之特征提取)​​

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:【Spark】(task4)SparkML基础(数据编码)
下一篇:数英DIGITALING:奥运营销如何破圈?伊利给出了参考答案!
相关文章

 发表评论

暂时没有评论,来抢沙发吧~