Wlgls 冲鸭!

机器学习(12)-Adaboost

2020-04-13
ML

Ensemeble(集成学习)

在前面的机器学习算法中,我们是在无数模型中选择最优解,尤其是交叉验证更是帮助我们进一步选择好的结果。但是如果有多个表现不好的模型将他们聚合在一起进行投票,我们仍然可以得到一个好的拟合结果,也就是三个臭皮匠顶一个诸葛亮。而这种聚集的方法也被称为集成学习。

Blending

集成学习的融合方法有以下方式, 主要是投票(Voting),线性混合(Linear Blending)等, 其数学表达式为:

  • $G(x) = sign(\sum_{t=1}^{T}1*g_t(x))$

    在这种情况下,我们相当于每个模型投一票,最终将投票次数最多的设为最终结果

  • $G(x) = sign(\sum_{t=1}^{T}\alpha_tg_t(x))$

    加权重的投票,这种情况可以类比到现实世界,比如做股票预测,有些人比较擅长进行股票预测,那么我们就应该更多的听从他的建议给他分配更多的权重

  • $G(x) = sign(\sum_{t=1}^{T}q_t(x)p_t(x)$

    这种情况比较复杂,实际上我们的样本也是不同的,仍然以现实中股票预测为例,我们的样本中包含服务行业的股票,也包含传统行业的股票,对于不同的人他们对不同产业的认知不同,所以当我们预测服务行业的时候,就给精通服务行业的人较大权重,对预测传统行业也是这样。

上面三种聚合方式有很明显的包含关系,即上面的表达情况实际上是下面的特例。

为什么Aggregation会工作的比较好,从以下两种情况来分析。

  • 情况一,如下图

    image-20200413143358609

    对于上面的样本,简单的线性回归其实效果并不会太好(其实还好啦,只是举例),为了更好的拟合它,按照以往的经验,我们需要进行一定的特征转换(feature transform)。

    但是如果使用Aggregation,我们可以将简单的分类器拟合在一起,比如上图的分类器我们是通过简单的水平或者垂直的线进行分类,然后进行拟合,最终得到了一个比较好的结果。也就是说,Aggregation提高了预测模型的power,起到了feature transform的效果。

  • 情况二,在使用PLA的时候,供我们选择的线是非常多的

    image-20200413144530708

    但是最理想的结果是最中间的黑线,这也是我们使用SVM的目标。但是如果我们只用Aggregation将所有的分类边界组合,我们会得到中间的那条线,这比SVM的计算要简单的多。而这种结果从实际上讲也是正则化(regularization)的效果。

综上Aggregation具有非常好的效果,他可以起到feature transform和regularization的效果。

Uniform Blending

假设我们已经有了一些性能比较好的$g_t$,然后我们将其整合合并,这个过程就被称为blending。

最简单的一种Blending是uniform blending

对于分类问题classification而言:

\[G(x) = sign(\sum_{t=1}^{T}1*g_t(x))\]

然后我们选择投票数最多的为我们的最终结果。

对于回归问题Regression,我们的表达式为:

\[G(x) = \frac{1}{T}\sum_{t=1}^{T}g_t(x)\]

也就是说取平均结果,从数据上看,我们去平均值的操作要比单一的$g_t$的效果要好,其推导如下:

image-20200413152736057

可见$avg((g_t(x)-f(x)^2)$与$(G-f)^2$相差一个$avg((g_t-G)^2)$,所所以$g_t$与目标函数的差距比G与f的差值大。

从期望的角度来看,也可以看出G的效果比较好:

\[avg(E_{out}(g_t)) =ave(\varepsilon(g_t-G)^2)+E_{out}(G)\ge E_{out}(G)\]

Linear Blending

如果使用Linear blending,我们为不同$g_t$赋予不同的权重$\alpha_t$,其中$\alpha_t>0$,那么我们最终的结果就是:

\[G(x) = sign(\sum_{t=1}^{T}\alpha_t*g_t(x))\ with \alpha_t\ge 0\]

对于确定$\alpha_t$的值,实际上也是一种误差最小化的思想,即$\min\limits_\alpha E_{in}(\alpha)$。其中$E_{in}$为:

\[E_{in}(\alpha) = \frac{1}{N}\sum_{n=1}^{N}(y_n-\sum_{t=1}^{T}\alpha_tg_t(x_n))^2\]

如果我们将$g_t(x)$看成一种feature transform,即:

\[\Phi(x_n) = \{g_1(x_n), g_2(x_n), g_3(x_n), g_4(x_n), ...., g_T(x_n) \}\]

我们完全可以看成是一种是一种线性拟合,我们可以使用带有条件的线性回归就可以得到我们的$\alpha$。

\[E_{in}(\alpha) = \frac{1}{N}\sum_{n=1}^{N}(y_n-\sum_{t=1}^{T}\alpha_t\Phi_t(x_n))^2 \ with\ \alpha_t>0\]

现在谈一下$\alpha>0$的条件,因为当我们的$g_t$越正确我们的$\alpha_t$越大,实际上如果在拟合过程中发生了使$\alpha_t<0$的情况,那显然是因为$g_t(x)$实在过于糟糕。

也就是说如果$g_t(x)$过于糟糕,比如$E_{in}(g_t)=99\%$, 错误率达到99%,而对于这种情况我们也许会得到一个$\alpha_t<0$。这实际上是对$g_t$的反向利用,即$g_t$认为是正确的,我们就认为是错误的。显然,反向的$g_t$是一个不错的结果。$\alpha_t<0 \Rightarrow \alpha_tg_t(x_t) = \vert \alpha_t\vert(-g_t(x))$

所以,实际上$\alpha_t>0$的条件是无关紧要的,我们完全可以使用线性回归来寻找我们的$\alpha$。

此外,一个更有效的改进方法是在线性拟合获得$\alpha$时,使用验证集而不是训练集。我们在获取$g_t$时一般都使用了validation。所以我们可以通过$D_{train}$来获得我们的$g^{-}(x)$,然后在训练$\alpha$的时候使用$D_{val}$,这会使我们得到一个更好的结果。

元算法

在集成学习中我们需要生成元算法,即弱分类器,然后可以将其进行混合,对此主要有两种方式:

  • bagging: 元学习器之间不存在依赖关系,而是使用样本随机化来生成元学习器, 比如随机森林
  • Boosting: 元学习器存在依赖关系,我们是基于前面模型的训练结果生成新的模型,比如串行化生成。比如Adaboost, GBDT等

Bagging

现在需要探讨如何获得$g_t$,一般我们有四种方式:

  • 不同的模型
  • 不同的参数
  • 算法的随机性(eg. PLA的起点改变会得到不同的结果)
  • 不同的随机数据

此外,不同的$g_t$最好是使用不同的数据集,但是实际情况中,我们往往只有一份数据集,为了从这一份数据集中模拟新数据,使用了一种方法叫bootstrapping。思想就是从已有数据集模拟出其他类似的样本$D_t$。

bootstrapping的做法就是从已有的N笔资料,先从中选出一个样本,再放回去,再选择一个样本,再放回去,重复M次后,会得到一份新的M笔资料$\check{D}_t$。我们就可以使用这份资料拟合出一个$g_t$,然后在进行aggregation。利用bootstrap进行aggregation的操作就被称为bagging。

算法过程

for t = 1….T: 1. 使用bootstrapping从原始数据获得M笔资料$D_t$ 2. 使用演算法$A(D_t)$求出$g_t$ G = aggregation(${g_t}$)

Boosting

Adaboost

使用bootstrap可以获得$g_t$,然后使用线性回归拟合获得$\alpha_t$进行合并,实际上有更加简单的方法,这种方法在获得$g_t$的同时就可以直接求得$\alpha_t$。

从Bootstrap开始说起,我们在使用Bootstrap是有放回的,也就是说我们的样本有可能被重复抽取。

假设我们有四个样本${(x_1, y_1),(x_2, y_2), (x_3, y_3), (x_4, y_4)}$。

其中一次的抽取结果为${(x_1, y_1),(x_1, y_1), (x_3, y_3), (x_4, y_4)}$。那么我们的目标函数为:

\[\min\ E_{in}(g_t) = \frac{1}{4}\sum_{n=1}^4u_nerr(y_n, g_t(x_n))\\ u_n = \{2, 0, 1, 1\}\]

也就是说我们为每一个样本分配了权重,但是这实际上和原本的$E_{in}$没有任何区别,这种方法被称作Weighted Base Algorithm。

所以我们的$g_t$的求解方法如下:

\[g_t \leftarrow \mathop{\arg\min}_{h\in H}(\sum_{n=1}^{N}u_n^t[[y_n\neq h(x_n)]])\]

我们该如何确定我们的$u_n$。因为在使用aggregation时,我们应当尽量是选择不同的$g_t$,$g_t$和$g_{t+1}$越不同,我们最终组合的效果会越好。因为如果大家都是同样的结果,即相同的$g_t$,实际上无论我们如何组合都只会跟$g_t$一致,就失去了聚合的意义。

因为$g_t$和$g_{t+1}$的求解方式如下:

\[g_t \leftarrow \mathop{\arg\min}_{h\in H}(\sum_{n=1}^{N}u_n^t[[y_n\neq g_t(x_n)]])\\ g_{t+1} \leftarrow \mathop{\arg\min}_{h\in H}(\sum_{n=1}^{N}u_n^{(t+1)}[[y_n\neq g_{t+1}(x_n)]])\]

显然$g_t$由$u_n^t$决定,$g_{t+1}$由$u_n^{t+1}$决定,如果$g_t$和$g_{t+1}$相似,则两者在$u_n^{t+1}$上应当表现的结果是一样的,所以如果我们能够定义$u_n^{t+1}$使$g_t$在其身上表现的越差越好,则$g_t$和$g_{t+1}$就会非常不同。

一般是在$g_t$的作用下,$u_n^{t+1}$的表现(即err)近似为0.5,表明$g_t$的预测基本没什么用,就跟抛硬币一样是随机的。使用表达式为:

\[\frac{\sum_{n=1}^Nu_n^{t+1}[[y_n\neq g_t(x_n)]]}{\sum_{n=1}^Nu_n^{t+1}} = \frac{1}{2}\]

为了进一步简化计算,我们定义了:

image-20200413221958026

为了使分式为0.5, 我们只需要将犯错的和没有犯错的数量相等就可以了,这就是我们求解$u_n^{t+1}$的思想。

我们是已知$u_n^t$的,假设我们的$u_n^t \text{of incorrect}$为1126, $u_n^t$ of correct为6211,为了是$u_n^{t+1}$中的错误比例正好为0.5, 那么我们的做法是:

  • for incorrect $u_n^{t+1}$:

    \[\sum_{n=1}^Nu_n^{t+1} = \sum_{n=1}^{N}u_n^t * 6211 \Rightarrow u_n^{t+1} = u_n^{t}*6211\]
  • for correct $u_n^{t+1}$

    \[\sum_{n=1}^Nu_n^{t+1} = \sum_{n=1}^{N}u_n^t * 1126 \Rightarrow u_n^{t+1} = u_n^{t}*1126\]

更流行的做法是使用犯错的比率,假设犯错误率为$\epsilon_t$,那么求解$u_n^{t+1}$的时候,$u_t^{t}$分别乘以$1-\epsilon_t$和$\epsilon_t$。

为了进一步简化,我们提出新的尺度因子: \(\Diamond t = \sqrt{\frac{1-\epsilon_t}{\epsilon_t}}\)

对于新的尺度因子,对于错误的$u_n^t$,将它乘以$\Diamond t$, 对于正确的$u_n^t$,将它除以$\Diamond t$。这种操作与前面并没有太大的区别。因为如果$\epsilon_t<0.5$, 那么$\Diamond t>1$,错误的点乘以他,则相当于将错误的点放大,而正确的点相除则是将正确的点缩小。最终仍能得到$g_t$与$g_{t+1}$不同的结果。

image-20200413223838600

这样,我们解决了如何从$u_n^t$得到$u_n^{t+1}$的过程,如果我们只需要定义一个初始的$u_n^1$,我们就可以得到不同的$g_t$进行合并。

对于初始的$u_n^1$,我们一般定义为$u_n^1=\frac{1}{N}$,之后逐次迭代更新。

而合并我们通常是使用线性组合,他可以表现的更好,在前面,我们是通过将$g_t$看作是特征转换进行线性拟合,而Adaboost算法提供了一种更巧妙的算法可以使得每次计算$g_t$的时候同时计算出$\alpha_t$。

我们知道$\alpha_t$和错误率有很大的关系,错误越多则分配的权重越小,因为$\epsilon_t$越小,$\Diamond t$越大,所以,我们可以构造出:

\[\alpha_t = ln(\Diamond t)\]

这样取值是有物理意义的,例如当$\epsilon_t=\frac12$时,error很大,此时对应的$\Diamond t=1$,$\alpha_t=0$,即此$g_t$对G没有什么贡献,权重设为零。而当$\epsilon_t=0$时,没有error,表示该$g_t$预测非常准,此时$\Diamond t=\infty$,$\alpha_t=\infty$,即$g_t$对G贡献非常大,权重应该设为无穷大。 综上我们总结出了Adaboost(Adaptive Boosting)的整个算法流程,他有三部分组成:

image-20200413225312180

算法过程

最终我们的算法过程为:

img

需要注意的是在选择学习算法时,一般选择比较弱的学习算法,一个简单的例子就是Decision Stump,即每次只是从一个维度进行分割。简单的说就是先选择一个维度,然后随机切上一刀。

注意:

有因为对于分类正确的点有$y_ng_t(x_n)=1$, 对于分类错误的点$y_ng_t(x_n)=-1$, 而$\alpha_t = ln(\Diamond t)\leftrightarrow\Diamond t = exp(\alpha_t)$

所以我们更新$u_n^t$的时候具体的更新为:

\[\text{for incorrect:} \ \ u_n^{t+1} = {u_n^{t}*exp(\alpha_t)}\\ \text{for correct:} \ \ u_n^{t+1} = {u_n^{t}*exp(-\alpha_t)}\\\]

\[u_n^{t+1} = {u_n^t\exp(-y_n\alpha_t g_t(x_n))}\]

所以算法步骤为(计算公式同上):

u = [1\N, 1\N, …, 1/N] for t = 1, 2, 3, 4….. T

  1. 计算gt
  2. 计算错误率$\epsilon$, 然后计算$\alpha$
  3. 更新新的权重,$u^{t+1}$

return G(x)

理论支撑

根据前面的VC Bound角度来看,Adaboost满足:

img

对于这个VC Bound,其中第一项,如果满足$\epsilon_t \le \epsilon< 0.5$ , 则经过$T = O(logN)$次迭代后,$E_{in}$可以降到很低,而当N很大时,第二项也可以很小,所以$E_{out}$被限定在一个有限的上届。这正是Adaboost的精髓之处,只要我们的$g_t$比乱猜好一点点($\epsilon_t \le \epsilon< 0.5$ ),那么经过迭代后,我们会得到一个很强的G。

参考

机器学习技法-coursera

RedstoneWill的github笔记