1 AdaBoost原理
AdaBoost算法是一种Boosting算法,它在解决Boosting算法提出的两个问题时,给出的答案是:1、(改变数据分布的问题) 提高那些在前一轮分类器中被错误分类的样本的权重,减小那些被正确分类样本的权重,这样那些被分类错误的样本在后续的训练中得到了更大的关注;2、(组合各个弱分类器策略的问题) 各个弱分类器的组合是通过加权表决进行组合的,具体来说就是加大分类错误率低的弱分类器的权重,减小分类错误率高的弱分类器的权重,这样那些表现好的分类器就能在最终的表决结果中起到更大的作用。
1.1 算法流程
下面我们结合《李航统计学习》和《机器学习(西瓜书)》来了解一下AdaBoost的算法流程。该流程基于“加性模型”,即基学习器的线性组合:
H
(
x
)
=
∑
t
=
1
T
α
t
h
t
(
x
)
H(\bm{x})=\sum_{t=1}^T \alpha_th_t(x)
H(x)=t=1∑Tαtht(x)
采用最小化指数损失函数的优化方法:
l
e
x
p
(
H
∣
D
)
=
E
x
∼
D
[
e
−
f
(
x
)
H
(
x
)
]
=
{
E
x
∼
D
[
e
−
1
]
,
H
(
x
)
=
f
(
x
)
E
x
∼
D
[
e
1
]
,
H
(
x
)
≠
f
(
x
)
l_{exp}(\bm{H|D})=\mathbb{E}_{\bm{x \sim D}}[e^{-f(x)H(\bm{x})}] = \left\{\begin{array}{ll} \mathbb{E}_{\bm{x \sim D}}[e^{-1}] , & H\left(x\right)=f(x) \\ \mathbb{E}_{\bm{x \sim D}}[e^{1}] , & H\left(x\right) \neq f(x) \end{array}\right.
lexp(H∣D)=Ex∼D[e−f(x)H(x)]={Ex∼D[e−1],Ex∼D[e1],H(x)=f(x)H(x)=f(x)
给定训练样本
D
=
{
(
x
1
,
y
1
)
,
x
2
,
y
2
)
,
…
,
(
x
N
,
y
N
)
}
D=\{(x_1,y_1),x_2,y_2),\dots,(x_N,y_N)\}
D={(x1,y1),x2,y2),…,(xN,yN)},其中
x
i
∈
x
⊆
R
d
x_i \in \bm{x}\subseteq \bm{R}^d
xi∈x⊆Rd,
y
i
∈
y
=
{
−
1
,
1
}
y_i \in \bm{y}=\{-1,1\}
yi∈y={−1,1},基本分类器为
h
t
(
x
)
h_t(x)
ht(x),设置分类器个数为
T
T
T个,最终分类器为
H
(
x
)
H(\bm{x})
H(x)。
AdaBoost算法流程
(1)初始化训练数据分布
D
1
=
(
w
1
,
1
,
w
1
,
2
,
…
,
w
1
,
i
,
…
,
w
1
,
N
)
D_1=(w_{1,1},w_{1,2},\dots,w_{1,i},\dots,w_{1,N})
D1=(w1,1,w1,2,…,w1,i,…,w1,N),
w
1
i
=
1
N
,
i
=
1
,
2
,
…
,
N
w_{1i} = \frac{1}{N},\;i=1,2,\dots,N
w1i=N1,i=1,2,…,N
(2)对于基分类器
h
t
(
x
)
,
t
∈
{
1
,
2
,
…
,
T
}
h_t(x),t\in \{1,2,\dots,T\}
ht(x),t∈{1,2,…,T}
\qquad
1)使用具有权值分布
D
t
D_t
Dt的训练数据进行学习,得到基本分类器
h
t
(
x
)
h_t(x)
ht(x);
\qquad
2)计算
h
t
(
x
)
h_t(x)
ht(x)在训练集上的分类误差率
e
t
=
∑
i
=
1
N
P
(
h
t
(
x
i
)
≠
y
i
)
=
∑
i
=
1
N
w
t
i
I
(
h
t
(
x
i
)
≠
y
i
)
e_t=\sum_{i=1}^NP(h_t(x_i)\neq y_i)=\sum_{i=1}^N w_{ti}I(h_t(x_i)\neq y_i)
et=∑i=1NP(ht(xi)=yi)=∑i=1NwtiI(ht(xi)=yi);
\qquad
3)计算
h
t
(
x
)
h_t(x)
ht(x)的系数
α
t
=
1
2
l
o
g
1
−
e
t
e
t
\alpha_t=\frac{1}{2}log\frac{1-e_t}{e_t}
αt=21loget1−et,这里
l
o
g
log
log是自然对数
l
n
ln
ln;
\qquad
4)更新训练数据集的权重分布(改变数据分布,解决第一个问题):
D
t
+
1
=
{
w
t
+
1
,
1
,
w
t
+
1
,
2
,
…
,
w
t
+
1
,
i
,
…
,
w
t
+
1
,
N
}
=
D
t
Z
t
e
x
p
(
−
α
t
f
(
x
i
)
h
t
(
x
i
)
)
=
D
t
Z
t
×
{
e
−
α
t
,
h
t
(
x
i
)
=
f
(
x
i
)
e
α
t
,
h
t
(
x
i
)
≠
f
(
x
i
)
D_{t+1}=\{w_{t+1,1},w_{t+1,2},\dots,w_{t+1,i},\dots,w_{t+1,N}\}\\=\frac{D_t}{Z_t}exp(-\alpha_tf(x_i)h_t(x_i))=\frac{D_t}{Z_t}\times\left\{\begin{array}{ll} e^{-\alpha_t} , & h_t\left(x_i\right)=f(x_i) \\ e^{\alpha_t}, & h_t\left(x_i\right) \neq f(x_i) \end{array}\right.
Dt+1={wt+1,1,wt+1,2,…,wt+1,i,…,wt+1,N}=ZtDtexp(−αtf(xi)ht(xi))=ZtDt×{e−αt,eαt,ht(xi)=f(xi)ht(xi)=f(xi)
w
t
+
1
,
i
=
w
t
,
i
Z
t
exp
(
−
α
t
f
(
x
i
)
h
t
(
x
i
)
)
,
i
=
1
,
2
,
…
,
N
Z
t
=
∑
i
=
1
N
w
t
,
i
e
x
p
(
−
α
t
f
(
x
i
)
h
t
(
x
i
)
)
w_{t+1,i}= \frac{w_{t,i}}{Z_t}\exp(-\alpha_tf(x_i)h_t(x_i)) , i=1,2,\dots,N \\Z_t=\sum_{i=1}^Nw_{t,i}exp(-\alpha_tf(x_i)h_t(x_i))
wt+1,i=Ztwt,iexp(−αtf(xi)ht(xi)),i=1,2,…,NZt=i=1∑Nwt,iexp(−αtf(xi)ht(xi))
这里
Z
t
Z_t
Zt是规范化因子,,使得更新后的权值
D
t
+
1
D_{t+1}
Dt+1符合概率分布。
(3)构建基本分类器的线性组合(组合弱分类器,解决第二个问题)
H
(
x
)
=
∑
t
=
1
T
α
t
h
t
(
x
)
H(\bm{x})=\sum_{t=1}^T \alpha_th_t(x)
H(x)=t=1∑Tαtht(x)
步骤(1)中,假设数据的权值分布是均匀的,使得第一次在没有先验知识的条件下,所有样本在基本分类器的学习中的作用是相同的。
步骤(2)中,每一步迭代的基分类器
h
t
(
x
)
h_t(x)
ht(x)的分类错误率
e
t
e_t
et代表了
h
t
(
x
)
h_t(x)
ht(x)中分类错误的样本权重和,这点直接说明了权重分布
D
t
D_t
Dt与
h
t
(
x
)
h_t(x)
ht(x)的分类错误率
e
t
e_t
et有直接关系。基本分类器的系数
α
t
\alpha_t
αt表示当前基分类器
h
t
(
x
)
h_t(x)
ht(x)在最终分类器
H
(
x
)
H(\bm{x})
H(x)的重要性程度,其取值与基分类器的分类误差率直接相关,且呈负相关,当
e
t
⩽
1
2
e_t\leqslant \frac{1}{2}
et⩽21时,
α
t
⩾
0
\alpha_t\geqslant 0
αt⩾0。因此基分类器的权值在分类错误率越大时越小,注意
α
t
\alpha_t
αt之和不为1;在样本权值更新时,分类错误率越大,权值越变大,
w
t
,
i
w_{t,i}
wt,i之和为1。
1.2 AdaBoost中的前向分步算法思想
在AdaBoost中,我们将单个基学习器组合成一个复杂分类器采用的是加权和的方式,如果仅仅是对于一个加法模型
f
(
x
)
=
∑
m
=
1
M
β
m
b
(
x
;
γ
m
)
f(x)=\sum_{m=1}^{M} \beta_{m} b\left(x ; \gamma_{m}\right)
f(x)=∑m=1Mβmb(x;γm),
b
(
x
;
γ
m
)
b\left(x ; \gamma_{m}\right)
b(x;γm)为基分类器,那么在给定训练集和损失函数
L
(
y
,
f
(
x
)
)
L(y, f(x))
L(y,f(x))的情况下,要想训练这个复杂模型,求解全部参数,就需要
min
β
m
,
γ
m
∑
i
=
1
N
L
(
y
i
,
∑
m
=
1
M
β
m
b
(
x
i
;
γ
m
)
)
\min _{\beta_{m}, \gamma_{m}} \sum_{i=1}^{N} L\left(y_{i}, \sum_{m=1}^{M} \beta_{m} b\left(x_{i} ; \gamma_{m}\right)\right)
βm,γmmini=1∑NL(yi,m=1∑Mβmb(xi;γm))
而这是一个复杂的优化问题,不能简单的通过凸优化进行学习优化。那么此时就可以使用前向分步算法思想,我们不在构建好加法模型后再优化求解,而是在对于这个加法模型,从前向后,每一步只优化一个基函数及其系数,逐步逼近目标函数,那么就可以降低优化的复杂度。
2 代码实践
利用sklear相关包进行AdaBoost实践。
# 导入必要库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use("ggplot")
%matplotlib inline
import seaborn as sns
我们使用uci的红酒数据集(wine)
# 加载训练数据:
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()
wine.info()
我们进行二分类,因此选择2、3类,暂不考虑1类
# 数据预处理
# 仅仅考虑2,3类葡萄酒,去除1类
wine = wine[wine['Class label'] != 1]
y = wine['Class label'].values
X = wine[['Alcohol','OD280/OD315 of diluted wines']].values
# 将分类标签变成二进制编码:标签变为{0,1}
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的类别等比例抽样
为了对比AdaBoost的性能,我们用决策树作为对比
# 使用单一决策树建模
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
tree = DecisionTreeClassifier(criterion='entropy',random_state=1,max_depth=3)
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 accuracies: %.3f' % tree_train)
print('Decision tree test accuracies: %.3f' % tree_test)
Decision tree train accuracies: 0.926
Decision tree test accuracies: 0.875
# 使用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 accuracies: %.3f' % ada_train)
print('Adaboost test accuracies: %.3f' % ada_test)
# print('Adaboost train/test accuracies %.3f/%.3f' % (ada_train,ada_test))
Adaboost train accuracies: 1.000
Adaboost test accuracies: 0.917
对比决策树和 AdaBoost,可以发现 AdaBoost学习准确率达到100%,而决策树则可能欠学习,因此泛化性能也略差。但是AdaBoost的训练集和测试集准确率相差较大,可能是因为过拟合。我们通过图像看下学习结果。
从图像看出Adaboost模型的决策边界比单层决策树的决策边界要复杂的多。Adaboost试图用增加模型复杂度而降低偏差的方式去减少总误差,但是过程中引入了方差,可能出现过拟合。因此在训练集和测试集之间的性能存在较大的差距。