装袋,AdaBoost和随机森林都属于组合分类方法的例子,用于改善单个分类模型的学习效果。
我们知道,在很多情况下,面对大量复杂的训练元组,如果只使用一种分类模型构造分类器,很可能对于某些元组是有“硬伤”的,预测结果很不准确。所以最直接的思路是将多种分类模型组合起来,通过得到的多个分类器投票判断。好比是有个消息你不知道,去问别人,如果只问一个人,那这个人说的也不一定对吧。但是如果问很多人,以多数人的答案为准,就不太容易出错了。
具体的组合分类方法的例子,一般我们接触较多的是三个:装袋,AdaBoost和随机森林
装袋
工作原理
装袋表示的含义是“自助聚集”,这个含义也比较形象:假设原始的训练集为 ,我们采用有放回抽样,每次从 中随机取出一个元组,经过 次有放回抽样( 也是 的大小),生成训练集 (就像把训练元祖装进一个袋中),注意这里 中的元组可能多次出现在 中,也可能不出现在 中。之后,采用事先设定好的学习方案,使用 中的训练元组生成分类器,记为 。经过 轮上述迭代,我们最终可以得到 个分类器 。那么,在对新元组(记为 )进行分类时,这 个分类器都被用来对 生成类号,根据“多数投票原则”,选择得票数最高的类作为最终分类结果。
效果分析
就准确率来说,装袋得到的组合分类器的准确率,通常显著高于直接用原始训练集 在学习模型上得到单个分类器的准确率,这是因为组合分类器降低了个体分类器的方差。而对于噪声数据和过分拟合的影响,装袋的性能也在可以接受的范围内。
AdaBoost
AdaBoost是Adaptive Boosting的简称,翻译过来就是“适应性提升”,所谓“提升”其实跟上面说的“装袋”基本思想是一致的,都是构造组合分类器,解决单个学习模型对于个别元组误判的问题。与“装袋”不同的是,“提升”的重点在于对每个训练元组赋予一个权重,每一轮迭代学习之后,更新元组的权重,最后经过多轮迭代,获得组合分类器,最后加权投票,得到预测结果。而不像装袋那样:在每轮迭代中,训练元组的权重是等大的,不用加以考虑。
AdaBoost中,元组权重迭代更新的根本目的在于使得下一轮学习过程更加“关注”上一轮迭代中,未被正确分类的元组。(装袋则不同,它的每轮迭代对于所有元组的关注度是一样的)这样做的好处其实很明显:对于单个学习模型来说,有些元组是所谓“难分类”的,我们现在使这些“难分类”的元组在下一轮学习中被“重点照顾”,这样,得到的新分类器会对“难分类”元组效果相对较好,循环往复,最终得到的组合分类器会更加均衡,它对于所有元组的分类效果能够做到都比较好。
据我所知,当前实现AdaBoost主流的方法有两种,一种是韩家炜教授《数据挖掘》中讲到的,每轮迭代采用“自助样本”(即多次有放回抽样生成的元组集合)作为训练集,利用学习方案生成分类器,其中,训练集的选择以元组权重大小为抽样概率依据;另一种是我在网上看到的大多数博客中引用的版本——李航《统计学习方法》中的方法,算法不采取抽样,而是整个训练集参与每轮迭代,每轮迭代根据元组的权重分布设计新的分类器。其实无论是哪一种方法,其基本思想都是一样的:通过迭代学习,更新元组权重,再根据元组权重,生成下一轮迭代的分类器(韩家炜的方法实际上是通过元组权重抽样得到不同的训练数据集,从而实现生成不同的分类器)。最终,得到带权重的组合分类器,加权投票得到预测的分类结果。
韩家炜教授的方法相对公式简单,也更容易理解一些,既然两种方法思想一致,也就没必要分别介绍了。本文中,我简单说明一下韩家炜的方法,至于另外的一种思路,网上有很多大神的博客讲的很详细了,也就不用我多说了。
AdaBoost的其工作原理如下面的伪代码所示:
Input: 训练元组集合D;迭代轮数k;学习方案Learning();
Output: 复合模型(组合分类器)
算法步骤:
将D中所有训练元组的权重初始化为TW_j = 1/N;
# 为不和后面的分类器权重混淆,我用TW_j指代第j个元组(Tuple)的权重;N为原始训练集D的大小
for i in [1, 2, ..., k] do
根据每个元组的权重$TW_j$抽样取得大小为N的训练集,记为D_i
# 权重越大,被抽到的概率越高,$D$中有的元组可能在D_i中出现多次,有的一次都不出现
M_i = Learning(D_i)
# 根据学习方案,使用训练集D_i,计算得到分类器M_i
根据原始训练集D,计算M_i的错误率:error(M_i) = \sum_{j = 1}^{N}TW_j * err(X_j)
# err(X_j) = 1,如果元组X_j被误分类;反之,err(X_j) = 0
if error(M_i) > 0.5
break
end if
# 说明这个生成的分类器效果太差,放弃它
for D 中每个被正确分类的元组 do
TW_j *= error(M_i) / (1 - error(M_i))
end for
# 其实是更新(减小)了每个正确分类的元组的权重,且分类器效果越好,减小幅度越大
规范化每个元组的权重
# 最简单的归一化算法即可
end for
使用组合分类器分类:
对于待检验的元组x,先假设其对于所有分类的得分都是0
# 这里有人用“权重”而不是我说的“得分”,但是我觉得算法中“权重”太多了,用“得分”可能更好理解些
for i in [1, 2, ..., k] do
MW_i = log [(1 - error(M_i)) / error(M_i)]
end for
# 符号MW_i为分类器的权重,上面一共生成了k个分类器,每个分类器均对应一个权重
return 得分最高的类 # 多数投票原则
通过对上面算法流程的学习,可以观察到,AdaBoost的核心思想在于对于每次没有正确被分类的元组,会在下一次迭代当中被“重点关注”(通过基于元组权重的有放回抽样实现)。这样可以建立出一组“互补”的分类器,使得每个元组都能被尽可能地正确分类。
最后,说一下AdaBoost的缺点。由于关注误分类元组,所以可能导致复合模型对数据过分拟合。这和装袋相比,是一个缺点。但是AdaBoost的优点也是很明显的:在有些情况下,可以获得比装袋更高的准确率。所以具体实现时,也要根据具体情况具体分析。
随机森林
两种基本模式
随机森林,本质上讲就是“决策树+装袋”,当使用决策树作为学习模型时,我们进行 轮迭代,每轮通过决策树模型,使用有放回抽样生成的“自助样本”构建分类器,这样一共可以生成 棵决策树,就好像一个森林。决策树归纳的相关知识我在之前的博文:决策树归纳中已经有了相对详细的讲解。
随机森林一般情况下有两种最常用的模型:Forest-RI(随机输入选择),Forest-RC(随机线性组合)。我下面简单谈一下。
Forest-RI(随机输入选择):每轮迭代中,构建决策树时,在每个树节点随机选择 个属性作为该节点“分裂”的候选属性。我们知道,普通的决策树归纳,在分裂节点时,应该是考虑所有属性并针对每个属性,计算用于选择的标准。比如信息增益,增益率,基尼指数等等,来找到最佳的属性,并确定分裂值。但是随即森林每次却是在可用属性中,随即选择 个属性作为候选(当然,为了保证算法是可执行的, 一般远远小于可用属性数),并且一般采用CART算法来增长树(即采用基尼指数选择分裂属性),树增长到最大规模,且不剪枝。
Forest-RC(随机线性组合):对于属性较少的训练集而言,Forest-RI就显得不可用了。解决办法是Forest-RC,即分裂节点时,首先随机选择 个属性,从 中随机选取 个数,生成 个属性的线性组合,根据这个线性组合和基尼指数找到最佳分裂点。这种随机线性组合是思想是为了降低个体分类器之间的相似度。
效果分析
- 随机森林在准确率上与AdaBoost相近,但对于错误和离群点更鲁棒;
- 随着森林中树的增加,泛化误差收敛,也能尽量避免过拟合的问题;
- 属性选择时,一般 的值为 。考虑较少属性的特点,也为随机森林应用于大型数据库提供了性能方面的优势。
此外,构造随机森林时,应尽量提高个体分类器的能力同时降低它们之间的相关性。