集成学习: ensemble learning, 通过构建多个学习器来完成学习任务,
要获得好的集成:个体学习器应“好而不同”,具备准确性和多样性。
Boosting
Boosting:个体学习器间存在强依赖关系,必须串行生成的序列化方法。工作机制:先从初始训练集训练出一个基学习器,再根据基学习器的表现对训练集样本分布进行调整,使得先前基学习器训练错误的训练样本在后续收到更多的关注,然后基于调整后的样本分布来训练下一个基学习器;如此反复进行,直至基学习器数目达到指定值,最终将这些基学习器进行结合。代表算法:AdaBoost,GDBT,XGBoost。
Boosting算法要求基学习器能对特定的数据分布进行学习,可通过“重赋权法re-weighting”(在训练过程中的每一轮,根据样本分布为每个训练样本重新赋予一个权重)和“重采样法re-sampling”(在每一轮学习中,根据样本分布对训练样本重新进行采样,再用重新采样的样本集对基学习器进行训练)实施。从偏差-方差分解的角度,Boosting主要关注降底偏差,因此它能基于泛化性能相当弱的学习器构建出很强的集成。
AdaBoost
1. 算法描述
给定二分类数据集 D={(x1,y1),(x2,y2),⋯,(xm,ym},xi∈Rn,yi∈{−1,+1} ,AdaBoost算法流程如下:
初始训练数据的权重分布: V1=(v11,⋯,v1i,⋯,v1m),v1i=1m
对 g=1,2,⋯,G :
a) 使用具有权值分布 Vt 的训练数据集学习,得到基本分类器 ht(x)
b) 计算 ht(x) 在训练集上的分类误差率: ϵg=P(ht(xi)≠yi)=∑i=1mI(ht(x)≠yi)
c) 计算 ht(x) 的系数: αg=12log1−ϵgϵg,log 是自然对数
d) 更新训练数据集的权值分布: Vg+1=(vg+1,1,⋯,vg+1,i,⋯,vg+1,m)vg+1,i=vgiZtexp(−αgyihg(xi)),
其中 Zt 是规范化因子: Zg=∑i=1mvgiexp(−αgyihg(xi)) 使 Vt+1 成为一个概率分布。
构建基本分类器的线性组合: H(x)=sign(h(x))=sign(∑g=1Gαghg(x)) .
2. AdaBoost的训练误差界: 1m∑i=1mI(H(xi)≠yi)≤1m∑imexp(−yih(xi))=∏gGZg
2.1 二分类问题AdaBoost的训练误差界:
∏g=1GZg=∏g=1G[1ϵg(1−ϵg)−−−−−−−−√]=∏g=1G(1−4γ2g)−−−−−−−−√≤exp(−2∑g=1Gγ2g),γg=12−ϵg
2.2 推论:如果存在
γ>0
,对所有
t
有
AdaBoost算法不需要知道下界 γ ,AdaBoost具有适应性,它能适应弱分类器各自的训练误差率。
3 AdaBoost算法的解释:可以认为AdaBoost算法是模型为加法模型、损失函数为指数函数、学习算法为前向分步算法时的二分类学习方法。
3.1 前向分步算法:考虑加法模型(additive model): f(x)=∑g=1Gβgb(x;θg),b(x;θg) 是基函数, θg 是基函数参数。在给定训练数据集和损失函数的条件下,学习加法模型成为损失函数极小化问题: minβt,θtL(yi,f(xi)) ,前向分步算法解决思路是:从前向后,每一步只学习一个基函数及其系数(每一步的优化损失函数 minβ,θL(yi,βb(x;θ)) ),算法流程如下:
初始化 f0(x)=0
对 g=1,2,⋯,G :
a) 极小化损失函数得到参数 (βg,θg)=argminβ,θ∑i=1mL(yi,fg−1(xi)+βb(xi;θ))
b) 更新 fg(x)=fg−1(xi)+βgb(xi;θg)
得到加法模型: f(x)=fG(x)=∑g=1Gβgb(x;θg)
3.2 前向分步算法与AdaBoots: AdaBoost算法是前向分步算法的特例,这时,模型是由基本分类器组成的加法模型,损失函数是指数函数。如前面描述的AdaBoost算法流程可知,前向分步算法逐一学习基函数与AdaBoost逐一学习基本分类器过程一致,下面说明前向分步算法优化指数损失函数与AdaBoost基本分类器学习一致:
指数损失函数: L(yi,f(x))=exp[−yf(x)],f(x)=∑g=1Gαghg(x)
假设经过 g−1 轮迭代前向分步算法得到 fg−1(x)=fg−2(x)+αghg(x)=∑j=1g−1αjhj(x) ,在第 t 代得到
αg,hg(x)及fg(x) 其中 fg(x)=fg−1(x)+αghg(x) 且 αg,hg(x) 满足使 fg(x) 在训练数据集上的指数损失最小,即:(αg,hg(x))=argminα,h∑i=1mexp[−yi(fg−1(xi)+αh(xi))]=argminα,h∑i=1mw¯giexp[−yiαh(xi)],w¯gi=exp[−yifg−1(xi)]
求 h∗g(x) ,对任意 α>0,h∗g(x)=argminh∑i=1mw¯giI(yi≠h(xi)
求 α∗t ,首先 ∑i=1mw¯giexp[−yiαh(xi))]=∑yi=hg(xi)w¯gie−α+∑yi≠hg(xi)w¯gieα=(eα−e−α)∑i=1mw¯giI(yi≠h(xi))+e−α∑i=1mw¯gi 将$
h_g^(x) 代入,对 \alpha 求导等于0可得: \alpha_g^=\frac{1}{2}\log\frac{1-\epsilon_g}{\epsilon_g},\epsilon_g=\sum\limits_{i=1}^m\bar{w}_{gi}\mathbb{I}(y_i\neq h(x_i))$
可见, h∗g(x),α∗g 均与AdaBoost算法一致。
4 Adaboost算法优缺点:
优点
1) Adaboost是一种有很高精度的分类器
2) 可以使用各种方法构建子分类器,Adaboost算法提供的是框架
3) 当使用简单分类器时,计算出的结果是可以理解的。而且弱分类器构造极其简单
4) 不易overfitting(过拟合)(注意是弱学习器前提下)
缺点
1) 容易受到噪声干扰
2) 训练时间过长
3) 执行效果依赖于弱分类器的选择
4) 数据不平衡导致分类精度下降
5 AdaBoost for 分类和回归
• 多分类任务:AdaBoost-SAMME 和 AdaBoost-SAMME.R.
• 回归: AdaBoost.R2
参数
- 基学习器
- 基学习器个数
- 学习率
'''
机器学习实战:AdaBoost
mete-algorithm元算法就是集成算法ensemble algorithm
AdaBoost
优点:泛化错误率低,易编码,可应用于大部分分类器上,无参数调整?
缺点:对离群点敏感
单层决策树:仅基于单个特征来做决策
二分类,标签必须是-1和1!!!
'''
#coding = utf-8
from numpy import *
def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
retArray = ones((dataMatrix.shape[0], 1))
if threshIneq == 'lt':
retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
else:
retArray[dataMatrix[:, dimen] > threshVal] = -1.0
return retArray
def buildStump(dataArr, classLabels, D):
'''构建基学习器:树桩(单层决策树)'''
dataMatrix = mat(dataArr)
labelMat = mat(classLabels).T
#print(dataMatrix.shape, labelMat.shape)
m, n = shape(dataMatrix)
numSteps = 10.0
bestStump = {}
bestClasEst = mat(zeros((m, 1)))
minError = inf
for i in range(n):
rangeMin = dataMatrix[:, i].min()
rangeMax = dataMatrix[:, i].max()
stepSize = (rangeMax - rangeMin) / numSteps
for j in range(-1, int(numSteps) + 1):
for inequal in ['lt', 'gt']:
threshVal = (rangeMin + float(j) * stepSize)
predictedVals = stumpClassify(dataMatrix, i, threshVal,inequal)
errArr = mat(ones((m, 1)))
errArr[predictedVals == labelMat] = 0
weightedError = (D.T) * errArr
if weightedError < minError:
minError = weightedError
bestClasEst = predictedVals.copy()
bestStump['dim'] = i
bestStump['thresh'] = threshVal
bestStump['ineq'] = inequal
return bestStump, minError, bestClasEst
def adaboost(dataArr, classLabels, numDS=2):
'''基于单层决策树的adaboost训练过程'''
weakClassifier = []
m = mat(dataArr).shape[0]
D = mat(ones((m,1))/m)
aggClassPre = mat(zeros((m,1)))
for i in range(numDS):
bestStump,error,classPre = buildStump(dataArr,classLabels,D)
alpha = float(0.5*log((1-error)/max(error,1e-16)))
bestStump['alpha'] = alpha
weakClassifier.append(bestStump)
t = multiply(-1*alpha*(mat(classLabels).T),classPre)#分类正确为-alpha,错误为alpha
D = multiply(D,exp(t))
D = D/sum(D)
aggClassPre +=alpha*classPre#adaboost最终结果是sign(基分类器*alpha累加)
aggError = multiply(sign(aggClassPre) != mat(classLabels).T,ones((m,1)))
errorRate = aggError.sum()/m
if errorRate == 0.0: break
return weakClassifier,aggClassPre
def adaboostClassify(classifierArr,testData):
'''分类函数'''
data = mat(testData)
m = data.shape[0]
aggPre = mat(zeros((m,1)))
for i in range(len(classifierArr)):
pre = stumpClassify(data,classifierArr[i]['dim'],classifierArr[i]['thresh'],classifierArr[i]['ineq'])
pre = classifierArr[i]['alpha']*pre
aggPre +=pre
return sign(aggPre)
def plotROC(predStrengths, classLabels):
'''
给定m+个正例和m-个反例,根据学习其预测结果对样例进行排序,然后把分类阈值设为最大,即所有样例均为反例,此时真正例率和
假正例率均为0,从坐标(0,0)开始。然后将分类阈值依次设为每个样例的预测值,即依次将每个样例划分为正例。
设前一个标记点坐标为(x,y),当前若为真正例,则对应标记点坐标为(x,y+1/m+);当前若为假正例,则对应标记点坐标为(x+1/m-,y)。
'''
import matplotlib.pyplot as plt
cur = (0.0,0.0) #cursor
ySum = 0.0 #variable to calculate AUC
numPosClas = sum(array(classLabels)==1.0)
yStep = 1/float(numPosClas)#1/m+
xStep = 1/float(len(classLabels)-numPosClas)#1/m-
sortedIndicies = predStrengths.argsort()
fig = plt.figure()
fig.clf()
ax = plt.subplot(111)
l = sortedIndicies.tolist()[0]
for i in range(len(l)):
j = len(l)-i-1
index = l[j]
if classLabels[index] == 1:
delX = 0
delY = yStep
else:
delX = xStep
delY = 0
ySum += cur[1]
ax.plot([cur[0],cur[0]+delX],[cur[1],cur[1]+delY], c='b')
cur = (cur[0]+delX,cur[1]+delY)
ax.plot([0,1],[0,1],'b--')
plt.xlabel('False positive rate'); plt.ylabel('True positive rate')
plt.title('ROC curve for AdaBoost horse colic detection system')
ax.axis([0,1,0,1])
plt.show()
print("the Area Under the Curve is: ",ySum*xStep)
myData,mylabels = loadDataSet('horseColicTraining2.txt')
classfierArr,aggClassPre = adaboost(myData,mylabels,10)
plotROC(aggClassPre.T,mylabels)
提升树
以分类树或回归树为基学习器的提升方法(二叉树),1.1 提升树模型: fG(x)=∑g=1GT(x;Θg),T(x;Θg) 表示决策树, Θg 表示决策树的参数, G 为决策树个数。
1.2 提升树算法:
不同损失函数for不同问题:比如用平方误差损失函数的回归问题,指数损失函数的分类问题,一般损失函数的一般问题
1.3 回归问题的提升树: T(x;Θ)=∑j=1JcjI(x∈Rj) 其中 Θ={(R1,c1),(R2,c2),⋯,(RJ,cJ)} 表示树的区域划分和各区域上的输出。当使用平方误差损失函数时, L(yi,fg−1(xi)+T(x;Θ))=[yi−fg−1(xi)−T(x;Θ)]2=[r−T(x;Θ)]2 其中 r=y−fg−1(x) 表示当前模型你和数据的**残差**residual。所以这里决策树是通过拟合残差学习的。
2 梯度提升GDBT
针对一般损失函数,利用最速下降法的近似方法,关键是用当前模型的损失函数的负梯度值( −[∂L(y,f(xi))∂f(xi)]f(x)=fg−1(x) )作为残差的近似值拟合学习回归数。
2.1 梯度提升算法流程:
初始化 f0(x)=argminc∑i=1mL(yi,c)
对 g=1,2,⋯,G
a) 对 i=1,2,⋯,m 计算: rgi=−[∂L(y,f(xi))∂f(xi)]f(x)=fg−1(x)
b) 对 rgi 拟合一个回归树,得到第 g 棵树的叶节点区域:
Rgj,j=1,2,⋯,J c) 对 j=1,2,⋯,J 计算: cgj=argminc∑i=1mL(yi,fg−1(xi)+c))
d) 更新: fg(x)=fg−1(x)+∑j=1JcgjI(x∈Rgj)
得到回归树: f^(x)=fG(x)=∑g=1G∑j=1JcgjI(x∈Rgj)
2.2 常用损失函数
对于分类算法
- 指数损失函数: L(y,f(x))=exp(−yf(x))
- 对数损失函数:二类 L(y,f(x))=log(1+exp(−yf(x))) 多类 L(y,f(x))=−∑k=1Kyklogpk(x),pk(x)=exp(fk(x))∑l=1Kexp(fl(x))
对于回归算法
均方差: L(y,f(x))=(y−f(x))2
绝对损失: L(y,f(x))=|y−f(x)|
Huber损失(均方差和绝对损失的折衷产物,对于远离中心的异常点,采用绝对损失,而中心附近的点采用均方差,这个界限一般有分位数点衡量):
L(y,f(x))={12(y−f(x))2,δ(|y−f(x)|−δ2),|y−f(x)|≤δ|y−f(x)|>δ分位数损失:它对应的是分位数回归的损失函数,表达式为 L(y,f(x))=∑y≥f(x)θ|y−f(x)|+∑y<f(x)(1−θ)|y−f(x)| ,其中 θ 为分位数,需要我们在回归前指定。
随机梯度提升树(Stochastic Gradient Boosting Tree, SGBT):使用不放回采样的梯度提升树。
2.3 参数及调优
2.3.1 boosting框架参数
首先,我们来看boosting框架相关的重要参数。由于GradientBoostingClassifier和GradientBoostingRegressor的参数绝大部分相同,我们下面会一起来讲,不同点会单独指出。
1) n_estimators: 也就是弱学习器的最大迭代次数,或者说最大的弱学习器的个数。一般来说n_estimators太小,容易欠拟合,n_estimators太大,又容易过拟合,一般选择一个适中的数值。默认是100。在实际调参的过程中,我们常常将n_estimators和下面介绍的参数learning_rate一起考虑。
2) learning_rate: 即每个弱学习器的权重缩减系数νν,也称作步长,在原理篇的正则化章节我们也讲到了,加上了正则化项,我们的强学习器的迭代公式为fk(x)=fk−1(x)+νhk(x)fk(x)=fk−1(x)+νhk(x)。νν的取值范围为0<ν≤10<ν≤1。对于同样的训练集拟合效果,较小的νν意味着我们需要更多的弱学习器的迭代次数。通常我们用步长和迭代最大次数一起来决定算法的拟合效果。所以这两个参数n_estimators和learning_rate要一起调参。一般来说,可以从一个小一点的νν开始调参,默认是1。
3) subsample: 即我们在原理篇的正则化章节讲到的子采样,取值为(0,1]。注意这里的子采样和随机森林不一样,随机森林使用的是放回抽样,而这里是不放回抽样。如果取值为1,则全部样本都使用,等于没有使用子采样。如果取值小于1,则只有一部分样本会去做GBDT的决策树拟合。选择小于1的比例可以减少方差,即防止过拟合,但是会增加样本拟合的偏差,因此取值不能太低。推荐在[0.5, 0.8]之间,默认是1.0,即不使用子采样。
4) init: 即我们的初始化的时候的弱学习器,拟合对应原理篇里面的f0(x)f0(x),如果不输入,则用训练集样本来做样本集的初始化分类回归预测。否则用init参数提供的学习器做初始化分类回归预测。一般用在我们对数据有先验知识,或者之前做过一些拟合的时候,如果没有的话就不用管这个参数了。
5) loss: 即我们GBDT算法中的损失函数。分类模型和回归模型的损失函数是不一样的。
对于分类模型,有对数似然损失函数”deviance”和指数损失函数”exponential”两者输入选择。默认是对数似然损失函数”deviance”。在原理篇中对这些分类损失函数有详细的介绍。一般来说,推荐使用默认的”deviance”。它对二元分离和多元分类各自都有比较好的优化。而指数损失函数等于把我们带到了Adaboost算法。
对于回归模型,有均方差”ls”, 绝对损失”lad”, Huber损失”huber”和分位数损失“quantile”。默认是均方差”ls”。一般来说,如果数据的噪音点不多,用默认的均方差”ls”比较好。如果是噪音点较多,则推荐用抗噪音的损失函数”huber”。而如果我们需要对训练集进行分段预测的时候,则采用“quantile”。
6) alpha:这个参数只有GradientBoostingRegressor有,当我们使用Huber损失”huber”和分位数损失“quantile”时,需要指定分位数的值。默认是0.9,如果噪音点较多,可以适当降低这个分位数的值。
2.3.2 弱学习器参数
这里我们再对GBDT的类库弱学习器的重要参数做一个总结。由于GBDT使用了CART回归决策树,因此它的参数基本来源于决策树类,也就是说,和DecisionTreeClassifier和DecisionTreeRegressor的参数基本类似。如果你已经很熟悉决策树算法的调参,那么这一节基本可以跳过。不熟悉的朋友可以继续看下去。
1) 划分时考虑的最大特征数max_features: 可以使用很多种类型的值,默认是”None”,意味着划分时考虑所有的特征数;如果是”log2”意味着划分时最多考虑log2Nlog2N个特征;如果是”sqrt”或者”auto”意味着划分时最多考虑N−−√N个特征。如果是整数,代表考虑的特征绝对数。如果是浮点数,代表考虑特征百分比,即考虑(百分比xN)取整后的特征数。其中N为样本总特征数。一般来说,如果样本特征数不多,比如小于50,我们用默认的”None”就可以了,如果特征数非常多,我们可以灵活使用刚才描述的其他取值来控制划分时考虑的最大特征数,以控制决策树的生成时间。
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。
2.4 GBDT的优缺点
主要的优点有:
1) 可以灵活处理各种类型的数据,包括连续值和离散值。
2) 在相对少的调参时间情况下,预测的准备率也可以比较高。这个是相对SVM来说的。
3) 使用一些健壮的损失函数,对异常值的鲁棒性非常强。比如 Huber损失函数和Quantile损失函数。
主要缺点有:
1)由于弱学习器之间存在依赖关系,难以并行训练数据。不过可以通过自采样的SGBT来达到部分并行。
XGBoost
1. 算法描述
1.1 tree ensemble model: y^i=∑k=1Kfk(xi),fk∈F 其中 F 表示包含所有回归树(不仅仅是回归问题,可以定义不同的损失函数for不同的学习任务)的函数空间。参数:每棵树的结构以及叶子结点的输出。
1.2 目标函数: Obj=∑i=1mL(yi,y^i)+∑k=1KΩ(fk) 其中 Ω(fk) 表示正则项,树的复杂度(树的节点数(对应剪枝优化)、树的深度、叶子结点权重(输出值)的2-范数等等)
1.3 Additive Training(Boosting): y^0i=0y^1i=f(1(xi))=y^0i+f1(xi)y^2i=f1(xi)+f2(xi)=y^1i+f2(xi)⋯y^ti=∑k=1tfk(xi)=y^t−1i+ft(xi)
ft(xi)=argminftObjt=argminft∑i=1mL(yi,y^ti)+∑k=1tΩ(fk)=argminft∑i=1mL(yi,y^t−1i+ft(xi))+∑k=1tΩ(fk)+constant
比如均方损失函数: Objt=∑i=1m(yi−(y^t−1i+ft(xi)))2+∑k=1tΩ(fk)+constant=∑i=1m(2(y^t−1i−yi)ft(xi)+ft(xi)2)+∑k=1tΩ(fk)+constant
其中 2(y^t−1i−yi) 表示残差
更加一般的,对于不是平方误差的情况,我们会采用如下的泰勒展开近似来定义一个近似的目标函数,方便我们进行这一步的计算:
泰勒展开式: f(x+Δx)≃f(x)+f′(x)Δx+12f′′(x)Δx2
定义: g=∂y^t−1L(yi,y^t−1),hi=∂2y^t−1L(yi,y^t−1)
则: Objt≃∑i=1m[L(yi,y^t−1)+gift(xi)+12hif2t(xi)]+Ω(ft(x))+const
1.4 树的复杂度:
将树拆分为结构部分
qq
和叶子权重部分
c
:
定义:
Gj=∑xi∈Rjgi,Hj=∑xi∈Rjhi
,
Objt=∑j=1J[Gjcj+12(Hj+λ)c2j]+γJ
,那么假设
q
已知,最优
寻找最优分割:对每个节点,枚举所有的特征:对于每个特征,根据特征值将样本排序;使用线性扫描来决定该特征的最优分割;对所有特征均采用最优分割。
时间复杂度(深度为K的树):
O(mnKlogm)
(对每一层,需要
O(mlogm)
排序,共
n
个特征,
1.5 xgboost与gdbt
- xgboost在目标函数中显示的加上了正则化项,基学习为CART时,正则化项与树的叶子节点的数量和叶子节点的值有关。
- GB中使用Loss Function对 f(x) 的一阶导数计算出伪残差用于学习生成 ft(x) ,xgboost不仅使用到了一阶导数,还使用二阶导数。
- GB中寻找最佳分割点的衡量标准是最小化均方差,xgboost寻找分割点的标准是最大化增益(与 λ,γ ,正则化项相关)。
xgboost与gdbt除了上述三点的不同,xgboost在实现时还做了许多优化:
- 在寻找最佳分割点时,考虑传统的枚举每个特征的所有可能分割点的贪心法效率太低,xgboost实现了一种近似的算法。大致的思想是根据百分位法列举几个可能成为分割点的候选者,然后从候选者中根据上面求分割点的公式计算找出最佳的分割点。
- xgboost考虑了训练数据为稀疏值的情况,可以为缺失值或者指定的值指定分支的默认方向,这能大大提升算法的效率,paper提到50倍。
- 特征列排序后以块的形式存储在内存中,在迭代中可以重复使用;虽然boosting算法迭代必须串行,但是在处理每个特征列时可以做到并行。
- 按照特征列方式存储能优化寻找最佳的分割点,但是当以行计算梯度数据时会导致内存的不连续访问,严重时会导致cache miss,降低算法效率。paper中提到,可先将数据收集到线程内部的buffer,然后再计算,提高算法的效率。
- xgboost 还考虑了当数据量比较大,内存不够时怎么有效的使用磁盘,主要是结合多线程、数据压缩、分片的方法,尽可能的提高算法的效率。
2. 参数及调优
2.1 通用参数
2.1.1 booster[默认gbtree]
- 选择每次迭代的模型,有两种选择:
gbtree:基于树的模型
gbliner:线性模型
2.1.2 silent[默认0]
- 当这个参数值为1时,静默模式开启,不会输出任何信息。
- 一般这个参数就保持默认的0,因为这样能帮我们更好地理解模型。
2.1.3 nthread[默认值为最大可能的线程数]
- 这个参数用来进行多线程控制,应当输入系统的核数。
- 如果你希望使用CPU全部的核,那就不要输入这个参数,算法会自动检测它。
还有两个参数,XGBoost会自动设置,目前你不用管它。接下来咱们一起看booster参数。
2.2 booster参数
尽管有两种booster可供选择,我这里只介绍tree booster,因为它的表现远远胜过linear booster,所以linear booster很少用到。
2.2.1 eta[默认0.3]
- 和GBM中的 learning rate 参数类似。
- 通过减少每一步的权重,可以提高模型的鲁棒性。
- 典型值为0.01-0.2。
2.2.2 min_child_weight[默认1]
- 决定最小叶子节点样本权重和。
- 和GBM的 min_child_leaf 参数类似,但不完全一样。XGBoost的这个参数是最小样本权重的和,而GBM参数是最小样本总数。
- 这个参数用于避免过拟合。当它的值较大时,可以避免模型学习到局部的特殊样本。
- 但是如果这个值过高,会导致欠拟合。这个参数需要使用CV来调整。
2.2.3 max_depth[默认6]
- 和GBM中的参数相同,这个值为树的最大深度。
- 这个值也是用来避免过拟合的。max_depth越大,模型会学到更具体更局部的样本。
- 需要使用CV函数来进行调优。
- 典型值:3-10
2.2.4 max_leaf_nodes
- 树上最大的节点或叶子的数量。
- 可以替代max_depth的作用。因为如果生成的是二叉树,一个深度为n的树最多生成n2个叶子。
- 如果定义了这个参数,GBM会忽略max_depth参数。
2.2.5 gamma[默认0]
- 在节点分裂时,只有分裂后损失函数的值下降了,才会分裂这个节点。Gamma指定了节点分裂所需的最小损失函数下降值。
- 这个参数的值越大,算法越保守。这个参数的值和损失函数息息相关,所以是需要调整的。
2.2.6 max_delta_step[默认0]
- 这参数限制每棵树权重改变的最大步长。如果这个参数的值为0,那就意味着没有约束。如果它被赋予了某个正值,那么它会让这个算法更加保守。
- 通常,这个参数不需要设置。但是当各类别的样本十分不平衡时,它对逻辑回归是很有帮助的。
- 这个参数一般用不到,但是你可以挖掘出来它更多的用处。
2.2.7 subsample[默认1]
- 和GBM中的subsample参数一模一样。这个参数控制对于每棵树,随机采样的比例。
- 减小这个参数的值,算法会更加保守,避免过拟合。但是,如果这个值设置得过小,它可能会导致欠拟合。
- 典型值:0.5-1
2.2.8 colsample_bytree[默认1]
- 和GBM里面的max_features参数类似。用来控制每棵随机采样的列数的占比(每一列是一个特征)。
- 典型值:0.5-1
2.2.9 colsample_bylevel[默认1]
- 用来控制树的每一级的每一次分裂,对列数的采样的占比。
- 我个人一般不太用这个参数,因为subsample参数和colsample_bytree参数可以起到相同的作用。但是如果感兴趣,可以挖掘这个参数更多的用处。
2.2.10 lambda[默认1]
- 权重的L2正则化项。(和Ridge regression类似)。
- 这个参数是用来控制XGBoost的正则化部分的。虽然大部分数据科学家很少用到这个参数,但是这个参数在减少过拟合上还是可以挖掘出更多用处的。
2.2.11 alpha[默认1]
- 权重的L1正则化项。(和Lasso regression类似)。
- 可以应用在很高维度的情况下,使得算法的速度更快。
2.2.12 scale_pos_weight[默认1]
- 在各类别样本十分不平衡时,把这个参数设定为一个正值,可以使算法更快收敛。
2.3学习目标参数
这个参数用来控制理想的优化目标和每一步结果的度量方法。
2.3.1 objective[默认reg:linear]
- 这个参数定义需要被最小化的损失函数。最常用的值有:
- binary:logistic 二分类的逻辑回归,返回预测的概率(不是类别)。
- multi:softmax 使用softmax的多分类器,返回预测的类别(不是概率)。
- 在这种情况下,你还需要多设一个参数:num_class(类别数目)。
- multi:softprob 和multi:softmax参数一样,但是返回的是每个数据属于各个类别的概率。
2.3.2 eval_metric[默认值取决于objective参数的取值]
- 对于有效数据的度量方法。
- 对于回归问题,默认值是rmse,对于分类问题,默认值是error。
- 典型值有:
- rmse 均方根误差(∑Ni=1ϵ2N−−−−−−√)
- mae 平均绝对误差(∑Ni=1|ϵ|N)
- logloss 负对数似然函数值
- error 二分类错误率(阈值为0.5)
- merror 多分类错误率
- mlogloss 多分类logloss损失函数
- auc 曲线下面积
2.3.3 seed(默认0)
- 随机数的种子
- 设置它可以复现随机数据的结果,也可以用于调整参数
如果你之前用的是Scikit-learn,你可能不太熟悉这些参数。但是有个好消息,python的XGBoost模块有一个sklearn包,XGBClassifier。这个包中的参数是按sklearn风格命名的。会改变的函数名是:
1、eta -> learning_rate
2、lambda -> reg_lambda
3、alpha -> reg_alpha
2.4 参数调优的一般方法
我们会使用和GBM中相似的方法。需要进行如下步骤:
- 选择较高的学习速率(learning rate)。一般情况下,学习速率的值为0.1。但是,对于不同的问题,理想的学习速率有时候会在0.05到0.3之间波动。选择对应于此学习速率的理想决策树数量。XGBoost有一个很有用的函数“cv”,这个函数可以在每一次迭代中使用交叉验证,并返回理想的决策树数量。
- 对于给定的学习速率和决策树数量,进行决策树特定参数调优(max_depth, min_child_weight, gamma, subsample, colsample_bytree)。在确定一棵树的过程中,我们可以选择不同的参数,待会儿我会举例说明。
- xgboost的正则化参数的调优。(lambda, alpha)。这些参数可以降低模型的复杂度,从而提高模型的表现。
- 降低学习速率,确定理想参数。
2.4.1 确定学习速率和tree_based 参数调优的估计器数目
为了确定boosting
参数,我们要先给其它参数一个初始值。咱们先按如下方法取值:
max_depth
= 5 :这个参数的取值最好在3-10之间。我选的起始值为5,但是你也可以选择其它的值。起始值在4-6之间都是不错的选择。min_child_weight
= 1:在这里选了一个比较小的值,因为这是一个极不平衡的分类问题。因此,某些叶子节点下的值会比较小。gamma
= 0: 起始值也可以选其它比较小的值,在0.1到0.2之间就可以。这个参数后继也是要调整的。subsample, colsample_bytree
= 0.8: 这个是最常见的初始值了。典型值的范围在0.5-0.9之间。scale_pos_weight
= 1: 这个值是因为类别十分不平衡。
2.4.2 max_depth 和 min_weight 参数调优
我们先对这两个参数调优,是因为它们对最终结果有很大的影响。首先,我们先大范围地粗调参数,然后再小范围地微调。
注意:在这一节我会进行高负荷的栅格搜索(grid search),这个过程大约需要15-30分钟甚至更久,具体取决于你系统的性能。你也可以根据自己系统的性能选择不同的值。
2.4.3 gamma参数调优
在已经调整好其它参数的基础上,我们可以进行gamma参数的调优了。Gamma参数取值范围可以很大,我这里把取值范围设置为5了。你其实也可以取更精确的gamma值。
2.4.4 调整subsample 和 colsample_bytree 参数
下一步是尝试不同的subsample 和 colsample_bytree 参数。我们分两个阶段来进行这个步骤。这两个步骤都取0.6,0.7,0.8,0.9作为起始值。
2.4.5 正则化参数调优
下一步是应用正则化来降低过拟合。由于gamma函数提供了一种更加有效地降低过拟合的方法,大部分人很少会用到这个参数。但是我们在这里也可以尝试用一下这个参数。我会在这里调整’reg_alpha’参数,然后’reg_lambda’参数留给你来完成。
2.4.6 降低学习速率
最后,我们使用较低的学习速率,以及使用更多的决策树。我们可以用XGBoost中的CV函数来进行这一步工作。
Bagging和随机森林
Bagging和随机森林个体学习器间不存在强依赖关系,可同时生成的并行化化方法。工作机制:给定训练数据集,基于自主采样(给定包含m个样本的数据集,先随机抽取一个样本放入采样集中,再把该样本放回初始数据集,使得下次采样仍有可能选中该样本。初始训练集中约有63.2%( limm↦∞(1−1m)m↦1e≃0.368 )未出现在采样集中样本出现在采样集中,可用于验证测试称为“包外估计”out-of-bag estimation)得到T个采样集,训练T个学习器。不同于标准AdaBoost只用于二分类,Bagging可用于多分类,回归任务。从偏差-方差分解的角度,Bagging主要关注降底方差,因此它在不剪枝决策树、神经网络等易受样本扰动的学习器上效果更为明显。
包外估计:令 Dt 表示 ht 实际使用的训练样本集,令 Hoob(x) 表示包外预测,即仅考虑那些未使用x训练的基学习器在x上的预测,有: Hoob(x)=argmaxy∑t=1TI(ht(x)=y)⋅I(x∉Dt) ,则相应的泛化误差为: ϵoob=1D∑I(Hoob(x)≠y) 。
随机森林Random Forest(RF)的工作机制:以决策树为基学习器构建Bagging基础的基础上,进一步在决策树的训练过程中加入了随机属性选择亦称属性扰动(对基决策树的每个节点,先从该节点的属性集合中随机选择一个包含f个属性的子集,然后再从这个子集中选择一个最优划分属性,参数f控制了随机性的引入程度,一般 f=log2n )。
1. RF算法描述
1.1 随机森林的训练过程可以总结如下:
(1)给定训练集D,测试集T,特征维数n。确定参数:使用到的CART的数量K,每棵树的深度d,每个节点使用到的特征数量f,终止条件:节点上最少样本数s,节点上最少的信息增益g
对于第1-K棵树, k=1−K :
(2)从D中有放回的抽取大小和D一样的训练集 D(k) ,作为根节点的样本,从根节点开始训练
(3)如果当前节点上达到终止条件,则设置当前节点为叶子节点,如果是分类问题,该叶子节点的预测输出为当前节点样本集合中数量最多的那一类c,概率p为c占当前样本集的比例;如果是回归问题,预测输出为当前节点样本集各个样本值的平均值。然后继续训练其他节点。如果当前节点没有达到终止条件,则从d维特征中无放回的随机选取f维特征。利用这f维特征,寻找分类效果最好的一维特征j及其阈值th,当前节点上样本第j维特征小于th的样本被划分到左节点,其余的被划分到右节点。继续训练其他节点。有关分类效果的评判标准在后面会讲。
(4)重复(2)(3)直到所有节点都训练过了或者被标记为叶子节点。
(5)重复(2),(3),(4)直到所有CART都被训练过。
1.2 利用随机森林的预测过程如下:
对于第1-K棵树,i=1-K:
(1)从当前树的根节点开始,根据当前节点的阈值th,判断是进入左节点($$)还是进入右节点($>=th$),直到到达,某个叶子节点,并输出预测值。
(2)重复执行(1)直到所有t棵树都输出了预测值。如果是分类问题,则输出为所有树中预测概率总和最大的那一个类,即对每个c的p进行累计;如果是回归问题,则输出为所有树的输出的平均值。
对于分类问题(将某个样本划分到某一类),也就是离散变量问题,CART使用Gini值作为评判标准(类别分布越平均,Gini值越大,类分布越不均匀,Gini值越小)。在寻找最佳的分类特征和阈值时,评判标准为: argmaxGini−GiniL−GiniR ,即寻找最佳的特征j和阈值th,使得当前节点的Gini值减去左子节点的Gini和右子节点的Gini值最大。
对于回归问题,直接使用 arg\maxVar−VarL−VarR 作为评判标准,即当前节点训练集的方差Var减去减去左子节点的方差 VarL 和右子节点的方差 VarR 值最大。
2. extra trees
extra trees是RF的一个变种, 原理几乎和RF一模一样,仅有区别有:
1) 对于每个决策树的训练集,RF采用的是随机采样bootstrap来选择采样集作为每个决策树的训练集,而extra trees一般不采用随机采样,即每个决策树采用原始训练集。
2) 在选定了划分特征后,RF的决策树会基于信息增益,基尼系数,均方差之类的原则,选择一个最优的特征值划分点,这和传统的决策树相同。但是extra trees比较的激进,他会随机的选择一个特征值来划分决策树。
从第二点可以看出,由于随机选择了特征值的划分点位,而不是最优点位,这样会导致生成的决策树的规模一般会大于RF所生成的决策树。也就是说,模型的方差相对于RF进一步减少,但是偏倚相对于RF进一步增大。在某些时候,extra trees的泛化能力比RF更好。
3. RF参数及调优
3.1 RF框架参数
GBDT的框架参数比较多,重要的有最大迭代器个数,步长和子采样比例,调参起来比较费力。但是RF则比较简单,这是因为bagging框架里的各个弱学习器之间是没有依赖关系的,这减小的调参的难度。换句话说,达到同样的调参效果,RF调参时间要比GBDT少一些。
1) n_estimators: 也就是弱学习器的最大迭代次数,或者说最大的弱学习器的个数。一般来说n_estimators太小,容易欠拟合,n_estimators太大,计算量会太大,并且n_estimators到一定的数量后,再增大n_estimators获得的模型提升会很小,所以一般选择一个适中的数值。默认是100。在实际调参的过程中,我们常常将n_estimators和learning_rate一起考虑。
2) oob_score :即是否采用袋外样本来评估模型的好坏。默认识False。个人推荐设置为True,因为袋外分数反应了一个模型拟合后的泛化能力。
3) criterion**: **即CART树做划分时对特征的评价标准。分类模型和回归模型的损失函数是不一样的。分类RF对应的CART分类树默认是基尼系数gini,另一个可选择的标准是信息增益。回归RF对应的CART回归树默认是均方差mse,另一个可以选择的标准是绝对值差mae。一般来说选择默认的标准就已经很好的。
从上面可以看出, RF重要的框架参数比较少,主要需要关注的是 n_estimators,即RF最大的决策树个数。
3.2 RF决策树参数
下面我们再来看RF的决策树参数,它要调参的参数基本和GBDT相同,如下:
1) RF划分时考虑的最大特征数max_features: 可以使用很多种类型的值,默认是”None”,意味着划分时考虑所有的特征数;如果是”log2”意味着划分时最多考虑log2N个特征;如果是”sqrt”或者”auto”意味着划分时最多考虑N个特征。如果是整数,代表考虑的特征绝对数。如果是浮点数,代表考虑特征百分比,即考虑(百分比xN)取整后的特征数。其中N为样本总特征数。一般来说,如果样本特征数不多,比如小于50,我们用默认的”None”就可以了,如果特征数非常多,我们可以灵活使用刚才描述的其他取值来控制划分时考虑的最大特征数,以控制决策树的生成时间。
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。
4. 随机森林的优缺点:
- 在数据集上表现良好
- 在当前的很多数据集上,相对其他算法有着很大的优势
- 它能够处理很高维度(feature很多)的数据,并且不用做特征选择
- 在训练完后,它能够给出哪些feature比较重要
- 在创建随机森林的时候,对generlization error使用的是无偏估计
- 训练速度快
- 在训练过程中,能够检测到feature间的互相影响
- 训练可以高度并行化
- 实现比较简单
- 随机森林算法能解决分类与回归两种类型的问题,并在这两个方面都有相当好的估计表现
- 就算存在大量的数据缺失,随机森林也能较好地保持精确性
- 当存在分类不平衡的情况时,随机森林能够提供平衡数据集误差的有效方法;
- 有out of bag samples,在随机森林中我们无需再对测试集进行另外的设置。
缺点:
- 随机森林在解决回归问题时并没有像它在分类中表现的那么好,这是因为它并不能给出一个连续型的输出。当进行回归时,随机森林不能够作出超越训练集数据范围的预测,这可能导致在对某些还有特定噪声的数据进行建模时出现过度拟合。
- 对于许多统计建模者来说,随机森林给人的感觉像是一个黑盒子——你几乎无法控制模型内部的运行,只能在不同的参数和随机种子之间进行尝试。
结合策略
学习器结合的好处:*1) 从统计的角度来看,由于学习任务的假设空间往往很大,可能有多个假设在训练集上达到同等性能,此时若使用单学习器可能因误选而导致泛化性能不佳,结合多个学习器会降低这个风险;2) 从计算的方面来看,学习算法往往会陷入局部极小,而通过多次运行之后进行结合可降低陷入糟糕局部极小值的风险;3) 从标识的方面来看,某些任务的真实假设可能不在当前学习算法所考虑的假设空间中,此时若使用单学习器则肯定无效,而通过结合多个学习器,由于相应的假设空间有所扩大,有可能得到更好的近似*。
假设集成包含T个基学习器
{h1,h2,⋯,hT}
,其中
hi
在样本
x
上的输出为
平均法
简单平均法: H(x)=1T∑Tt=1hi(x)
加权平均法: H(x)=∑Tt=1vtht(x)
加权平均法的权重一般是从训练数据中学习而得,现实任务中的预训练样本通常不充分或存在噪声导致学出的权重不可靠,尤其是对规模较大的集成来说。要学习的权重比较多,较容易导致过拟合。一般而言,在个体学习器性能相差较大时宜使用加权平均法,而在个体学习器性能相近时宜使用简单平均法。
投票法
对分类任务来说,学习器将从类别标记集合
{c1,c2⋯,cN}
中预测出一个标记,将学习器
ht
在样本
x
上预测的输出表示为一个
绝对多数投票法:若某标记得票数过半,则预测为该标记;否则拒绝预测。
相对多数投票法: H(x)=cargmaxj∑Tt=1hjt(x) 预测为得票最多的标记,若同时有多个标记获得最高票,则从中随机选取一个
加权投票法: H(x)=cargmaxj∑Tt=1vthjt(x)
学习法
通过另一个学习器来进行结合。把个体学习器成为初级学习器,用于结合的学习器称为次级学习器或元学习器(meta-learner)。
Stacking:先从初级数据集训练出初级学习器,然后再将初级学习器的输出作为样本特征,初始数据集的标记仍作为标记训练次级学习器。一般通过使用交叉验证法,使用训练初级学习器未使用的样本来产生次级学习器。有研究表明,将初级学习器的输出类概率作为次级学习器的输入属性,用多响应线性回归(MLR)作为次级学习器效果较好(MLR是基于线性回归的分类器,它对每个类分别进行线性回归,属于该类的训练样本所对应的输出被置为1,其他类置为0,测试样例将被分给输出值最大的类)。
贝叶斯模型平均(Bayes Model Average BMA):基于后验概率来为不同模型赋予权重。
多样性
多样性度量
给定数据集
D={(x1,y1),(x2,y2),⋯,(xm,ym)}
,对于二分类任务,
yi∈{−1,+1}
,分类器
hs和ht
的预测结果列联表:
a 表示
不合度量: disst=b+cm
相关系数: ρst=ad−bc(a+b)(a+c)(c+d)(b+d)√
Q− 统计量: Qst=ad−bcad+bc
k− 统计量: k=p1−p21−p2 其中 p1 是两个分类器取得一致的概率: p1=a+dm ; p2 是两个分类器偶然达成一致的概率: p2=(a+b)(a+c)+(c+d)(b+d)m2 。若分类器完全一致, k=1 ;若它们仅仅是偶然达成一致, k=0 。
多样性增强
一般思路是在学习过程引入随机性,常见做法:对数据样本、输入属性、输出表示、算法参数进行扰动。
数据样本扰动:给定初始数据集,可以从中产生不同的数据子集,再利用不同的数据子集训练不同的个体学习器,通常基于采样法。注意,对于决策树,神经网络
输入属性扰动:从不同子空间训练不同的个体学习器。“子空间”(属性子集)一般指从初始的高维属性空间投影产生的低维属性空间,描述低维空间的属性是通过初始属性投影变换得到的,不一定是初始属性。随机子空间算法从初始属性集中抽取若干个属性子集,再基于每个属性子集训练一个基学习器。注意:若数据只包含少量属性或者冗余属性少,不宜使用输入属性扰动。
输出表示扰动:对输出表示进行操作以增强多样性。如“翻转法”(随机改变一些训练样本的标记),“输出调制法”(将分类输出转化为回归输出后构建个体学习器),“ECOC法”(利用纠错输出码将多分类任务拆解为一系列二分类任务来训练基学习器)。
算法参数扰动:例如“负相关法”显式地通过正则化项来强制个体神经网络使用不同的参数;替换决策树的特征选择机制。