AdaBoost算法是一种Boosting算法,它在解决Boosting算法提出的两个问题时,给出的答案是:1、(改变数据分布的问题) 提高那些在前一轮分类器中被错误分类的样本的权重,减小那些被正确分类样本的权重,这样那些被分类错误的样本在后续的训练中得到了更大的关注;2、(组合各个弱分类器策略的问题) 各个弱分类器的组合是通过加权表决进行组合的,具体来说就是加大分类错误率低的弱分类器的权重,减小分类错误率高的弱分类器的权重,这样那些表现好的分类器就能在最终的表决结果中起到更大的作用。
下面我们结合《李航统计学习》和《机器学习(西瓜书)》来了解一下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。
在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))
而这是一个复杂的优化问题,不能简单的通过凸优化进行学习优化。那么此时就可以使用前向分步算法思想,我们不在构建好加法模型后再优化求解,而是在对于这个加法模型,从前向后,每一步只优化一个基函数及其系数,逐步逼近目标函数,那么就可以降低优化的复杂度。
利用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类
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试图用增加模型复杂度而降低偏差的方式去减少总误差,但是过程中引入了方差,可能出现过拟合。因此在训练集和测试集之间的性能存在较大的差距。