【机器学习】【提升方法-AdaBoost-2】AdaBoost算法分类器的Python实现+代码讲解

本文详细介绍了AdaBoost算法的原理和数学求解过程,并提供了完整的Python实现代码,包括关键知识点的解释和运行结果。通过示例解释了训练样本集、分类器的f(x)以及sign[f(x)]的计算,帮助读者深入理解AdaBoost算法。

1.AdaBoost算法详解和数学求解过程

    AdaBoost强分类器算法的生成过程详解、以及示例讲解数学求解的详细过程,可以详见上篇博客:

    https://blog.csdn.net/u012421852/article/details/80201506

2.AdaBoost算法的Python实现

2.0python知识点

代码中主要用到的重要python知识点有:

1)lambda(函数) + np.frompyfunc(函数向量化)

    函数向量化可详见:【机器学习】【Numpy】函数向量化运算的4种方法(numpy.frompyfunc()和numpy.vectorize())

2)function + np.frompyfunc

3)np.multiply(序列1, 序列2),实现序列1和序列2的内部下标对应的元素相乘

    知识点可详见:【Python】【numpy-汇总5】所有多元计算函数的示例代码

4)lambda + [for 序列A] 可以对序列A中每个元素都进行lambda操作

5)list.count(e) :计算list中元素e的个数

6)sum(list):计算list的所有元素之和

7)ndarray.sum():计算array的所有元素之和

    上面5~7详见:【再回首Python之美】【numpy】矩阵matrix、array、list操作示例汇总

8)序列的切片A[0:-1] :不要A的最后(-1)一个元素剩下的切片,如A=[4,5,6,7,8],则A[0:-1] = A[:-1] = [4,5,6,7]

    知识点可详见:【Python】【numpy-汇总8】numpy.ndarray索引/切片方式的示例代码

9)(condition then [result1] or [resultt2])[0]

千万不要使用cond then r1 or r2,原因详见:【再回首Python之美】【and-or】危险的and-or,安全的if-else,和安全的and-or

10)生成一个序列,并指定元素的默认值

    比如[0.1, 0.1, 0.1, 0.1],可以如下生成:

    可知元素默认值为0.1,序列长度N=4

    方法1)ret = np.ones((1,N)) / N,生成对象类型为np.ndarray

    方法2)ret = [0.1 for e in range(N)],生成对象类型为list

2.2此代码对应看的示例

此代码看时可以对应着下面示例来理解


2.3代码

完全人肉出品,代码详见如下:

# -*- coding: utf-8 -*-
"""
@author:蔚蓝的天空tom
Aim:向量法实现AdaBoost算法
"""
import numpy as np
    
class CAdaBoost(object):
    '''AdaBoost算法'''
    def __init__(self, train_samples):
        self.S = train_samples      #训练样本集
        self.N = np.shape(self.S)[0]#样本总数
        self.V = [] #所有可能阈值的集合
        self.G = [] #分类器的分类结果
        self.W = [] #权值分布,/85 weight distribution
        self.E = [] #误差率,根据分类器的分类结果计算得到
        self.A = [] #分类器的系数,根据误差率e计算得到
        self.Z = [] #规范化因子,根据W,A,S[:,-1],G
        self.Gen = [] #sign[f(x)]中子分类器的存储,每个子分类器=[系数alpha, 阈值v, f1, f2]
        
        self.init_v()
        self.build_generator()
        return

    def init_v(self):
        '''所有可能阈值的集合'''
        F = self.S[:,-1] #训练样本集的标签集合
        #初始化阈值集合self.V
        e_func = lambda i: (F[i] != F[i+1]) \
                             and self.V.append([i+0.5, F[i],F[i+1]]) \
                             or None
        func = np.frompyfunc(e_func, 1, 1)
        ar = np.arange(self.N)[0:-1]#切片:不包含最后一个元素
        func(ar)
        return self.V
        
    def build_generator(self):
        '''迭代生成分类器'''
        datas, flags, N, V = self.S[:,0], self.S[:,-1], self.N, self.V
        #基本分类器, if x<v then f1 elif x>v then f2
        base_gen = lambda x,v,f1,f2 : ((x<v) and [f1] or [f2])[0]
        #分类结果错误标记方法, 如果分类结果不等于观测值,则在分类结果错误统计表stat中置1
        dif_ufunc = lambda gen_f,observed_f : (gen_f != observed_f and [1] or [0])[0]
        dif_func = np.frompyfunc(dif_ufunc, 2, 1)
        #初始化权值都为1/N
        if np.shape(self.Gen)[0] == 0:
            self.W = np.ones((1,N))/N
        #遍历访问阈值集合,选取误差率最小的阈值作为最优阈值
        G, e, v, f1, f2 = [], 1, None, None, None
        for i in range(np.shape(V)[0]):
            #分类
            cur_G = [base_gen(x, V[i][0], V[i][1], V[i][2]) for x in datas]
            #误差率
            stat = dif_func(flags, cur_G)
            e_dist = np.multiply(stat, self.W)[0]
            cur_e = e_dist.sum()
            #选取最小误差率
            if cur_e<e:
                G, e, v, f1, f2 = cur_G, cur_e, V[i][0], V[i][1], V[i][2]
        #分类器的系数alpha
        alpha = 0.5*np.log((1-e)/e)
        #规范化因子z
        z_ufunc = lambda w,y,g : w*np.exp(-1*alpha*y*g)
        z_func = np.frompyfunc(z_ufunc, 3, 1)
        z = z_func(self.W, flags, G).sum()
        #更新训练数据的权值分布
        w_ufunc = lambda w,y,g : w/z * np.exp(-1*alpha*y*g)
        w_func = np.frompyfunc(w_ufunc, 3, 1)
        self.W = w_func(self.W, flags, G)
        #更新强分类器sign[f(x)]中的f(x)
        self.Gen.append([alpha, v, f1, f2]) #系数alpha, 阈值v, f1, f2
        #判断是否结束分类器的迭代生成
        fx_ufunc = lambda x : sum([g[0]*base_gen(x, g[1], g[2], g[3]) for g in self.Gen])
        fx_func = np.frompyfunc(fx_ufunc, 1, 1)
        G = np.sign(fx_func(datas))
        error_stat = dif_func(flags, G)
        error_cnt = list(error_stat).count(1)
        if 0 == error_cnt:            
            print('sign[f(x)]误差个数是0, 成功生成分类器\nsign[f(x)], f(x)=')
            print(np.array(self.Gen))
            return self.Gen
        else:
            print('sign[f(x)]误差个数是%d'%list(error_stat).count(1),',需要继续迭代生成分类器.\n')
            return self.build_generator()
            
    def Generator(self, test_samples):
        '''对测试样本集进行分类'''
        #基本分类器 if x<v then f1 elif x>v then f2
        base_gen = lambda x,v,f1,f2 : ( x<v and [f1] or [f2])[0]
        #f(x)
        fx_ufunc = lambda x : sum([g[0]*base_gen(x, g[1], g[2], g[3]) for g in self.Gen])
        fx_func = np.frompyfunc(fx_ufunc, 1, 1)
        #sign[f(x)]对测试样本集分类
        G = np.sign([fx_func(test_samples)])
        print('\n测试样本集:', test_samples)
        print('分类结果:', G)
        return G
    
def CAdaBoost_manual():
    #训练样本集
    samples = np.array([[0,1],[1,1],[2,1],[3,-1],[4,-1],[5,-1],[6,1],[7,1],[8,1],[9,-1]])
    
    ab = CAdaBoost(samples) #AdaBoost Algorithm
    
    #用训练样本作为测试样本看看分类器的分类结果
    ab.Generator(samples[:,0])
    
    #新建测试样本集看看分类器的结果
    test_samples = np.array([0.5,1.5,2.5,3.5,4.5,6.5,7.5,8.5,9.5])
    ab.Generator(test_samples)

    test_samples = np.array([-5,-4,-3,-2,-1])
    ab.Generator(test_samples)
    
    test_samples = np.array([10,11,12,13,14,15])
    ab.Generator(test_samples)

if __name__=='__main__':
    CAdaBoost_manual()

2.4运行结果

sign[f(x)]误差个数是3 ,需要继续迭代生成分类器.

sign[f(x)]误差个数是3 ,需要继续迭代生成分类器.

sign[f(x)]误差个数是0, 成功生成分类器
sign[f(x)], f(x)=
[[ 0.42364893  2.5         1.         -1.        ]
 [ 0.64964149  8.5         1.         -1.        ]
 [ 0.7520387   5.5        -1.          1.        ]]

测试样本集: [0 1 2 3 4 5 6 7 8 9]
分类结果: [[1 1 1 -1 -1 -1 1 1 1 -1]]

测试样本集: [ 0.5  1.5  2.5  3.5  4.5  6.5  7.5  8.5  9.5]
分类结果: [[1 1 -1 -1 -1 1 1 -1 -1]]

测试样本集: [-5 -4 -3 -2 -1]
分类结果: [[1 1 1 1 1]]

测试样本集: [10 11 12 13 14 15]
分类结果: [[-1 -1 -1 -1 -1 -1]]

3.解释上面程序生成的分类器

3.1 训练样本集

train_samples = np.array([[0,1],

                                        [1,1],

                                        [2,1],

                                        [3,-1],

                                        [4,-1],

                                        [5,-1],

                                        [6,1],

                                        [7,1],

                                        [8,1],

                                        [9,-1]])

训练数据=[0,1,3,4,5,6,7,8,9]

训练数据的标记集合=[1,1,1,-1,-1,-1,1,1,1,-1]

3.2 f(x) of sign[f(x)]

sign[f(x)], f(x)=
[[ 0.42364893  2.5         1.         -1.        ]
 [ 0.64964149  8.5         1.         -1.        ]
 [ 0.7520387   5.5        -1.          1.        ]]

3.3sign[f(x)]的数学公式代码

sign[f(x)]的数学函数代码为:

    def Generator(self, test_samples):
        '''对测试样本集进行分类'''
        #基本分类器 if x<v then f1 elif x>v then f2
        base_gen = lambda x,v,f1,f2 : ( x<v and [f1] or [f2])[0]
        #f(x)
        fx_ufunc = lambda x : sum([g[0]*base_gen(x, g[1], g[2], g[3]) for g in self.Gen])
        fx_func = np.frompyfunc(fx_ufunc, 1, 1)
        #sign[f(x)]对测试样本集分类
        G = np.sign([fx_func(test_samples)])
        print('\n测试样本集:', test_samples)
        print('分类结果:', G)
        return G

上面代码解释如下:

最终分类器sign[f(x)] = f1(x) + f2(x) + f3(x)

base_gen(x,v,f1,f2) = if x<v then f1 else then f2 #base generator

每一个(fx) = a*bg(x, v, f1, f2)

其中self.Gen[i]存储的是第i个函数f(x)的a, v, f1, f2

则第i个fx, fi(x) = self.Gen[i][0] * base_gen(x,  self.Gen[i][1],  self.Gen[i][2],  self.Gen[i][3]) 


最终的分类器sign[f(x)] = f1(x) + f2(f) + f3(x)

= sign[0.42364893 * base_gen(x, 2.5, 1, -1) +0.64964149 * base_gen(x, 8.5, 1, -1) + 0.7520387 * base_gen(x, 5.5, -1, 1)]

3.4示例:给样本x=3.5分类

样本x=3.5,分类结果就是:sign[f(x)] = sign[f(3.5)] = sign[-0.526046136517] = -1

再瞅瞅训练样本集:samples = np.array([[0,1],[1,1],[2,1],[3,-1],[4,-1],[5,-1],[6,1],[7,1],[8,1],[9,-1]])

可以知道:3.5被分类到-1,是合理的。

(end)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值