Adaboost 原理介绍和python代码实现

Adaboost 原理

Adaboost 全称为 adaptive boost, 意为自适应的提升方法,作用是能够通过分布不同权重的方式将多个弱分类器提升为一个强分类器,不同的弱分类器解决不同的分类问题,多个弱分类器通过加权组合得到一个强分类器。详细公式过程如下:

  1. 给定 N 个样本的特征向量 ( x 1 , . . . . . )   ( x 2 . . . . . )   . . . ( x N , . . . . ) (x_1, .....) \, (x_2.....) \, ...(x_N, ....) , 每一个样本都有对应的标签 y 1 ,   y 2 , . . . y N {y_1, \, y_2, ... y_N} 与之相对。
  2. 初始化每个样本权重 D 1 ( i ) = ( w 1 , . . . . . w N ) D_1(i) = (w_1,.....w_N) ,初始权重值都为 1 / N 1/N
  3. 循环迭代 $T $ 次,每一次迭代执行如下步骤:
    1. 找出分类误差最小的弱分类器 h j h_j , 计算公式为:$h_j = arg, min , \theta $,其中 θ = 1 N D i ( h j ( x i . . . )   y i ) \theta = \sum_1^N D_i * (h_j(x_i...) \neq \,y_i)
    2. 计算该弱分类器 h j h_j 分类器权重 α = 0.5 l n ( 1 θ θ ) \alpha =0.5 * ln(\frac{1 - \theta}{\theta}) 。这意味着误分率小于0.5的弱分类器得到更高的分类器权重。
    3. 重新更新所有样本的权重 D t + 1 ( i ) = D t ( i ) e x p ( a t y i h t ( x i ) ) Z D_{t+1}(i) = \frac{D_t(i)exp(-a_ty_ih_t(x_i))}{Z} ,其中 Z Z 为规范化因子, 公式为所有样本权重的分子部分 D t ( i ) e x p ( a t y i h t ( x i ) ) D_t(i)exp(-a_ty_ih_t(x_i)) 之和。该式子使得误分类的样本权重提升,正确分类的样本权重降低。
  4. 迭代结束,输出强分类器 H ( x ) = t = 1 T a t h t ( x ) H(x) = \sum_{t=1}^{T} a_t h_t(x) , 形式为 T T 个分类器的加权组合。

代码实现

以下是我个人写的一个Adaboost方法的实现,弱分类器采用sklearn库中的决策树分类器。分类结果并不理想,但大致算法思想足以体现,有助于理解算法。仅此而已。

"""
    Description: 以决策树分类器为弱分类器,使用adaboost进行分类器增强
    Author: qyh
    Date: 18/11/19
"""
import numpy as np
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

class AdaboostWithDT:
    """
    @ self.param:
        train_num: 训练的迭代的次数
        classifier_num: 分类器的个数,等于迭代的次数
        learn_rate:  学习率,在确定最优(误分率最小的)分类器的时候使用
        train_data: 保存的训练数据
        train_labels: 保存训练标签
        train_samples_num: 训练样本的数目
        vector_columns: 样本特征向量的维度
        classifer_sets: 学习得到的弱分类器集合
        sample_weights: 学习得到的样本的权重集合
        alpha: 弱分类器的权重集合
    """

    def __init__(self, n_estunatirs = 50, learn_rate = 1.0):
        self.train_num = n_estunatirs        # 迭代次数
        self.classifier_num = n_estunatirs   # 学习到的弱分类器的个数
        self.learn_rate = learn_rate         # 学习率

    def init_args(self, train_data, train_labels):
        self.train_data = train_data
        self.train_labels = train_labels 
        self.train_samples_num, self.vector_columns = train_data.shape # 驯良样本数目, 样本特征向量的维度 

        self.classifier_sets = []  # 学得的弱分类器的集合

        self.sample_weights = [1.0 / self.train_samples_num] * self.train_samples_num # 训练样本的权重集合

        self.alpha = []  # 弱分类器的权重集合

    # 输入误分率,计算在这一轮迭代中的alpha值,即当前习得的分类器的权重值
    def cal_alpha(self, error):
        return 0.5 * np.log((1 - error) / error)

    #计算规范化因子,在更新权重的时候会使用到
    def cal_normalize_factor(self,weights,alpha,classifiers):  # 第i个样本的classifers[i] 表示分类结果
        return sum([weights[i] * np.exp(-1 * alpha * self.train_labels[i] * classifiers[i]) for i in range(self.train_samples_num)])    


    # 更新样本的权重值
    def cal_weight(self, alpha, classifier, normalize_factor):
        for i in range(self.train_samples_num):
           self.sample_weights[i] = self.sample_weights[i] * np.exp(-1 * alpha * self.train_labels[i] * classifier[i]) / normalize_factor


    # 训练弱分类器
    """
        输入:训练样本,标签,样本权重
        输出:分类器,误分因子, 分类结果
    """
    def find_classifier(self, features, labels, weights):
        samples_num = len(features)  # 训练样本数目
        error = 1000000             # 错误率
        resultClassifier = None    # 该轮得到的最佳分类器
       
        for i in range(5):
            learn_size = self.learn_rate + i 
            max_depth = 6 + learn_size  
            min_samples_leaf = 6 + learn_size
            min_samples_split = 6 + learn_size

            DTClassifier = DecisionTreeClassifier(max_depth=max_depth, 
            min_samples_split=min_samples_split, min_samples_leaf=min_samples_leaf) # 决策树分类器
            DTClassifier.fit(features, labels) # 传入样本数据和标签,训练决策树分类器

            # 计算误分类因子 
            predicted_value = DTClassifier.predict(features) # 二维预测结果
            error_factor = 0  
            for j in range(samples_num):
                if predicted_value[j] != labels[j]:
                    error_factor += weights[j]

            if error_factor < error:
                error  = error_factor
                resultClassifier = DTClassifier
            
            if error == 0:
                break
        result = resultClassifier.predict(features)  # 存储分类结果
        return resultClassifier, error, result 


    """
        训练接口:
        输入: 训练样本集, 标签集
    """
    def fit(self, train_data, train_labels):
        self.init_args(train_data, train_labels)
        for epoch in range(self.train_num):  # 训练次数
            # 训练得到弱分类器(决策树分类器)
            resultClassifier, minError, classifiedResult = self.find_classifier(train_data, train_labels, self.sample_weights)
            
            # 计算分类器的权重系数alpha
            a = self.cal_alpha(minError)
            self.alpha.append(a)
            
            #记录分类器
            self.classifier_sets.append(resultClassifier)

            #更新权重
            Z = self.cal_normalize_factor(self.sample_weights, a, classifiedResult)
            self.cal_weight(a, classifiedResult, Z)

    """
        预测接口:
        输入:一个样本数据
        输出:预测结果
    """
    def predict(self, test_data):
        result = 0
        voteList = [0 for i in range(10)]  
        for i in range(len(self.classifier_sets)):
            classifer = self.classifier_sets[i]
            test_value = np.array(test_data).reshape(1, len(test_data)).tolist() # 二维形式
            predicted_value =int(classifer.predict(test_value))
            voteList[predicted_value] += self.alpha[i]  # 投票的思想 按分类器权值投票
        result = voteList.index(max(voteList))
        return result


    """
        使用测试数据和标签评估
    """
    def score(self, test_data, test_labels):
        right_num = 0
        for i in range(len(test_data)):
            if self.predict(test_data[i]) == test_labels[i]:
                right_num += 1
        return right_num / len(test_data)

猜你喜欢

转载自blog.csdn.net/CVSvsvsvsvs/article/details/85112999