什么是AdaBoost
AdaBoost是Boosting算法的一种,比较出名,它可以将多个弱分类器线性组合成一个强分类器,通俗的说就是“三个臭皮匠,顶个诸葛亮”。其要求弱分类器好而不同,每个分类器的准确率要在50%以上。
算法流程
AdaBoos先初始化样本权值分布,并从初始训练集训练出一个基学习器,再根据这个基学习器的分类结果对训练样本的权值分布进行调整,再生成新的基学习器,依次进行下去,直到满足要求。其流程如下:
(1)初始化样本权值分布
(2)生成基本分类器G1
(3)计算分类器系数
α
\alpha
α
(4)更新训练数据的权值分布
(5)生成新的分类器G2
(6)循环(2-5)
(7)将所有的分类器线性相加。
这个过程看起来比较难理解,同时书上公式太多,看得头大,所以咱们还是举个简单的例子吧,这个例子是李航《统计学习方法》里的。假设我们有这样一些数据:
序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
y | 1 | 1 | 1 | -1 | -1 | -1 | 1 | 1 | 1 | -1 |
x x x表示样本值, y y y是这些样本的标签,我们现在要做的就是根据x的值建立一个模型来预测y的值。本质上是一个二分类问题,建立一个分类器将这些样本进行分类,1表示正例,-1表示反例。在正式开始之前,我们先假定这些样本的权值 w w w都是一样的,都为0.1(总共10个样本,权值的和为1),如下表所示:
序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
w1 | 0.1 | 0.1 | 0.1 | 0.1 | 0.1 | 0.1 | 0.1 | 0.1 | 0.1 | 0.1 |
权值初始化代码如下:
def func_w_1(x):
w_1=[]
for i in range(len(x)):
w_1.append(0.1)
return w_1
假设我们最初的分类器可以将前n个样本识别为正例,剩下的样本为反例,可以用下面的公式表达:
G
i
(
x
)
=
{
1
x<u
−
1
x>u
(1)
Gi(x)= \begin{cases} 1& \text{x<u}\\ -1& \text{x>u} \end{cases}\tag{1}
Gi(x)={1−1x<ux>u(1)
其中
u
u
u为阈值,
i
i
i是下标(不会编辑,哭了),为了方便讨论我们将u的值设置为:
序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---|---|---|---|---|---|---|---|---|---|---|---|
u | -0.5 | 0.5 | 1.5 | 2.5 | 3.5 | 4.5 | 5.5 | 6.5 | 7.5 | 8.5 | 9.5 |
阈值生成的代码如下:
def func_threshs(x):
threshs =[i-0.5 for i in x]
threshs.append(x[len(x)-1]+0.5)
return threshs
对于每一个分类器
G
i
(
x
)
Gi(x)
Gi(x),其误差率
e
e
e可用其出错(即
G
i
(
x
i
)
≠
y
i
Gi(xi)\neq yi
Gi(xi)=yi)的权值和来表示,比如当阈值为2.5时,该分类器为
G
i
(
x
)
=
{
1
x<2.5
−
1
x>2.5
(2)
Gi(x)= \begin{cases} 1& \text{x<2.5}\\ -1& \text{x>2.5} \end{cases}\tag{2}
Gi(x)={1−1x<2.5x>2.5(2)
或者
G
i
(
x
)
=
{
−
1
x<2.5
1
x>2.5
(3)
Gi(x)= \begin{cases} -1& \text{x<2.5}\\ 1& \text{x>2.5} \end{cases}\tag{3}
Gi(x)={−11x<2.5x>2.5(3)
这样每一个阈值都可以生成2种分类器,因此共有22个分类器。对于分类器
(
2
)
(2)
(2)给定输入
x
x
x,其输出
G
G
G如下
序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
y | 1 | 1 | 1 | -1 | -1 | -1 | 1 | 1 | 1 | -1 |
G | 1 | 1 | 1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 |
分类器实现的代码如下:
def func_cut(threshs):
y_pres_all={}#定义正向分类器,形如公式2
y_last_all={}#定义正向分类器,形如公式3
for thresh in threshs:
y_pres=[]
y_last=[]
for i in range(len(x)):
if x[i]<thresh:
y_pres.append(1)
y_last.append(-1)
else:
y_pres.append(-1)
y_last.append(1)
y_pres_all[thresh]=y_pres
y_last_all[thresh]=y_last
return y_pres_all,y_last_all
从表中可以看出,样本7、8、9分错了,故其误差率e=w7+w8+w9=0.3。我们用同样的方法求这22个分类器的误差率,选择误差率最小的作为基分类器。计算误差率的代码如下:
def sub_func_e(y,w_n,y_pres_all,thresh):
e=0
for i in range(len(y)):
if y_pres_all[thresh][i]!=y[i]:
e+=w_n[i];
return e
def sub_func_e_s(y,w_n,y_pres_all,threshs):
e_s={}
e_min=1
n=0
for thresh in threshs:
e=sub_func_e(y,w_n,y_pres_all,thresh)
e_s[thresh]=round(e,6)
if e<e_min:
e_min=round(e,6)
n=thresh
return e_s,e_min,n
def sub_func_e_all(y,w_n,y_pres_all,y_last_all,threshs):
e_s1,e_min1,n1=sub_func_e_s(y,w_n,y_pres_all,threshs)
e_s2,e_min2,n2=sub_func_e_s(y,w_n,y_last_all,threshs)
if e_min1<=e_min2:
return e_s1,e_min1,n1,y_pres_all
else:
return e_s2,e_min2,n2,y_last_all
在我们的例子中,通过计算所有的误差率得到基分类器为公式2所示。下面开始计算分类器的系数
α
\alpha
α,其计算公式如下:
α
=
1
2
l
n
1
−
e
e
(4)
\alpha=\frac{1}{2}ln\frac{1-e}{e}\tag{4}
α=21lne1−e(4) 由此可见误差率e不能大于0.5,否则会使得
α
\alpha
α<0。本例中
α
=
1
2
l
n
1
−
0.3
0.3
=
0.4236
\alpha=\frac{1}{2}ln\frac{1-0.3}{0.3}=0.4236
α=21ln0.31−0.3=0.4236,计算分类器系数的代码如下:
def func_a_n(e_min):
a_n=round(0.5*math.log((1-e_min)/e_min),6)
return a_n
最后再根据以上计算的结果更新权值分布w2,更新的公式如下:
w
2
i
=
w
1
(
i
)
Z
∗
e
−
α
i
∗
y
i
∗
G
1
(
x
i
)
(5)
w2i=\frac{w1(i)}{Z}*e^{-\alpha{i}*yi*G1(xi)}\tag{5}
w2i=Zw1(i)∗e−αi∗yi∗G1(xi)(5)其中
Z
=
∑
i
=
1
m
w
1
(
i
)
∗
e
−
α
i
∗
y
i
∗
G
1
(
x
i
)
(6)
Z=\sum_{i=1}^m w1(i)*e^{-\alpha{i}*yi*G1(xi)}\tag{6}
Z=i=1∑mw1(i)∗e−αi∗yi∗G1(xi)(6)
m是样本的个数,不难看出
Z
Z
Z的作用是让新生成的样本权值的和为1,通过该公式计算可以得到新的权值分布:
序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
w2 | 0.07143 | 0.07143 | 0.07143 | 0.07143 | 0.07143 | 0.07143 | 0.16667 | 0.16667 | 0.16667 | 0.07143 |
举个例子,第一个样本的权值更新成了0.07143,这个是怎么来的呢,是这样算的: w 1 ( 1 ) Z ∗ e − α 1 ∗ y 1 ∗ G 1 ( x 1 ) = 0.1 5.50011 ∗ e − 0.4236 ∗ 1 ∗ 1 = 0.07143 ,其中 Z = 0.07143 ∗ 7 + 0.16667 ∗ 3 = 5.50011 \frac{w1(1)}{Z}*e^{-\alpha{1}*y1*G1(x1)}=\frac{0.1}{5.50011}*e^{-0.4236*1*1}=0.07143,其中Z=0.07143*7+0.16667*3=5.50011 Zw1(1)∗e−α1∗y1∗G1(x1)=5.500110.1∗e−0.4236∗1∗1=0.07143,其中Z=0.07143∗7+0.16667∗3=5.50011,其实就是w2的和。从表里可以看出,之前分错的样本7,8,9的权值提高了,其余的则下降了。样本分布权值的更新代码如下:
def func_w_n_tmp(x,pre_n,w_n,a_n):
w_n_tmp=[]
for i in range(len(x)):
w_new=round(w_n[i]*math.exp(-1*a_n*y[i]*pre_n[i]),6)
w_n_tmp.append(w_new)
return w_n_tmp
def func_z_n(w_n_tmp):
z_n=sum(w_n_tmp)
return z_n
def func_w_n(w_n_tmp,z_n):
w_n=[round(i/z_n,5) for i in w_n_tmp]
return w_n
我们用同样的流程可以得到多个分类器及多个分类器权值分布,共同组成了新的分类器,即
G
(
X
)
=
s
i
g
n
[
f
(
x
)
]
G(X)=sign[f(x)]
G(X)=sign[f(x)]其中
f
(
x
)
=
α
1
∗
G
1
(
x
)
+
α
2
∗
G
2
(
x
)
+
⋅
⋅
⋅
α
n
∗
G
n
(
x
)
f(x)=\alpha1*G1(x)+\alpha2*G2(x)+\cdot\cdot\cdot\alpha n*Gn(x)
f(x)=α1∗G1(x)+α2∗G2(x)+⋅⋅⋅αn∗Gn(x)
s
i
g
n
[
f
(
x
)
]
sign[f(x)]
sign[f(x)]表示当
f
(
x
)
f(x)
f(x)大于0时取1,小于0时取-1,书上给出的3个分类器的集成,结果为
f
(
x
)
=
0.4236
∗
G
1
(
x
)
+
0.6496
∗
G
2
(
x
)
+
0.7514
∗
G
3
(
x
)
f(x)=0.4236*G1(x)+0.6496*G2(x)+0.7514*G3(x)
f(x)=0.4236∗G1(x)+0.6496∗G2(x)+0.7514∗G3(x),其中:
G
1
(
x
)
=
{
1
x<2.5
−
1
x>2.5
G1(x)= \begin{cases} 1& \text{x<2.5}\\ -1& \text{x>2.5} \end{cases}
G1(x)={1−1x<2.5x>2.5
G
2
(
x
)
=
{
1
x<8.5
−
1
x>8.5
G2(x)= \begin{cases} 1& \text{x<8.5}\\ -1& \text{x>8.5} \end{cases}
G2(x)={1−1x<8.5x>8.5
G
3
(
x
)
=
{
−
1
x<5.5
1
x>5.5
G3(x)= \begin{cases} -1& \text{x<5.5}\\ 1& \text{x>5.5} \end{cases}
G3(x)={−11x<5.5x>5.5
则
f
(
x
)
f(x)
f(x)为
f
(
x
)
=
{
0.4236
+
0.6496
−
0.7514
=
0.3218
x<2.5
−
0.4236
+
0.6496
−
0.7514
=
−
0.5254
2.5<x<5.5
−
0.4236
+
0.6496
+
0.7514
=
0.9774
5.5<x<8.5
−
0.4236
−
0.6496
+
0.7514
=
−
0.3218
x>8.5
f(x)= \begin{cases} 0.4236+0.6496-0.7514=0.3218& \text{x<2.5}\\ -0.4236+0.6496-0.7514=-0.5254& \text{2.5<x<5.5}\\ -0.4236+0.6496+0.7514=0.9774& \text{5.5<x<8.5}\\ -0.4236-0.6496+0.7514=-0.3218& \text{x>8.5} \end{cases}
f(x)=⎩
⎨
⎧0.4236+0.6496−0.7514=0.3218−0.4236+0.6496−0.7514=−0.5254−0.4236+0.6496+0.7514=0.9774−0.4236−0.6496+0.7514=−0.3218x<2.52.5<x<5.55.5<x<8.5x>8.5
最终的分类器
G
(
x
)
=
{
1
x<2.5
−
1
2.5<x<5.5
1
5.5<x<8.5
−
1
x>8.5
G(x)= \begin{cases} 1& \text{x<2.5}\\ -1& \text{2.5<x<5.5}\\ 1& \text{5.5<x<8.5}\\ -1& \text{x>8.5}\end{cases}
G(x)=⎩
⎨
⎧1−11−1x<2.52.5<x<5.55.5<x<8.5x>8.5
模型的输出如下:
序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
y | 1 | 1 | 1 | -1 | -1 | -1 | 1 | 1 | 1 | -1 |
G | 1 | 1 | 1 | -1 | -1 | -1 | 1 | 1 | 1 | -1 |
正确率为100%!是不是很神奇?
python实现
最后我们用python实现上述例子中的结果,我们像书上那样,设置3个分类器,具体的实现代码如下:
#导入相关的库
import math
import pandas as pd
import numpy as np
#输入原始数据
x=[0,1,2,3,4,5,6,7,8,9]
y=[1,1,1,-1,-1,-1,1,1,1,-1]
T=3 #设置分类器个数
#初始化样本权值分布函数
def func_w_1(x):
w_1=[]
for i in range(len(x)):
w_1.append(0.1)
return w_1
#生成阈值函数
def func_threshs(x):
threshs =[i-0.5 for i in x]
threshs.append(x[len(x)-1]+0.5)
return threshs
#根据阈值生成22种分类器的输出
def func_cut(threshs):
y_pres_all={}
y_last_all={}
for thresh in threshs:
y_pres=[]
y_last=[]
for i in range(len(x)):
if x[i]<thresh:
y_pres.append(1)
y_last.append(-1)
else:
y_pres.append(-1)
y_last.append(1)
y_pres_all[thresh]=y_pres #前向分类器
y_last_all[thresh]=y_last #后向分类器
return y_pres_all,y_last_all
#求一个分类器误差率e
def sub_func_e(y,w_n,y_pres_all,thresh):
e=0
for i in range(len(y)):
if y_pres_all[thresh][i]!=y[i]:
e+=w_n[i];
return e
#求一类分类器误差率
def sub_func_e_s(y,w_n,y_pres_all,threshs):
e_s={}
e_min=1
n=0
for thresh in threshs:
e=sub_func_e(y,w_n,y_pres_all,thresh)
e_s[thresh]=round(e,6)
if e<e_min:
e_min=round(e,6)
n=thresh
return e_s,e_min,n
#求两类分类器误差率
def sub_func_e_all(y,w_n,y_pres_all,y_last_all,threshs):
e_s1,e_min1,n1=sub_func_e_s(y,w_n,y_pres_all,threshs)
e_s2,e_min2,n2=sub_func_e_s(y,w_n,y_last_all,threshs)
if e_min1<=e_min2:
return e_s1,e_min1,n1,y_pres_all
else:
return e_s2,e_min2,n2,y_last_all
#输出最佳分类器
def func_pre_n(y_all,n):
pre_n=y_all[n]
return pre_n
#求最佳分类器的误差率
def func_a_n(e_min):
a_n=round(0.5*math.log((1-e_min)/e_min),6)
return a_n
#求临时分布权值
def func_w_n_tmp(x,pre_n,w_n,a_n):
w_n_tmp=[]
for i in range(len(x)):
w_new=round(w_n[i]*math.exp(-1*a_n*y[i]*pre_n[i]),6)
w_n_tmp.append(w_new)
return w_n_tmp
#求Z
def func_z_n(w_n_tmp):
z_n=sum(w_n_tmp)
return z_n
#求更新后的样本分布权值
def func_w_n(w_n_tmp,z_n):
w_n=[round(i/z_n,5) for i in w_n_tmp]
return w_n
#求f(x)
def func_g_n(x,pre_n,a_n):
g_n=[]
for i in range(len(x)):
g_n_single=a_n*pre_n[i]
g_n.append(g_n_single)
return g_n
#各子函数封装
def func_AdaBoost(x,y,T):
a_n_s=[] #保存分类器系数
w_n_s=[] #保存样本分布权值
pre_n_s=[] #保存各个基分类器的输出
e_n_s=[] #保存误差率
g_n_s=[] #保存子分类器
n_s=[] #保存阈值
w_1=func_w_1(x) #初始化权值,都为0.1
w_n_s.append(w_1)
threshs=func_threshs(x)
for i in range(T):
w_n=w_n_s[i]
y_pres_all,y_last_all=func_cut(threshs)
e_s,e_min,n,y_all=sub_func_e_all(y,w_n,y_pres_all,y_last_all,threshs)
pre_n=func_pre_n(y_all,n)
a_n=func_a_n(e_min)
w_n_tmp=func_w_n_tmp(x,pre_n,w_n,a_n)
z_n=func_z_n(w_n_tmp)
w_n=func_w_n(w_n_tmp,z_n)
g_n=func_g_n(x,pre_n,a_n)
a_n_s.append(a_n)
w_n_s.append(w_n)
pre_n_s.append(pre_n)
g_n_s.append(g_n)
n_s.append(n)
e_n_s.append(e_min)
return a_n_s,w_n_s,pre_n_s,g_n_s,n_s,e_n_s
#主程序
a_n_s,w_n_s,pre_n_s,g_n_s,n_s,e_n_s=func_AdaBoost(x,y,T)
#列表、显示结果
data={'x':x,'y':y,'w_1':w_n_s[0],'pre_1':pre_n_s[0],'g_1':g_n_s[0],
'w_2':w_n_s[1],'pre_2':pre_n_s[1],'g_2':g_n_s[1],
'w_3':w_n_s[2],'pre_3':pre_n_s[2],'g_3':g_n_s[2]}
df=pd.DataFrame(data)
df['sum']=df['g_1']+df['g_2']+df['g_3']
#输出预测值
df['G(x)']=np.sign(df['sum'])
df.T
程序的输出结果如下:
备注:以上代码参考b站up主“致敬大神”,大家都去点波关注啊,哈哈哈,溜了溜了,有问题可以留言。