Bagging策略
1.总样本数量是n个,从样本中重采样(有放回的)选出n个样本 ,会有约33.2%的样本不会被抽到
2.在所有属性上对这n个样本建立分类器(比如决策树,svm,lr)
3.重复步骤1和2m次,建立了m个分类器
4.将数据放在这m个分类器上,根据这m个分类器的投票结果决定数据属于哪一类
随机森林--在Bagging基础上做了改进
1.从样本中重采样(有放回的)选出n个样本,与bagging相同
2.从属性中选出k个属性,选择最佳分割属性作为节点建立CART决策树,假设有F个特征,k一般取log2F
3.重复步骤1和2m次,建立了m个决策树分类器
4.这m个决策树构成了随机森林,根据投票结果决定数据属于哪一类
随机森林和bagging主要是降低方差。在随机森林中也可以使用svm、lr等其他的分类器。
随机森林中oob error的计算:
对于每棵树来说,样本集采样都是放回采样,所以必然有一些样本没有被采样到,没有被采样到的样本称为袋外数据(out of bag)。(1-1/M)^M求极限得1/e,也就是有大约 1/e的数据没有被采样到。可以利用oob来验证随机森林的性能。
如上图,*表示该样本没有被gt采样到,比如(xn,yn)假设它只没有被g2,g3,gT采样到,对于这个样本可以让g2,g3,gT分类,取投票结果,如果与其真实分类不同,则错误样本数加一。
同样的对于所有的样本,将他们用没有使用它们的树集合来分类,统计他们的错误数,然后除以总的样本数,也就是oob error了。公式如下图所示:
随机森林进行特征重要性度量的详细说明--可以借此进行特征选择
特征选择方法中,有一种方法是利用随机森林,进行特征的重要性度量,选择重要性较高的特征。下面对如何计算重要性进行说明。
1 特征重要性度量
计算某个特征X的重要性时,具体步骤如下:
1)对每一颗决策树,选择相应的袋外数据(out of bag,OOB)计算袋外数据误差,记为errOOB1.
所谓袋外数据是指,每次建立决策树时,通过重复抽样得到一个数据用于训练决策树,这时还有大约1/3的数据没有被利用,没有参与决策树的建立。这部分数据可以用于对决策树的性能进行评估,计算模型的预测错误率,称为袋外数据误差。
这已经经过证明是无偏估计的,所以在随机森林算法中不需要再进行交叉验证或者单独的测试集来获取测试集误差的无偏估计。
2)随机对袋外数据OOB所有样本的特征X加入噪声干扰(可以随机改变样本在特征X处的值),再次计算袋外数据误差,记为errOOB2。
3)假设森林中有N棵树,则特征X的重要性=∑(errOOB2-errOOB1)/N。这个数值之所以能够说明特征的重要性是因为,如果加入随机噪声后,袋外数据准确率大幅度下降(即errOOB2上升),说明这个特征对于样本的预测结果有很大影响,进而说明重要程度比较高。
2 特征选择
在特征重要性的基础上,特征选择的步骤如下:
1)计算每个特征的重要性,并按降序排序
2)确定要剔除的比例,依据特征重要性剔除相应比例的特征,得到一个新的特征集
3)用新的特征集重复上述过程,直到剩下m个特征(m为提前设定的值)。
4)根据上述过程中得到的各个特征集和特征集对应的袋外误差率,选择袋外误差率最低的特征集。
AdaBoosting (Adaptive Boosting)
提高前一轮被弱分类器错误分类样本的权重,降低那些正确分类的样本的权值
加权多数表决:加大分类误差小的分类器的权值,减小分类误差大的分类器的权值
AdaBoost算法是模型为加法模型、损失函数为指数函数、学习算法为前向分布算法的二类学习算法。
AdaBoost算法可以看做是采用指数损失函数的提升方法,每个基函数的学习算法为前向分步算法。
AdaBoost的训练误差是以指数速率下降的,不需要事先知道下界,具有自适应性,它能自适应弱分类器的训练误差。
boosting的思想主要是降低偏差。
GBDT(Gradient Bossting Decision Tree)
提升树是以分类树或回归树最为基本分类器的提升方法。
简单的损失函数优化比较简单,可以使用残差作为拟合的目标,对于一般的损失函数来说,利用损失函数的负梯度在当前模型的值作为残差的近似值
对于决策树,其实可以把它表示为下式,即是把特征空间划分为多个区域,每个区域返回某个值作为决策树的预测值
gbdt算法的步骤
随机森林与gbdt的异同
随机森林的优点:
实现简单,训练速度快,泛化能力强,可以并行实现,因为训练时树与树之间是相互独立的;
相比单一决策树,能学习到特征之间的相互影响,且不容易过拟合;
能处理高维数据(即特征很多),并且不用做特征选择,因为特征子集是随机选取的;
对于不平衡的数据集,可以平衡误差;
相比SVM,不是很怕特征缺失,因为待选特征也是随机选取;
训练完成后可以给出哪些特征比较重要。
随机森林的缺点:
在噪声过大的分类和回归问题还是容易过拟合;
相比于单一决策树,它的随机性让我们难以对模型进行解释。
GBDT和随机森林的相同点:
1、都是集成算法,都是由多棵树组成
2、最终的结果都是由多棵树一起决定
GBDT和随机森林的不同点:
1、组成随机森林的树可以是分类树,也可以是回归树;而GBDT只由回归树组成
2、组成随机森林的树可以并行生成;而GBDT只能是串行生成
3、对于最终的输出结果而言,随机森林采用多数投票等;而GBDT则是将所有结果累加起来,或者加权累加起来
4、随机森林对异常值不敏感,GBDT对异常值非常敏感
5、随机森林对训练集一视同仁,GBDT是基于权值的弱分类器的集成
6、随机森林是通过减少模型方差提高性能,GBDT是通过减少模型偏差提高性能
gbdt正则化的几个参数
Shrinkage
Shrinkage就是将每棵树的输出结果乘一个因子(0
fm(x)=fm−1(x)+ν⋅ΣJmj=1γjmI(x∈Rjm)fm(x)=fm−1(x)+ν⋅Σj=1JmγjmI(x∈Rjm)
ESL书中这样讲:
The parameter νν can be regarded as controlling the leanring rate of the boosting procedure
νν和迭代轮数M(树个数)是一个tradeoff,推荐的是νν值设置小一点(如0.1),而M设置大一些。这样一般能有比较好的准确率,代价是训练时间变长(与M成比例)。
Subsampling
Subsampling其实源于bootstrap averaging(bagging)思想,GBDT里的做法是在每一轮建树时,样本是从训练集合中无放回随机抽样的ηη部分,典型的ηη值是0.5。这样做既能对模型起正则作用,也能减少计算时间。
事实上,XGBoost和Sklearn的实现均借鉴了随机森林,除了有样本层次上的采样,也有特征采样。也就是说建树的时候只从随机选取的一些特征列寻找最优分裂。
Tree ensemble算法的特征重要度计算
集成学习因具有预测精度高的优势而受到广泛关注,尤其是使用决策树作为基学习器的集成学习算法。树的集成算法的著名代码有随机森林和GBDT。随机森林具有很好的抵抗过拟合的特性,并且参数(决策树的个数)对预测性能的影响较小,调参比较容易,一般设置一个比较大的数。GBDT具有很优美的理论基础,一般而言性能更有优势。
基于树的集成算法还有一个很好的特性,就是模型训练结束后可以输出模型所使用的特征的相对重要度,便于我们选择特征,理解哪些因素是对预测有关键影响,这在某些领域(如生物信息学、神经系统科学等)特别重要。本文主要介绍基于树的集成算法如何计算各特征的相对重要度。
使用boosted tree作为学习算法的优势:
使用不同类型的数据时,不需要做特征标准化/归一化
可以很容易平衡运行时效率和精度;比如,使用boosted tree作为在线预测的模型可以在机器资源紧张的时候截断参与预测的树的数量从而提高预测效率
学习模型可以输出特征的相对重要程度,可以作为一种特征选择的方法
模型可解释性好
对数据字段缺失不敏感
能够自动做多组特征间的interaction,具有很好的非性线性
特征重要度的计算
Friedman在GBM的论文中提出的方法:
特征j[Math Processing Error]的全局重要度通过特征j[Math Processing Error]在单颗树中的重要度的平均值来衡量:
J2j^=1M∑m=1MJ2j^(Tm)
[Math Processing Error]
其中,M是树的数量。特征j[Math Processing Error]在单颗树中的重要度的如下:
J2j^(T)=∑t=1L−1i2t^1(vt=j)
[Math Processing Error]
其中,L[Math Processing Error]为树的叶子节点数量,L−1[Math Processing Error]即为树的非叶子节点数量(构建的树都是具有左右孩子的二叉树),vt[Math Processing Error]是和节点t[Math Processing Error]相关联的特征,i2t^[Math Processing Error]是节点t[Math Processing Error]分裂之后平方损失的减少值。
实现代码片段
为了更好的理解特征重要度的计算方法,下面给出scikit-learn工具包中的实现,代码移除了一些不相关的部分。
下面的代码来自于GradientBoostingClassifier对象的feature_importances属性的计算方法:
def feature_importances_(self):
total_sum = np.zeros((self.n_features, ), dtype=np.float64)
for tree in self.estimators_:
total_sum += tree.feature_importances_
importances = total_sum / len(self.estimators_)
return importances
其中,self.estimators_是算法构建出的决策树的数组,tree.feature_importances_ 是单棵树的特征重要度向量,其计算方法如下:
cpdef compute_feature_importances(self, normalize=True):
"""Computes the importance of each feature (aka variable)."""
while node != end_node:
if node.left_child != _TREE_LEAF:
# ... and node.right_child != _TREE_LEAF:
left = &nodes[node.left_child]
right = &nodes[node.right_child]
importance_data[node.feature] += (
node.weighted_n_node_samples * node.impurity -
left.weighted_n_node_samples * left.impurity -
right.weighted_n_node_samples * right.impurity)
node += 1
importances /= nodes[0].weighted_n_node_samples
return importances
上面的代码经过了简化,保留了核心思想。计算所有的非叶子节点在分裂时加权不纯度的减少,减少得越多说明特征越重要。