机器学习中的集成学习

一.Voting

从最简单的Voting说起,这也可以说是一种模型融合。假设对于一个二分类问题,有3个基础模型,那么就采取投票制的方法,投票多者确定为最终的分类。

二.Averaging

对于回归问题,一个简单直接的思路是取平均。稍稍改进的方法是进行加权平均。权值可以用排序的方法确定,举个例子,比如A、B、C三种基本模型,模型效果进行排名,假设排名分别是1,2,3,那么给这三个模型赋予的权值分别是3/6、2/6、1/6
这两种方法看似简单,其实后面的高级算法也可以说是基于此而产生的,Bagging或者Boosting都是一种把许多弱分类器这样融合成强分类器的思想。

三.Bagging

Bagging就是采用有放回的方式进行抽样,用抽样的样本建立子模型,对子模型进行训练,这个过程重复多次,最后进行融合。大概分为这样两步:

  • 1.重复K次
    • 有放回地重复抽样建模
    • 训练子模型
  • 2.模型融合
    • 分类问题:voting
    • 回归问题:average
      Bagging算法不用自己实现,随机森林就是基于Bagging算法的一个典型例子,采用的基分类器是决策树。
四.Boosting
  • Bagging算法可以并行处理,而Boosting的思想是一种迭代的方法,每一次训练的时候都更加关心分类错误的样例,给这些分类错误的样例增加更大的权重,下一次迭代的目标就是能够更容易辨别出上一轮分类错误的样例。最终将这些弱分类器进行加权相加。
  • 同样地,基于Boosting思想的有AdaBoost、GBDT等,在R和python也都是集成好了直接调用。
  • PS:理解了这两点,面试的时候关于Bagging、Boosting的区别就可以说上来一些,问Randomfroest和AdaBoost的区别也可以从这方面入手回答。
五.Stacking

Stacking方法其实弄懂之后应该是比Boosting要简单的,毕竟小几十行代码可以写出一个Stacking算法。我先从一种“错误”但是容易懂的Stacking方法讲起。
Stacking模型本质上是一种分层的结构,这里简单起见,只分析二级Stacking.假设我们有3个基模型M1、M2、M3。

  1. 基模型M1,对训练集train训练,然后用于预测train和test的标签列,分别是P1,T1
    ( ⋮ P 1 ⋮ ⋮ ) ( ⋮ T 1 ⋮ ⋮ ) \left(\begin{array}{c}{\vdots} \\ {P_{1}} \\ {\vdots} \\ {\vdots}\end{array}\right)\left(\begin{array}{c}{\vdots} \\ {T_{1}} \\ {\vdots} \\ {\vdots}\end{array}\right) P1T1
    对于M2和M3,重复相同的工作,这样也得到P2,T2,P3,T3。
  2. 分别把P1,P2,P3以及T1,T2,T3合并,得到一个新的训练集和测试集train2,test2.
    ( ⋮ P 1 ⋮ ⋮ ) ( ⋮ P 2 ⋮ ⋮ ) ( ⋮ P 3 ⋮ ⋮ ) ⇒ ( ⋮ ⋮ ⋮ P 1 P 2 P 3 ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ) ⏞ t r a i n 2 \left(\begin{array}{c}{\vdots} \\ {P_{1}} \\ {\vdots} \\ {\vdots}\end{array}\right)\left(\begin{array}{c}{\vdots} \\ {P_{2}} \\ {\vdots} \\ {\vdots}\end{array}\right)\left(\begin{array}{c}{\vdots} \\ {P_{3}} \\ {\vdots} \\ {\vdots}\end{array}\right)\Rightarrow \overbrace{\left(\begin{array}{ccc}{\vdots} & {\vdots} & {\vdots} \\ {P_{1}} & {P_{2}} & {P_{3}} \\ {\vdots} & {\vdots} & {\vdots} \\ {\vdots} & {\vdots} & {\vdots}\end{array}\right)}^{train2} P1P2P3P1P2P3 train2
    ( ⋮ T 1 ⋮ ⋮ ) ( ⋮ T 2 ⋮ ⋮ ) ( ⋮ T 3 ⋮ ⋮ ) ⇒ ( ⋮ ⋮ ⋮ T 1 T 2 T 3 ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ) ⏞ t e s t 2 \left(\begin{array}{c}{\vdots} \\ {T_{1}} \\ {\vdots} \\ {\vdots}\end{array}\right)\left(\begin{array}{c}{\vdots} \\ {T_{2}} \\ {\vdots} \\ {\vdots}\end{array}\right)\left(\begin{array}{c}{\vdots} \\ {T_{3}} \\ {\vdots} \\ {\vdots}\end{array}\right)\Rightarrow \overbrace{\left(\begin{array}{ccc}{\vdots} & {\vdots} & {\vdots} \\ {T_{1}} & {T_{2}} & {T_{3}} \\ {\vdots} & {\vdots} & {\vdots} \\ {\vdots} & {\vdots} & {\vdots}\end{array}\right)}^{test2} T1T2T3T1T2T3 test2

3.再用第二层的模型M4训练train2,预测test2,得到最终的标签列。
( ⋮ ⋮ ⋮ P 1 P 2 P 3 ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ) ⏞ t r a i n 2 ⟹ ⏞ train ( ⋮ ⋮ ⋮ T 1 T 2 T 3 ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ) ⏞ t e s t 2 ⟹ ⏞ predict ( ⋮ p r e d ⋮ ⋮ ) \overbrace{\left(\begin{array}{ccc}{\vdots} & {\vdots} & {\vdots} \\ {P_{1}} & {P_{2}} & {P_{3}} \\ {\vdots} & {\vdots} & {\vdots} \\ {\vdots} & {\vdots} & {\vdots}\end{array}\right)}^{train2}\overbrace{\Longrightarrow}^{\text {train}}\overbrace{\left(\begin{array}{ccc}{\vdots} & {\vdots} & {\vdots} \\ {T_{1}} & {T_{2}} & {T_{3}} \\ {\vdots} & {\vdots} & {\vdots} \\ {\vdots} & {\vdots} & {\vdots}\end{array}\right)}^{test2}\overbrace{\Longrightarrow}^{\text {predict}}\left(\begin{array}{c}{\vdots} \\ {p r e d} \\ {\vdots} \\ {\vdots}\end{array}\right) P1P2P3 train2 trainT1T2T3 test2 predictpred

Stacking本质上就是这么直接的思路,但是这样肯定是不行的,问题在于P1的得到是有问题的,用整个训练集训练的模型反过来去预测训练集的标签,毫无疑问过拟合是非常非常严重的,因此现在的问题变成了如何在解决过拟合的前提下得到P1、P2、P3,这就变成了熟悉的节奏——K折交叉验证。我们以2折交叉验证得到P1为例,假设训练集为4行3列
( a 11 a 12 a 13 a 21 a 22 a 23 a 31 a 32 a 33 a 41 a 42 a 43 ) \left(\begin{array}{lll}{a_{11}} & {a_{12}} & {a_{13}} \\ {a_{21}} & {a_{22}} & {a_{23}} \\ {a_{31}} & {a_{32}} & {a_{33}} \\ {a_{41}} & {a_{42}} & {a_{43}}\end{array}\right) a11a21a31a41a12a22a32a42a13a23a33a43
将其划分为2部分
( a 11 a 12 a 13 a 21 a 22 a 23 ) ⏞  traina  \overbrace{\left(\begin{array}{lll}{a_{11}} & {a_{12}} & {a_{13}} \\ {a_{21}} & {a_{22}} & {a_{23}}\end{array}\right)}^{\text { traina }} (a11a21a12a22a13a23)  traina 
( a 31 a 32 a 33 a 41 a 42 a 43 ) ⏞  trainb  \overbrace{\left(\begin{array}{lll}{a_{31}} & {a_{32}} & {a_{33}} \\ {a_{41}} & {a_{42}} & {a_{43}}\end{array}\right)}^{\text { trainb }} (a31a41a32a42a33a43)  trainb 
用traina训练模型M1,然后在trainb上进行预测得到preb3和pred4
( a 11 a 12 a 13 a 21 a 22 a 23 ) ⏞  traina  ⟹ ⏞ train ( a 31 a 32 a 33 a 41 a 42 a 43 ) ⏞  trainb  ⟹ ⏞ predict ( pred 3 pred 4 ) \overbrace{\left(\begin{array}{lll}{a_{11}} & {a_{12}} & {a_{13}} \\ {a_{21}} & {a_{22}} & {a_{23}}\end{array}\right)}^{\text { traina }} \overbrace{\Longrightarrow}^{\text {train}}\overbrace{\left(\begin{array}{lll}{a_{31}} & {a_{32}} & {a_{33}} \\ {a_{41}} & {a_{42}} & {a_{43}}\end{array}\right)}^{\text { trainb }}\overbrace{\Longrightarrow}^{\text {predict}}\left(\begin{array}{l}{\text {pred} 3} \\ {\text {pred} 4}\end{array}\right) (a11a21a12a22a13a23)  traina  train(a31a41a32a42a33a43)  trainb  predict(pred3pred4)
在trainb上训练模型M1,然后在traina上进行预测得到pred1和pred2
( a 31 a 32 a 33 a 41 a 42 a 43 ) ⏞  trainb  ⟹ ⏞ train ( a 11 a 12 a 13 a 21 a 22 a 23 ) ⏞  traina  ⟹ ⏞ predict ( pred 1 pred 2 ) \overbrace{\left(\begin{array}{lll}{a_{31}} & {a_{32}} & {a_{33}} \\ {a_{41}} & {a_{42}} & {a_{43}}\end{array}\right)}^{\text { trainb }} \overbrace{\Longrightarrow}^{\text {train}}\overbrace{\left(\begin{array}{lll}{a_{11}} & {a_{12}} & {a_{13}} \\ {a_{21}} & {a_{22}} & {a_{23}}\end{array}\right)}^{\text { traina }}\overbrace{\Longrightarrow}^{\text {predict}}\left(\begin{array}{l}{\text {pred} 1} \\ {\text {pred} 2}\end{array}\right) (a31a41a32a42a33a43)  trainb  train(a11a21a12a22a13a23)  traina  predict(pred1pred2)
然后把两个预测集进行拼接
( pred 1 pred 2 ) + ( pred 3 pred 4 ) = ( pred 1 pred 2 pred 3 pred 4 ) = ( ⋮ P 1 ⋮ ⋮ ⋮ ) \left(\begin{array}{c}{\text {pred} 1} \\ {\text {pred} 2}\end{array}\right)+\left(\begin{array}{c}{\text {pred} 3} \\ {\text {pred} 4}\end{array}\right)=\left(\begin{array}{l}{\text {pred} 1} \\ {\text {pred} 2} \\ {\text {pred} 3} \\ {\text {pred} 4}\end{array}\right)=\left(\begin{array}{c}{\vdots} \\ {P_{1}} \\ {\vdots} \\ {\vdots} \\ {\vdots}\end{array}\right) (pred1pred2)+(pred3pred4)=pred1pred2pred3pred4=P1
对于测试集T1的得到,有两种方法。注意到刚刚是2折交叉验证,M1相当于训练了2次,所以一种方法是每一次训练M1,可以直接对整个test进行预测,这样2折交叉验证后测试集相当于预测了2次,然后对这两列求平均得到T1。
或者直接对测试集只用M1预测一次直接得到T1。
P1、T1得到之后,P2、T2、P3、T3也就是同样的方法。理解了2折交叉验证,对于K折的情况也就理解也就非常顺利了。所以最终的代码是两层循环,第一层循环控制基模型的数目,每一个基模型要这样去得到P1,T1,第二层循环控制的是交叉验证的次数K,对每一个基模型,会训练K次最后拼接得到P1,取平均得到T1。这下再这张图片放出来就很容易看懂了。
在这里插入图片描述
该图是一个基模型得到P1和T1的过程,采用的是5折交叉验证,所以循环了5次,拼接得到P1,测试集预测了5次,取平均得到T1。而这仅仅只是第二层输入的一列/一个特征,并不是整个训练集。再分析作者的代码也就很清楚了。也就是刚刚提到的两层循环。
python实现
用了一个泰坦尼克号的尝试了一下代码,从头到尾都是可以运行的。代码放在Github,针对其中一段关键的稍作分析

def get_oof(clf, x_train, y_train, x_test):
 oof_train = np.zeros((ntrain,))  
 oof_test = np.zeros((ntest,))
 oof_test_skf = np.empty((NFOLDS, ntest))  #NFOLDS行,ntest列的二维array
 for i, (train_index, test_index) in enumerate(kf): #循环NFOLDS次
     x_tr = x_train[train_index]
     y_tr = y_train[train_index]
     x_te = x_train[test_index]
     clf.fit(x_tr, y_tr)
     oof_train[test_index] = clf.predict(x_te)
     oof_test_skf[i, :] = clf.predict(x_test)  #固定行填充,循环一次,填充一行
 oof_test[:] = oof_test_skf.mean(axis=0)  #axis=0,按列求平均,最后保留一行
 return oof_train.reshape(-1, 1), oof_test.reshape(-1, 1)  #转置,从一行变为一列

这里只实现了针对一个基模型做K折交叉验证,因为P1和T1都是多行一列的结构,这里是先存储为一行多列,最后进行转置。

六.Blending(非交叉堆叠)
  • Blending的出现是为了解决Stacking在交叉验证阶段出现的数据泄露(stage2的input会包含output的信息,具体参考为什么做stacking ensemble的时候需要固定k-fold?),容易产生过拟合,Blending直接使用不相交的数据集用于不同层的训练,通俗的理解就是不做交叉验证,而是将训练集分成3:7两个部分,70%作为训练集,对30%验证集和测试集进行预测,第二层是对30%验证集的预测结果进行训练,不存在数据泄露的问题。但是存在30%验证集数量较少,容易过拟合的问题,所以在实际融合中,使用Stacking还是Blending是有很多Trick的。
  • 下面来一个Blending的例子
    • 首先需要对原始训练数据集进行分割。一般训练集比例为60%-80%,这个比例不易太大,太大的话,容易第二层模型训练数据较少,产生过拟合。
    • 假设:原始训练数据集(D)分为训练数据集(DT)和验证数据集(DA),测试数据集为(DP)
    • 构建模型阶段:构建M个模型M1,M2,…MM,以模型Mi为例,对训练集DT进行学习,得到模型Mi。学习完毕后,对验证数据DA进行预测为DA_Mi,再对测试数据进行预测得到DP_Mi; 对验证集得到的DA_M1,DA_M2,…DA_MM, 作为新的特征。将这些新的特征作为第二层模型的输入,训练得到模型Model0,将测试集得到的DP_M1,DP_M2,…DP_MM,将这些特征作为预测数据的输入,用Model0预测,得到最终的结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值