Boosting的思路与Adaboost算法
Boosting的思路:
首先我们来介绍一下强可学习和弱可学习的概念:
- 弱可学习:一个概念如果存在一个多项式算法能够学习它,且准确率仅比随机猜测高一点,那我们称这个概念是弱可学习的。
- 强可学习:一个概念如果存在一个多项式算法能够学习它,且准确率很高,那我们称这个概念是强可学习的。
在PAC(probably approximately correct)学习的框架下,强可学习和弱可学习是等价的,即一个概念是强可学习的充分必要条件是这个概念是弱可学习的。那么问题来了,在学习中,我们已经发现了弱可学习算法,那么我们能否将其升级为强可学习算法呢?那么对于一个概念,弱可学习算法是很容易实现的。
什么是Bossting:
Boosting,也称为增强学习或提升法,是一种重要的集成学习技术,Boosting是组合多个弱学习器成为一个强可学习器,早期的boosting算法尝试通过突出错分样本的方法,提高学习器准确度,其主要过程如下:
- 从样本集D中,不放回的随机抽样 n 1 < n n_1 < n n1<n 个样本,得到集合 D 1 D_1 D1,训练弱学习器 C 1 C_1 C1。
- 从样本集D中,抽取 n 2 < n n_2 < n n2<n 个样本,其中合并进一半被 C 1 C1 C1 分类错误的样本。得到样本集 D 2 D_2 D2,训练弱学习器 C 2 C_2 C2。
- 抽取D样本集, C 1 C_1 C1 和 C 2 C_2 C2 分类不一致样本,组成 D 3 D_3 D3,训练弱学习器 C 3 C_3 C3。
- 用三个弱学习器做加权和,得到最终的强学习器。
通过重复抽样的方法略显粗糙,且存在较大的随机性和不稳定性,为此提出了AdaBoost算法。
AdaBoost算法
AdaBoost,是英文”Adaptive Boosting“(自适应增强)的缩写。
算法原理:
通过调整样本权重和弱分类器权值,从训练出的弱分类器中筛选出权值系数最小的弱分类器组合成一个最终强分类器。基于训练集训练弱分类器,每次下一个弱分类器都是在样本的不同权值集上训练获得的。每个样本被分类的难易度决定权重,而分类的难易度是经过前面步骤中的分类器的输出估计得到的。
算法流程:
假设给定一个数据集:
T
=
{
(
X
1
,
Y
1
)
,
(
X
2
,
Y
2
)
.
.
.
.
.
.
(
X
m
,
Y
m
)
}
T = \left\{(X_1, Y_1),(X_2, Y_2) . . . . . . (X_m, Y_m)\right\}
T={(X1,Y1),(X2,Y2)......(Xm,Ym)}其中每个样本点由特征与类别组成。特征
x
i
∈
X
⊆
R
n
x_{i} \in \mathcal{X} \subseteq \mathbf{R}^{n}
xi∈X⊆Rn,类别
y
i
∈
Y
=
{
−
1
,
+
1
}
y_{i} \in \mathcal{Y}=\{-1,+1\}
yi∈Y={−1,+1} ,
X
X
X 是特征空间,
Y
Y
Y 是类别集合,输出最终分类器
H
(
X
)
H(X)
H(X)。Adaboost算法如下:
- 初始化训练数据的分布: D 1 ( i ) = 1 / m D_1(i) = 1/m D1(i)=1/m
- 对于
t
=
1
,
2......
T
t = 1,2 ......T
t=1,2......T:
- 使用分布 D t D_t Dt 训练弱学习器,得到弱学习器: h t : X → { − 1 , + 1 } h_t: \mathcal{X} \rightarrow\{-1,+1\} ht:X→{−1,+1}
- 计算 h t h_t ht 在训练集上的误差率: e t = ∑ i = 1 m P ( h t ( x i ) ≠ y i ) = ∑ i = 1 m w t i I ( h t ( x i ) ≠ y i ) e_t=\sum_{i=1}^m P\left(h_t\left(x_{i}\right) \neq y_{i}\right)=\sum_{i=1}^m w_{t i} I\left(h_t\left(x_i\right) \neq y_i\right) et=∑i=1mP(ht(xi)=yi)=∑i=1mwtiI(ht(xi)=yi)
- 计算 h t h_t ht 的系数: a t = 1 2 ln ( 1 − e t e t ) a_t = \frac{1}{2} \ln( \frac{1-e_t}{e_t}) at=21ln(et1−et)
- 更新训练数据集的权重分布:
D
t
+
1
(
i
)
=
D
t
(
i
)
Z
t
×
{
e
−
a
i
i
f
h
t
(
x
i
)
=
y
i
e
a
i
i
f
h
t
(
x
i
)
≠
y
i
=
D
t
(
i
)
e
x
p
(
−
a
t
y
i
h
t
(
x
i
)
)
Z
t
\begin{aligned} D_{t+1}(i) &=\operatorname {\frac{D_t(i)}{Z_t} \times \{\begin{array}{ll}e^{-a_i} & if&{h_t(x_i)= {y_i}} \\ e^{a_i} & if&{h_t(x_i)\neq {y_i}}\end{array}} \\ &=\operatorname \frac{D_t(i)exp(-a_ty_ih_t(x_i))}{Z_t} \end{aligned}
Dt+1(i)=ZtDt(i)×{e−aieaiififht(xi)=yiht(xi)=yi=ZtDt(i)exp(−atyiht(xi))
这里 Z t Z_t Zt 是规一化因子,使得 D t D_t Dt 称为概率分布, Z t = ∑ i = 1 m w t i exp ( − a t y i h t ( x i ) ) Z_t=\sum_{i=1}^{m} w_{t i} \exp(-a_ty_ih_t(x_i)) Zt=∑i=1mwtiexp(−atyiht(xi))
- 构建基本学习器的线性组合
f
(
x
)
=
∑
t
=
1
T
α
t
h
t
f(x)=\sum_{t=1}^{T} \alpha_{t} h_t
f(x)=∑t=1Tαtht,得到最终的强学习器
H ( x ) = sign ( f ( x ) ) = sign ( ∑ t = 1 T α t h t ( x ) ) \begin{aligned} H(x) &=\operatorname{sign}(f(x)) \\ &=\operatorname{sign}\left(\sum_{t=1}^{T} \alpha_{t} h_t(x)\right) \end{aligned} H(x)=sign(f(x))=sign(t=1∑Tαtht(x))
- 为什么每次迭代要扩大错误分类样本的权重呢?
最后的表达式为: H ( x ) = s i g n ( ∑ t = 1 T α t h t ( x ) ) H(x) = {sign}\left(\sum_{t=1}^{T} \alpha_{t} h_t(x)\right) H(x)=sign(∑t=1Tαtht(x)),这里 a a a 表示的权值是由 a t = 1 2 ln ( 1 − e t e t ) a_t = \frac{1}{2} \ln( \frac{1-e_t}{e_t}) at=21ln(et1−et)得到的,而 a a a 是关于误差的表达式,到这里我们就明白了,提高错误分类样本的权值,当下一次学习器再次分类错误这些样本之后,会提高整体的错误率,这样就导致 a a a 变的很小,最终就会导致这个学习器在整个混合学习器的权值变低,也就是说,AdaBoost算法让优秀的学习器占整体的权值更高,差的学习器占整体的权值更低。
使用sklearn对Adaboost算法进行建模:
# 引入数据科学相关工具包:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use("ggplot")
%matplotlib inline
import seaborn as sns
wine = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data",header=None)
wine.columns = ['Class label', 'Alcohol', 'Malic acid', 'Ash', 'Alcalinity of ash','Magnesium', 'Total phenols','Flavanoids', 'Nonflavanoid phenols',
'Proanthocyanins','Color intensity', 'Hue','OD280/OD315 of diluted wines','Proline']
# 数据查看:
print("Class labels",np.unique(wine["Class label"]))
wine.head()
# 数据预处理
# 仅仅考虑2,3类葡萄酒,去除1类
wine = wine[wine['Class label'] != 1]
y = wine['Class label'].values
X = wine[['Alcohol','OD280/OD315 of diluted wines']].values
# 将分类标签变成二进制编码:
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
y = le.fit_transform(y)
# 按8:2分割训练集和测试集
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=1,stratify=y) # stratify参数代表了按照y的类别等比例抽样
# 使用单一决策树建模
from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier(criterion='entropy',random_state=1,max_depth=1)
from sklearn.metrics import accuracy_score
tree = tree.fit(X_train,y_train)
y_train_pred = tree.predict(X_train)
y_test_pred = tree.predict(X_test)
tree_train = accuracy_score(y_train,y_train_pred)
tree_test = accuracy_score(y_test,y_test_pred)
print('Decision tree train/test accuracies %.3f/%.3f' % (tree_train,tree_test))
# 使用sklearn实现Adaboost(基分类器为决策树)
'''
AdaBoostClassifier相关参数:
base_estimator:基本分类器,默认为DecisionTreeClassifier(max_depth=1)
n_estimators:终止迭代的次数
learning_rate:学习率
algorithm:训练的相关算法,{'SAMME','SAMME.R'},默认='SAMME.R'
random_state:随机种子
'''
from sklearn.ensemble import AdaBoostClassifier
ada = AdaBoostClassifier(base_estimator=tree,n_estimators=500,learning_rate=0.1,random_state=1)
ada = ada.fit(X_train,y_train)
y_train_pred = ada.predict(X_train)
y_test_pred = ada.predict(X_test)
ada_train = accuracy_score(y_train,y_train_pred)
ada_test = accuracy_score(y_test,y_test_pred)
print('Adaboost train/test accuracies %.3f/%.3f' % (ada_train,ada_test))
得到输出:
Class labels [1 2 3]
Decision tree train/test accuracies 0.916/0.875
Adaboost train/test accuracies 1.000/0.917
结果分析:单层决策树似乎对训练数据欠拟合,而Adaboost模型正确地预测了训练数据的所有分类标签,而且与单层决策树相比,Adaboost的测试性能也略有提高。然而,为什么模型在训练集和测试集的性能相差这么大呢?我们使用图像来简单说明下这个道理!
# 画出单层决策树与Adaboost的决策边界:
x_min = X_train[:, 0].min() - 1
x_max = X_train[:, 0].max() + 1
y_min = X_train[:, 1].min() - 1
y_max = X_train[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1),np.arange(y_min, y_max, 0.1))
f, axarr = plt.subplots(nrows=1, ncols=2,sharex='col',sharey='row',figsize=(12, 6))
for idx, clf, tt in zip([0, 1],[tree, ada],['Decision tree', 'Adaboost']):
clf.fit(X_train, y_train)
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
axarr[idx].contourf(xx, yy, Z, alpha=0.3)
axarr[idx].scatter(X_train[y_train==0, 0],X_train[y_train==0, 1],c='blue', marker='^')
axarr[idx].scatter(X_train[y_train==1, 0],X_train[y_train==1, 1],c='red', marker='o')
axarr[idx].set_title(tt)
axarr[0].set_ylabel('Alcohol', fontsize=12)
plt.tight_layout()
plt.text(0, -0.2,s='OD280/OD315 of diluted wines',ha='center',va='center',fontsize=12,transform=axarr[1].transAxes)
plt.show()
从上面的决策边界图可以看到:Adaboost模型的决策边界比单层决策树的决策边界要复杂的多。也就是说,Adaboost试图用增加模型复杂度而降低偏差的方式去减少总误差,但是过程中引入了方差,可能出现国拟合,因此在训练集和测试集之间的性能存在较大的差距,这就简单地回答的刚刚问题。值的注意的是:与单个分类器相比,Adaboost等Boosting模型增加了计算的复杂度,在实践中需要仔细思考是否愿意为预测性能的相对改善而增加计算成本,而且Boosting方式无法做到现在流行的并行计算的方式进行训练,因为每一步迭代都要基于上一部的基本分类器。