集成学习依据个体学习器之间是否存在依赖关系分为两类:一类是个体学习器之间存在强依赖关系,其代表算法是boosting算法;另一类是个体学习器之间不存在强依赖关系,代表算法是bagging算法。AdaBoost是最著名的算法之一,既可以做分类问题,也可以解决回归问题。
Boosting的思想是对于一个复杂模型,每一个学习器是一个弱学习器,其学习能力有限,并不能很好的学习模型中的复杂特征,但是将每一个弱学习器学习的模型组合在一起形成一个强学习器,大大地提升了模型的学习能力,正所谓“三个臭皮匠顶个诸葛亮”。
假设给定一个二类分类的训练集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x N , y N ) } T=\left\{(x_1,y_1),(x_2,y_2),...,(x_N,y_N)\right\} T={(x1,y1),(x2,y2),...,(xN,yN)}
x i x_i xi是n维向量,有n个特征, x i ∈ X ⊆ R n x_i\in \mathcal X \subseteq R^n xi∈X⊆Rn; y i y_i yi是类别标签, y i ∈ Y = { − 1 , + 1 } y_i\in \mathcal {Y}=\left\{-1,+1\right\} yi∈Y={−1,+1}, X \mathcal X X是实例空间, Y \mathcal Y Y是标记集合。AdaBoost利用以下算法,从训练数据中学习一系列弱分类器(基分类器),并将这些弱分类器线性组合成一个强分类器。
在介绍AdaBoost算法之前,先思考以下这些问题:
输入:训练数据集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x N , y N ) } T=\left\{(x_1,y_1),(x_2,y_2),...,(x_N,y_N)\right\} T={(x1,y1),(x2,y2),...,(xN,yN)},其中 x i ∈ X ⊆ R n x_i\in \mathcal X \subseteq R^n xi∈X⊆Rn, y i ∈ Y = { − 1 , + 1 } y_i\in \mathcal {Y}=\left\{-1,+1\right\} yi∈Y={−1,+1};弱分类器;
输出:最终分类器 G ( x ) G(x) G(x)
步骤1:假设训练数据集具有均匀的权值分布,即每个训练样本在基分类器的学习作用相同,数据集样本的权重初始化,这一假设保证能够在原始数据上学习基分类器 G 1 ( x ) G_1(x) G1(x)。
步骤2:AdaBoost迭代学习基分类器,在每一轮 m = 1 , 2 , . . . , M m=1,2,...,M m=1,2,...,M按顺序执行以下操作:
(a) 使用当前分布 D m D_m Dm加权的训练数据集,学习基分类器 G m ( x ) G_m(x) Gm(x)。
(b) 计算基分类器 G m ( x ) G_m(x) Gm(x)在加权训练数据集上的分类错误率: e m = ∑ i = 1 N P ( G m ( x i ) = ̸ y i ) = ∑ G m ( x i ) = ̸ y i W m i e_m=\sum_{i=1}^NP(G_m(x_i)=\not y_i)=\sum_{G_m(x_i)=\not y_i}W_{mi} em=i=1∑NP(Gm(xi)≠yi)=Gm(xi)≠yi∑Wmi W m i W_{mi} Wmi表示第m轮中第i个实例的权值, ∑ i = 1 N W m i = 1 \sum_{i=1}^NW_{mi}=1 ∑i=1NWmi=1,表明 G m ( x ) G_m(x) Gm(x)在加权的训练数据集上的分类误差率是被 G m ( x ) G_m(x) Gm(x)误分类样本的权值之和。
(c) 计算基分类器 G m ( x ) G_m(x) Gm(x)的系数 α m \alpha_m αm, α m \alpha_m αm表示 G m ( x ) G_m(x) Gm(x)在最终分类器中的重要性。当 e m ≤ 1 2 e_m\leq \frac{1}{2} em≤21时, α m ≥ 0 \alpha_m\ge 0 αm≥0,并且 α m \alpha_m αm随着 e m e_m em的减少而增大,所以分类误差率越小的基分类器在最终分类器中的作用越大。
(d) 更新训练数据的权值分布为下一轮做准备
W m + 1 , i = { W m i Z m e − α m , G m ( x i ) = y i W m i Z m e α m , G m ( x i ) = ̸ y i W_{m+1,i} = \begin{cases} \frac{W_{mi}}{Z_m}e^{-\alpha_m}, G_m(x_i)=y_i \\ \frac{W_{mi}}{Z_m}e^{\alpha_m},G_m(x_i)=\not y_i \end{cases} Wm+1,i={ZmWmie−αm,Gm(xi)=yiZmWmieαm,Gm(xi)≠yi
由此可知,被基分类器 G m ( x ) G_m(x) Gm(x)误分类样本的权值在扩大,而被正确分类样本的权值在缩小。相比较,误分类样本的权值被放大 e 2 α m = 1 − e m e m e^{2\alpha_m}=\frac{1-e_m}{e_m} e2αm=em1−em倍。正确分类的样本在下一轮的训练过程中的权重较小,而误分类样本在下一轮的学习权重较大。对于全体训练数据集,在每一轮训练之前更新对应的权重分布,使得训练数据在基分类器的学习中起着不同的作用。
步骤3:线性组合 f ( x ) f(x) f(x)实现M个基分类器的加权表决。系数 α m \alpha_m αm表示基分类器 G m ( x ) G_m(x) Gm(x)的重要程度。 f ( x ) f(x) f(x)的符号决定了实例x的类别。
AdaBoost的核心思想在于样本权重的更新和弱分类器权值的生成,样本权重的更新保证了前面的弱分类器重点处理普遍情况,后续的分类器重点处理疑难杂症。最终,弱分类器加权组合保证了前面的弱分类器会有更大的权重,这其实有先抓总体,再抓特例的分而治之思想。
AdaBoost算法的目标是学习每一个弱学习器 G m ( x ) G_m(x) Gm(x)和对应的权重系数 α m \alpha_m αm,然后将这些弱学习器线性组合成一个强学习器。因此,AdaBoost模型是加法模型,学习算法为前向分步算法。其中前向分步算法事实上是通过每一轮的弱学习器学习,利用前N轮的弱学习器的结果来更新后一个弱学习器的训练模型。
例如,前k-1轮的强学习器为:
f k − 1 ( x ) = ∑ i = 1 k − 1 α i G i ( x ) f_{k-1}(x)=\sum_{i=1}^{k-1}\alpha_iG_i(x) fk−1(x)=i=1∑k−1αiGi(x)
目标是使前向分步算法得到的 α k , G k ( x ) \alpha_k,G_k(x) αk,Gk(x)使 f k ( x ) f_{k}(x) fk(x)在训练数据集上的损失最小,即
( α k , G k ( x ) ) = a r g min α , G ∑ i = 1 N L ( y i , f k − 1 ( x ) + α G ( x ) ) (\alpha_k,G_k(x))=arg\min_{\alpha,G}\sum_{i=1}^NL(y_i,f_{k-1}(x)+\alpha G(x)) (αk,Gk(x))=argα,Gmini=1∑NL(yi,fk−1(x)+αG(x))
则第k轮的强学习器为: f k ( x ) = f k − 1 ( x ) + α k G k ( x ) f_k(x)=f_{k-1}(x)+\alpha_kG_k(x) fk(x)=fk−1(x)+αkGk(x)
可见强学习器是通过前向分步学习算法一步步叠加得到的。
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
print('Loading data...')
# load data
iris = load_iris()
data = iris.data
target = iris.target
print('Plotting scatter...')
# plot
def plot(data,target):
x0,y0 = [],[]
x1,y1 = [],[]
x2,y2 = [],[]
for i in range(len(data)):
if target[i] == 0:
x0.append(data[i][0])
y0.append(data[i][1])
elif target[i] == 1:
x1.append(data[i][0])
y1.append(data[i][1])
else:
x2.append(data[i][0])
y2.append(data[i][1])
plt.scatter(x0,y0,c='b',marker='o',label='0')
plt.scatter(x1,y1,c='r',marker='o',label='1')
plt.scatter(x2,y2,c='g',marker='o',label='2')
plt.legend()
plt.show()
plot(data,target)
from sklearn.ensemble import AdaBoostClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from sklearn.datasets import load_iris
print('Loading data...')
# load data
iris = load_iris()
data = iris.data
target = iris.target
X_train,X_test,y_train,y_test = train_test_split(data,target,test_size=0.8,random_state=2019)
print('Starting training...')
# train
clf = AdaBoostClassifier(n_estimators=20,
learning_rate=0.05,
algorithm='SAMME.R',
random_state=20)
clf.fit(X_train,y_train)
print('Predicting with the trained model...')
y_pred = clf.predict(X_test)
print('Evaluating model...')
print("the precise of the model's prediction is:",precision_score(y_test,y_pred,average='macro'))
print("the recall of the model is :",recall_score(y_test,y_pred,average='macro'))
print('the f1_score of the model is :',f1_score(y_test,y_pred,average='macro'))
print('the feature importance of model is:',list(clf.feature_importances_))
Loading data...
Starting training...
Predicting with the trained model...
Evaluating model...
the precise of the model's prediction is: 0.923060391145
the recall of the model is : 0.915363769022
the f1_score of the model is : 0.915824915825
the feature importance of model is: [0.10000000000000001, 0.0, 0.55000000000000004, 0.34999999999999998]