集成学习
Bagging
Bagging模式有以下特点:
- Bootstrap抽样,也就是有放回
- 弱学习器之间没有依赖关系,可以并行训练
RandomForest
随机森林和决策树的区别
随机森林是最常见的一种Bagging集成算法,它使用决策树作为弱学习器,可以用于分类,也可以用于回归。相比单一的决策树,随机森林有以下特点:
- 对于每颗树,它的训练样本是Bootstrap方式抽取的,也就是假如训练样本有N个,那么就又放回的抽取N个,这样大概有2/3的原样本被抽中,用于训练,剩下的1/3用于验证,还可以用于判断特征的重要性。
- 对于每棵树,它选择最佳特征的时候并不是从所有特征中选择的,假如有M个特征,最终选择的特征只是从m(m<<M)个随机特征中选择出来的。
- 不需要剪枝
- 不容易过拟合
sklearn工具包
#随机森林可以用来分类,也可以用来回归
#随机森林的几个重要的参数:n_estimators、max_features、max_depth、min_samples_split、min_samples_leaf
# n_estimators表示弱学习器个数,太小容易欠拟合,太大导致计算量很大。默认100
# max_features表示选择最优特征时,使用的特征数量,默认"aotu",表示划分时最多考虑sqrt(M)个特征,"log2"表示最多考虑log2M个特征。
#以分类树为例
from sklearn.ensemble import RandomForestClassifier
clf= RandomForestClassifier()
clf.fit(data, labels)
#随机森林有自带的验证方法
print(clf.oob_score_)
随机森林常见面试题
- 随机森林优缺点
优点:
a. 泛化能力强,精度高
b. 能处理大样本
c. 可以用来做特征选择
d. 可以处理不平衡样本
缺点:
噪声较大时容易过拟合 - 随机森林如何做特征选择
随机森林可以给出特征的重要性,可以用来做特征选择。
clf.feature_importances_
对于每一颗决策树,使用袋外数据计算误差err1,依次对每个特征加入高斯噪声,再次计算袋外误差err2。按照误差变化的程度作为特征重要性的度量。
Boosting
Boosting和Bagging的区别主要从以下几个方面阐述:
- 样本选择
Bagging采用bootstrap方式采样,因此每个弱学习器的训练样本不一样。Boosting模型弱学习器的训练样本一样。 - 并行和串行
Bagging可以并行训练,而Boosting只能串行 - 方差和偏差
Bagging减小方差,Boosting减小偏差 - 弱学习器权重
Bagging中每个弱学习器重要性是一样的,而Boosting中不一样
AdaBoost
AdaBoost基本原理:
训练一个弱学习器,然后对样本进行预测,根据预测的结果调整样本的权值,预测正确的样本权值增加,然后基于调整权值后的样本再次进行训练,直到学习器数量达到指定数量,最后将这些弱学习器加权结合。
AdaBoost算法流程:
- 初始化训练数据的权值分布
D 1 = ( w 11 , w 12 , . . . , w 1 n ) , w 1 i = 1 N , i = 1 , 2 , . . . N {D_1} = \left( {{w_{11}},{w_{12}},...,{w_{1n}}} \right),{w_{1i}} = \frac{1}{N},i = 1,2,...N D1=(w11,w12,...,w1n),w1i=N1,i=1,2,...N样本权重初始化为训练数据的倒数,样本的权重会影响误差率计算,而误差率会影响最终弱分类器权值。 - 对于1,2,3,…m个弱分类器,进行如下循环操作:
a. 对训练数据学习,得到一个弱分类器 G m ( x ) {G_m}\left( x \right) Gm(x)
b. 计算弱分类器在训练集上的误差率
e m = ∑ i = 1 N P ( G m ( x i ) ≠ y i ) = ∑ i = 1 N w m i I ( G m ( x i ) ≠ y i ) {e_m} = \sum\limits_{i = 1}^N {P\left( {{G_m}\left( {{x_i}} \right) \ne {y_i}} \right)} = \sum\limits_{i = 1}^N {{w_{mi}}I\left( {{G_m}\left( {{x_i}} \right) \ne {y_i}} \right)} em=i=1∑NP(Gm(xi)̸=yi)=i=1∑NwmiI(Gm(xi)̸=yi)c. 计算弱分类器的权重
α m = 1 2 log 1 − e m e m {\alpha _m} = \frac{1}{2}\log \frac{{1 - {e_m}}}{{{e_m}}} αm=21logem1−emd.更新样本权重
D m + 1 = ( w m + 1 , 1 , . . . , w m + 1 , n ) w m + 1 , i = w m i Z m exp ( − α m y i G m ( x i ) ) Z m = ∑ i = 1 n w m i exp ( − α m y i G m ( x i ) ) \begin{array}{l} {D_{m + 1}} = \left( {{w_{m + 1,1}},...,{w_{m + 1,n}}} \right)\\ {w_{m + 1,i}} = \frac{{{w_{mi}}}}{{{Z_m}}}\exp \left( { - {\alpha _m}{y_i}{G_m}\left( {{x_i}} \right)} \right)\\ {Z_m} = \sum\limits_{i = 1}^n {{w_{mi}}\exp \left( { - {\alpha _m}{y_i}{G_m}\left( {{x_i}} \right)} \right)} \end{array} Dm+1=(wm+1,1,...,wm+1,n)wm+1,i=Zmwmiexp(−αmyiGm(xi))Zm=i=1∑nwmiexp(−αmyiGm(xi))从公式中可以看出来对于分错的样本,权重是变大的,对于分对的样本,权重是变小的。 - 线性组合弱分类器
G ( x ) = s i g n ( f ( x ) ) = s i g n ( ∑ m = 1 M α m G m ( x ) ) G\left( x \right) = sign\left( {f\left( x \right)} \right) = sign\left( {\sum\limits_{m = 1}^M {{\alpha _m}{G_m}\left( x \right)} } \right) G(x)=sign(f(x))=sign(m=1∑MαmGm(x))
sklearn工具包
from sklearn.ensemble import AdaBoostClassifier
'''
重要参数:
base_estimator:弱学习器种类,默认是决策树,AdaBoostClassifier默认是CART分类树;AdaBoostRegressor默认是CART回归树
n_estimators:弱学习器数量,默认为50
learning_rate:这里的learning_rate代表每个弱学习器权重缩减系数,默认是1
'''
clf= AdaBoostClassifier()
clf.fit(data, labels)
AdaBoost常见面试题
- AdaBoost的优缺点
优点:
分类精度高、不容易过拟合、可以使用很多弱学习器。
缺点:
对异常值很敏感;难以处理样本不均衡的情况 - AdaBoost如何进行回归任务
和分类相比,回归问题主要体现在误差率的计算方法上:
a.计算训练集上的最大误差
b.计算每个样本的相对误差
c.计算误差率等于每个样本乘以相应权重的和
弱学习器权重计算也不一样。
GBDT
GBDT原理概述
GBDT用CART回归树拟合损失函数的负梯度
GBDT的算法流程(回归)
- 初始化弱学习器
f 0 ( x ) = a r g m i n c ∑ i = 1 N L ( y i , c ) {f_0}\left( x \right) = arg\mathop {min}\limits_c \sum\limits_{i = 1}^N {L\left( {{y_i},c} \right)} f0(x)=argcmini=1∑NL(yi,c)L是损失函数,可以是均方误差或者是指数损失,c是一个常数,也就是初始化一个常数作为回归的结果使得损失函数最小。 - 串行训练弱学习器,对于1,2,…,M
a. 对于每个样本i= 1,2,…N,计算它的负梯度
r m i = − [ ∂ L ( y i , f ( x i ) ) ∂ f ( x i ) ] f ( x ) = f m − 1 ( x ) {r_{mi}} = - {\left[ {\frac{{\partial L\left( {{y_i},f\left( {{x_i}} \right)} \right)}}{{\partial f\left( {{x_i}} \right)}}} \right]_{f\left( x \right) = {f_{m - 1}}\left( x \right)}} rmi=−[∂f(xi)∂L(yi,f(xi))]f(x)=fm−1(x)这里的梯度很有意思,梯度下降法中常见的是权重作为参数,在这里弱学习器变成了学习的参数,学习的方向是使得模型的结果和标签越来越接近。
b. 对 r m i {r_{mi}} rmi拟合一个CART回归树,得到第m棵树的叶节点区域 R m j {R_{mj}} Rmj,这里的j是每一个特征。
c. 对每一个特征j= 1,2,…,J,计算:
c m j = a r g m i n c ∑ x i ∈ R m j L ( y i , f m − 1 ( x i ) + c ) {c_{mj}} = arg\mathop {min}\limits_c \sum\limits_{{x_i} \in {R_{mj}}} {L\left( {{y_i},{f_{m - 1}}\left( {{x_i}} \right) + c} \right)} cmj=argcminxi∈Rmj∑L(yi,fm−1(xi)+c)对于这一步我解释一下,比较抽象,其实就是找最佳特征j,使得按照j切分后损失最小,而c表示这种情况下的树的输出值。
d. 更新弱学习器:
f m ( x ) = f m − 1 ( x ) + ∑ j = 1 J c m j I ( x ∈ R m j ) {f_m}\left( x \right) = {f_{m - 1}}\left( x \right) + \sum\limits_{j = 1}^J {{c_{mj}}I\left( {x \in {R_{mj}}} \right)} fm(x)=fm−1(x)+j=1∑JcmjI(x∈Rmj) - 最终的回归树:
f ( x ) ∧ = f M ( x ) = ∑ m = 1 M f m ( x ) \mathop {f\left( x \right)}\limits^ \wedge = {f_M}\left( x \right) = \sum\limits_{m = 1}^M {{f_m}\left( x \right)} f(x)∧=fM(x)=m=1∑Mfm(x)
GBDT的sklearn库
from sklearn.ensemble import GradientBoostingClassifier
'''
重要参数:
n_estimators:弱学习器数量,默认100
learning_rate:弱学习器更新步长,默认为1
loss:损失函数
分类:
对数似然损失函数"deviance"
指数损失函数"exponential"
回归:
均方误差"ls"
绝对误差"lad"
Huber损失"huber",在噪声比较多的情况下使用
'''
clf= GradientBoostingClassifier()
clf.fit(data, labels)
GBDT常见面试题
- GBDT如何用于分类
损失函数使用对数似然函数或者指数函数(退化成AdaBoost),如果我们用对数损失,我们就是用类别的预测概率和标签的差来拟合损失。如果是多分类,就对每个类别训练一颗回归树,然后得到每棵树对输入样本的与测试,最后通过softmax产生概率作为某个样本属于某一类的概率。 - GBDT优缺点
优点:
a. 预测精度高
b. 对异常值不敏感
缺点:
不适用于维度大的样本
XgBoost
XgBoost扩展和改进了GBDT,更快速、更准确。XgBoost和GBDT的区别如下:
- GBDT的弱学习器只能是CART回归树,而XgBoost中的弱学习器不单一
- XgBoost在目标函数中加入正则化项
- GBDT通过计算一阶导数求得梯度,XgBoost中还用到了二阶导数
XgBoost的数学推导
XgBoost的目标函数:
L
t
=
∑
i
=
1
n
l
(
y
i
,
y
^
i
t
−
1
+
f
t
(
x
i
)
)
+
Ω
(
f
t
)
{L_{\rm{t}}} = \sum\limits_{i = 1}^n {l\left( {{y_i},{{\hat y}_i}^{t - 1} + {f_t}\left( {{x_i}} \right)} \right)} + \Omega \left( {{f_t}} \right)
Lt=i=1∑nl(yi,y^it−1+ft(xi))+Ω(ft)t表示第t次模型,
y
^
i
t
−
1
{{{\hat y}_i}^{t - 1}}
y^it−1表示现有的t-1棵树的最优解,
Ω
(
f
t
)
\Omega \left( {{f_t}} \right)
Ω(ft)表示第t棵树模型的正则化项。
二阶泰勒公式:
f
(
x
+
a
)
≈
f
(
a
)
+
f
′
(
a
)
x
+
f
′
′
(
a
)
2
x
2
f\left( {x + a} \right) \approx f\left( a \right) + f'\left( a \right)x + \frac{{f''\left( a \right)}}{2}{x^2}
f(x+a)≈f(a)+f′(a)x+2f′′(a)x2
对目标函数二阶泰勒展开:
L
t
=
∑
i
=
1
n
l
(
y
i
,
f
t
(
x
i
)
+
y
^
i
t
−
1
)
+
Ω
(
f
t
)
≈
∑
i
=
1
n
l
(
y
i
,
y
^
i
t
−
1
)
+
g
i
f
i
(
x
i
)
+
h
i
2
f
i
(
x
i
)
2
{L_t} = \sum\limits_{i = 1}^n {l\left( {{y_i},{f_t}\left( {{x_i}} \right) + {{\hat y}_i}^{t - 1}} \right) + \Omega \left( {{f_t}} \right) \approx \sum\limits_{i = 1}^n {l\left( {{y_i},{{\hat y}_i}^{t - 1}} \right) + } } {g_i}{f_i}\left( {{x_i}} \right) + \frac{{{h_i}}}{2}{f_i}{\left( {{x_i}} \right)^2}
Lt=i=1∑nl(yi,ft(xi)+y^it−1)+Ω(ft)≈i=1∑nl(yi,y^it−1)+gifi(xi)+2hifi(xi)2
正则化项:
Ω
(
f
t
)
=
γ
T
+
1
2
λ
∑
j
=
1
T
w
j
2
\Omega \left( {{f_t}} \right) = \gamma T + \frac{1}{2}\lambda \sum\limits_{j = 1}^T {{w_j}^2}
Ω(ft)=γT+21λj=1∑Twj2T表示叶子节点的数量,
w
j
{{w_j}}
wj表示第j个叶子节点的预测值,把正则化项带入目标函数。
L
t
=
∑
i
=
1
n
[
g
i
f
i
(
x
i
)
+
h
i
2
f
i
(
x
i
)
2
]
+
γ
T
+
1
2
λ
∑
j
=
1
T
w
j
2
{L_t} = \sum\limits_{i = 1}^n {\left[ {{g_i}{f_i}\left( {{x_i}} \right) + \frac{{{h_i}}}{2}{f_i}{{\left( {{x_i}} \right)}^2}} \right] + \gamma T + \frac{1}{2}\lambda \sum\limits_{j = 1}^T {{w_j}^2} }
Lt=i=1∑n[gifi(xi)+2hifi(xi)2]+γT+21λj=1∑Twj2我们知道对于很多样本它们的输出其实是一样的,属于同一个叶子节点,因此我们可以把n转化成T。
L
t
=
∑
j
=
1
T
[
(
∑
i
∈
I
j
g
j
)
w
j
+
(
∑
i
∈
I
j
h
j
+
λ
)
2
w
j
2
]
+
γ
T
{L_t} = \sum\limits_{j = 1}^T {\left[ {\left( {\sum\limits_{i \in {I_j}} {{g_j}} } \right){w_j} + \frac{{\left( {\sum\limits_{i \in {I_j}} {{h_j} + \lambda } } \right)}}{2}{w_j}^2} \right] + \gamma T}
Lt=j=1∑T⎣⎢⎢⎢⎢⎡⎝⎛i∈Ij∑gj⎠⎞wj+2(i∈Ij∑hj+λ)wj2⎦⎥⎥⎥⎥⎤+γT目标函数对
w
j
{{w_j}}
wj求导,令导数为0,求出使得目标函数最小的叶子节点上的预测值
w
j
∗
=
−
g
j
h
j
+
λ
w_j^* = - \frac{{{g_j}}}{{{h_j} + \lambda }}
wj∗=−hj+λgjXgBoost使用这种目标函数来确定切分点。
XgBoost sklearn接口
#安装好XgBoost工具包后
'''
常用参数:
learning_rate:学习率
max_depth:树深度
reg_lambda:控制模型复杂度的L2正则化参数,越大越不容易过拟合
n_estimators:树数量
min_child_weight:默认1,控制叶子节点样本权重和,越小越容易过拟合
gamma:默认0,指定节点分裂最小的增益值
'''
from xgboost.sklearn import XGBClassifier
clf= XGBClassifier()
clf.fit(data,labels)
XgBoost面试
- XgBoost和GBDT的区别
RF、GBDT、XGBOOST常见面试算法整理- 为什么XgBoost用到了二阶导数信息,有什么用?
损失函数的梯度不容易直接求得,用泰勒展开式近似替代损失函数有利于求导,用二阶导信息是因为二阶导可以让梯度收敛得更快更准。- XgBoost如何处理缺失值
XgBoost把缺失值当成稀疏矩阵来对待,缺失值被分到左子树和右子树分别计算损失,选择较优的那个。如果测试数据中有缺失值,那么默认分到右子树。但是要注意,处理缺失值是需要人工来做的,XgBoost只是提供了一个默认的方法,方法的好坏,它并不负责。- XgBoost如何防止过拟合
目标函数加入正则化项,有效控制了模型复杂度;Shrinkage策略,相当于学习率;Column Subsampling(列抽样),每次使用部分特征来选择最佳切分点。- XgBoost是一种Boosting算法,它是如何做到并行训练的?
XgBoost中的并行不是指弱学习器同时训练,而是指在选择切分点是,增益计算并行
参考博客
xgboost的原理没你想像的那么难
LightGBM、XGBoost对比及存在哪些优势
XGBoost参数调优完全指南(附Python代码)
Xgboost中文文档
为什么XGBoost效果更好,速度更快