Python手撸机器学习系列(十三):AdaBoost提升算法实现

目录

  • AdaBoost算法
    • 一、概述
    • 二、代码实现
    • 三、参考文献

AdaBoost算法

一、概述

提升算法是将一组弱分类器组合成强分类器的算法,大多数的提升算法都是改变训练数据的概率分布(或权值),针对不用的训练数据训练弱分类器学习,最后再组合弱分类器成为一个强分类器。

对于提速算法,需要解决两个问题:

  • 如何在训练中修改样本的权值
  • 如何组合弱分类器

AdaBoost提升算法主要做法:

  • 首先提高前一轮分类器中被错误分类的样本的权值,并降低被正确分类样本的权值,再将更新后的数据分配给下一轮分类器训练。
  • 在组合分类器上,将每个弱分类器的分类误差率作为权值计算加权值,即加权多数表决。

具体算法为:

有训练集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x N , y N } T = \{(x_1,y_1),(x_2,y_2),...,(x_N,y_N\} T={(x1,y1),(x2,y2),...,(xN,yN},其中 y = { − 1 , + 1 } y=\{-1,+1\} y={1,+1}

首先初始化每个样本的权值为样本总数的倒数,即:
D 1 = ( w 11 , . . . , w 1 , i , . . . , w 1 , N ) ,     w 1 , N = 1 N D_1 = (w_{11},...,w_{1,i},...,w_{1,N}),\ \ \ w_{1,N}=\frac{1}{N} D1=(w11,...,w1,i,...,w1,N),   w1,N=N1
假设我们希望学习 M M M个弱分类器, m = 1 , 2 , . . . , . M m = 1,2,...,.M m=1,2,...,.M,有如下计算步骤:

  • 对第 m m m个分类器对上一轮更新权值后的数据集 D m D_m Dm进行学习,可学得模型:

G m ( x ) : X → { − 1 , 1 } G_m(x):\mathcal{X}→\{-1,1\} Gm(x):X{1,1}

  • 计算当前分类器 G m ( x ) G_m(x) Gm(x)在训练集上的分类误差率:

e m = ∑ i = 1 N w m i I ( G m ( x i ) ≠ y i ) e_m = \displaystyle\sum_{i=1}^Nw_{mi}I(G_m(x_i)≠y_i) em=i=1NwmiI(Gm(xi)=yi)

注意这里是误分类率,即当前分类器分类错误的个数并且乘上当前样本的权值 w m i w_{mi} wmi。更要注意的是,这里的 G m ( x ) G_m( x ) Gm(x)是单个分类器,不要使用当前积累的总分类器 f m ( x ) f_m(x) fm(x)去预测

  • 计算当前分类器的系数(最后组合的权值):

α m = 1 2 l o g 1 − e m e m \alpha_m = \frac{1}{2}log\frac{1-e_m}{e_m} αm=21logem1em

这个式子的意义在于,当 e m > 0.5 e_m>0.5 em>0.5 α m < 0 \alpha_m<0 αm<0,即误差率高时呈负面作用,而 e m < 0.5 e_m<0.5 em<0.5 α m > 0 \alpha_m>0 αm>0 e m e_m em越小 α m \alpha_m αm越大,表明误差率越小该分类器对分类的贡献越大,在组合时“话语权”更大。

  • 更新训练数据集的权值分布
    w m + 1 , i = w m i Z m e − α m y i G m ( x i ) \Large w_{m+1,i} = \frac{w_{mi}}{Z_m}e^{-\alpha_my_iG_m(x_i)} wm+1,i=ZmwmieαmyiGm(xi)
    其中 Z Z Z是规范化因子:
    Z m = ∑ i = 1 N w m i e − α m y i G m ( x i ) \Large Z_m = \displaystyle\sum_{i=1}^Nw_{mi}e^{-\alpha_my_iG_m(x_i)} Zm=i=1NwmieαmyiGm(xi)
    这里 y i ∗ G m ( x i ) y_i*G_m(x_i) yiGm(xi)表示的是当分类器预测正确时结果为1,分类错误时结果为-1

  • 组合当前的弱分类器:
    f m ( x ) = ∑ i = 1 m α m G m ( x ) f_m(x) = \displaystyle\sum_{i=1}^m\alpha_mG_m(x) fm(x)=i=1mαmGm(x)

一直循环上述步骤,直到组合出达到标准的强分类器(比如误分类率为0),最后得到的强分类器:
G ( x ) = s i g n ( f ( x ) ) = ∑ m = 1 M α m G m ( x ) G(x) = sign(f(x)) = \displaystyle\sum_{m=1}^M\alpha_mG_m(x) G(x)=sign(f(x))=m=1MαmGm(x)
这里 s i g n ( x ) sign(x) sign(x)是指示函数,将 f ( x ) f(x) f(x)的值映射到 { 1 , − 1 } \{1,-1\} {1,1}

二、代码实现

这里以李航《统计学习方法》第158页例8.1为例,简要实现Adaboost算法

在这个例子中,分类器为 x < v xx<v x > v x>v x>v,所以在定义模型时需要确立方向,代码中用flag区分;强分类器的标准是误分类点个数为0

import numpy as np

# 计算单个分类器
# 按《统计学习方法》中的例子,该出分类器为寻找切分点切分左右进行分类
def cal_G(data, label ,weights):
    best_point = -1
    best_error = float('inf')   #初始化误分类率
    best_flag = 0 #是小于切分点为正还是大于切分点为正
    for i in range(len(data)-1): #n条数据有n-1个切分点
        point = (data[i] + data[i+1]) / 2
        for flag in [-1, 1]: #两种切分方向
            error = 0
            for j in range(len(data)):
                if predict(data[j],point, flag) != label[j]:
                    error += weights[j]
            if error < best_error:
                best_point = point
                best_error = error
                best_flag = flag

    # 选出切分点后,计算误差率e和系数α
    e = best_error
    a = 1/2 * np.log((1-e)/e)

    # 调整权值分布
    #计算Z
    Z = 0
    for i in range(len(data)):
        Z += weights[i] * np.exp(-a*label[i]*predict(data[i], best_point, best_flag))
    #更新权值
    for i in range(len(weights)):
        weights[i] = weights[i] * np.exp(-a*label[i]*predict(data[i],best_point, best_flag)) / Z
    return a, best_point, weights, best_flag

#不断将弱分类器组合成强分类器
def adaboost(data, label, weight):
    a, g, w, f = cal_G(data, label, weight) #先进行一轮
    A = [a] #分类器的系数
    G = [g] #所有的分类器
    F = [f] #切分点朝向
    all_e = final_predict(data, label, A, G ,F) #整体分类器误分类点个数
    while all_e != 0 :
        a, g, w, f = cal_G(data, label, w)
        A.append(a)
        G.append(g)
        F.append(f)
        all_e = final_predict(data, label, A, G, F)

    return A, G, F

#一个切分点的预测值
def predict(x, point,flag):
    # 小于point为正例子时
    if flag == 1:
        return 1 if x < point else -1
    if flag == -1:
        return -1 if x < point else 1

#对于组合分类器的预测
def final_predict(data, label, A, G, F):
    error = 0
    for i,x in enumerate(data):
        res = 0
        for j in range(len(G)):
            res += A[j] * predict(x, G[j], F[j])
        res = 1 if res > 0 else -1
        if res != label[i]:
            error += 1
    return error #总体误分类点个数

if __name__ == '__main__':
    X = list(np.arange(0,10))
    Y = [1,1,1,-1,-1,-1,1,1,1,-1]
    weights = [1/len(X)]*len(X) #初试化权值
    A, G, F = adaboost(X, Y, weights)
    s = ['%.4f'%(A[i]) + '*' + ('(<' if F[i] == 1 else '(>' )+(str(G[i])) + ')' for i in range(len(A))]
    print('最终模型为:{}'.format('+'.join(s)))

最后得到的强分类器,与李航《统计学习方法》第160页中得到的结果一致。
其实训练过程也是一样的,大家可以逐步打印。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PlKRj9ai-1649929621995)(adaBoost.assets/image-20220414174507295.png)]

三、参考文献

李航《统计学习方法》第二版

你可能感兴趣的:(机器学习,python,机器学习,人工智能,深度学习)