AdaBoost算法原理及python实现(手动感叹号)

什么是AdaBoost

  AdaBoost是Boosting算法的一种,比较出名,它可以将多个弱分类器线性组合成一个强分类器,通俗的说就是“三个臭皮匠,顶个诸葛亮”。其要求弱分类器好而不同,每个分类器的准确率要在50%以上。

算法流程

  AdaBoos先初始化样本权值分布,并从初始训练集训练出一个基学习器,再根据这个基学习器的分类结果对训练样本的权值分布进行调整,再生成新的基学习器,依次进行下去,直到满足要求。其流程如下:
(1)初始化样本权值分布
(2)生成基本分类器G1
(3)计算分类器系数 α \alpha α
(4)更新训练数据的权值分布
(5)生成新的分类器G2
(6)循环(2-5)
(7)将所有的分类器线性相加。
  这个过程看起来比较难理解,同时书上公式太多,看得头大,所以咱们还是举个简单的例子吧,这个例子是李航《统计学习方法》里的。假设我们有这样一些数据:

序号12345678910
x0123456789
y111-1-1-1111-1

   x x x表示样本值, y y y是这些样本的标签,我们现在要做的就是根据x的值建立一个模型来预测y的值。本质上是一个二分类问题,建立一个分类器将这些样本进行分类,1表示正例,-1表示反例。在正式开始之前,我们先假定这些样本的权值 w w w都是一样的,都为0.1(总共10个样本,权值的和为1),如下表所示:

序号12345678910
x0123456789
w10.10.10.10.10.10.10.10.10.10.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)={11x<ux>u(1)
  其中 u u u为阈值, i i i是下标(不会编辑,哭了),为了方便讨论我们将u的值设置为:

序号1234567891011
u-0.50.51.52.53.54.55.56.57.58.59.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)={11x<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如下

序号12345678910
x0123456789
y111-1-1-1111-1
G111-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} α=21lne1e(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.310.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αiyiG1(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=1mw1(i)eαiyiG1(xi)(6)
  m是样本的个数,不难看出 Z Z Z的作用是让新生成的样本权值的和为1,通过该公式计算可以得到新的权值分布:

序号12345678910
x0123456789
w20.071430.071430.071430.071430.071430.071430.166670.166670.166670.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α1y1G1(x1)=5.500110.1e0.423611=0.07143,其中Z=0.071437+0.166673=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)=α1G1(x)+α2G2(x)+αnGn(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.4236G1(x)+0.6496G2(x)+0.7514G3(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)={11x<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)={11x<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.64960.7514=0.32180.4236+0.64960.7514=0.52540.4236+0.6496+0.7514=0.97740.42360.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)= 1111x<2.52.5<x<5.55.5<x<8.5x>8.5
模型的输出如下:

序号12345678910
x0123456789
y111-1-1-1111-1
G111-1-1-1111-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主“致敬大神”,大家都去点波关注啊,哈哈哈,溜了溜了,有问题可以留言。

  • 12
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

S.E.G

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值