AdaBoost算法实现

120 阅读4分钟

一、AdaBoost 基本原理

​ AdaBoost的全称为Adaptive Boosting, 即自适应提升算法,是一种在每一轮实时改变训练样本权重来学习多个弱分类器并按权重线性组合成强分类器的提升算法。之前的模型都可以称之为单模型,而AdaBoost 是一种集成模型,而且是纵向的,即对一个训练集通过多次迭代训练多个弱分类器,每一个新的分类器都是在前一个分类器的基础上进行训练,每一个弱分类器主要是为了纠正前一个分类器的错误(通过提高错分样本的权重实现,使得那些被前一个分类器分类错误的样本在后续分类器的训练中得到更多的关注。这种方法使得后续的分类器能够聚焦于前面分类器没有正确分类的样本)同时根据每个弱分类器的准确率赋予相应的权重,准确率高的赋予高的权重,低的赋予低的权重,然后组合所有迭代轮次的弱分类器,形成强分类器。详细推导见《机器学习公式推导与代码实现》(鲁伟,P145-147)。

二、代码实现

### 定义决策树桩类,作为Adaboost弱分类器
class DecisionStump():
    def __init__(self):
        # 基于划分阈值决定样本分类为1还是-1
        self.label = 1
        # 特征索引
        self.feature_index = None
        # 特征划分阈值
        self.threshold = None
        # 指示分类准确率的值
        self.alpha = None
### 定义AdaBoost算法类
class Adaboost:
    # 弱分类器个数,即迭代次数,默认为5
    def __init__(self, n_estimators=5):
        self.n_estimators = n_estimators
        
    # Adaboost拟合算法
    def fit(self, X, y):
        m, n = X.shape
        # 初始化权重分布为均匀分布 1/m,产生一维的维度值为m(即m长)的数组;
        w = np.full(m, (1/m))   
        # 处初始化基分类器列表,作为强分类器的容器;
        self.estimators = []
        
        for _ in range(self.n_estimators):   # 根据分类器数目进行迭代,以产生每次的弱分类器;
            # (2.a) 训练一个弱分类器:决策树桩
            estimator = DecisionStump()  # 每一轮初始化一个弱分类器;
            # 设定一个最小化误差
            min_error = float('inf')   #初始化为最大值;
            # 遍历数据集特征,根据最小分类误差率选择最优划分特征
            for i in range(n):    # n 特征数目 
                # 获取特征值 
                values = np.expand_dims(X[:, i], axis=1)  #np.expand_dims 在指定维度前面增加一维,
                #X[:, i] 为切片
                # 特征取值去重
                unique_values = np.unique(values)
                # 尝试将每一个特征值作为分类阈值
                for threshold in unique_values:
                    p = 1
                    # 初始化所有预测值为1
                    pred = np.ones(np.shape(y))
                    # 小于分类阈值的预测值为-1
                    pred[X[:, i] < threshold] = -1     #小于阈值的赋值为 -1;
                    # 2.b 计算误差率
                    error = sum(w[y != pred])   #  分类误差率,结合概率加权的; 
                    
                    # 如果分类误差大于0.5,则进行正负预测翻转;大于0.5,说明应将i特征值大于
                    # 阈值对应的标签标为-1,并使error = 1- error
                    # 例如 error = 0.6 => (1 - error) = 0.4
                    if error > 0.5:
                        error = 1 - error
                        p = -1

                    # 一旦获得最小误差则保存相关参数配置
                    if error < min_error:
                        estimator.label = p   #为了正负翻转而定义的flag符号,以使--(tag)式不改变符号;
                        estimator.threshold = threshold   #保存分类器的阈值
                        estimator.feature_index = i  #保存最佳特征值
                        min_error = error   # 保存最小误差
                        
            # 2.c 计算基分类器的权重
            estimator.alpha = 0.5 * np.log((1.0 - min_error) / (min_error + 1e-9))  #防止min_error==0;
            # 初始化所有预测值为 1
            preds = np.ones(np.shape(y))
            # 获取所有小于阈值的负类索引
            negative_idx = (estimator.label * X[:, estimator.feature_index] < estimator.label * estimator.threshold)   #    --(tag)
            
            # 负值索引;如果是正负翻转预测则是大于estimator.threshold  若不改变小于符号  则需要乘上符号
            # 将负类设为 '-1'
            preds[negative_idx] = -1  
            # 2.d 更新样本权重  
            w *= np.exp(-estimator.alpha * y * preds)   # 向量乘法
            w /= np.sum(w)

            # 保存该弱分类器
            self.estimators.append(estimator)
    
    # 定义预测函数
    def predict(self, X):
        m = len(X)
        y_pred = np.zeros((m, 1))
        # 计算每个弱分类器的预测值
        for estimator in self.estimators:
            # 初始化所有预测值为1
            predictions = np.ones(np.shape(y_pred))
            # 获取所有小于阈值的负类索引
            negative_idx = (estimator.label * X[:, estimator.feature_index] < estimator.label * estimator.threshold)
            # 将负类设为 '-1'
            predictions[negative_idx] = -1
            # 2.e 对每个弱分类器的预测结果进行加权
            y_pred += estimator.alpha * predictions   # 每个局部已经预测好了;

        # 返回最终预测结果
        y_pred = np.sign(y_pred).flatten()    # 对每一项预测取符号函数值并flatten,返回成一个维度;
        return y_pred   # 返回值是(m,)形式的数组;  
        
        
        
# 测试        
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 产生测试数据
from sklearn.model_selection import train_test_split
# 导入sklearn模拟二分类数据生成模块
from sklearn.datasets import make_blobs
# 生成模拟二分类数据集
X, y =  make_blobs(n_samples=550, n_features=2, centers=2,
  cluster_std=2.2, random_state=40)
# 将标签转换为1/-1
y_ = y.copy()
y_[y_==0] = -1
y_ = y_.astype(float)
# 训练/测试数据集划分
X_train, X_test, y_train, y_test = train_test_split(X, y_,
 test_size=0.3, random_state=43)
# 设置颜色参数
colors = {0:'r', 1:'g'}
# 绘制二分类数据集的散点图
plt.scatter(X[:,0], X[:,1], marker='o', c=pd.Series(y).map(colors))
plt.show();

# 导入sklearn metrics 计算准确率
from sklearn.metrics import accuracy_score
# 创建Adaboost模型实例
clf = Adaboost(n_estimators=20)
# 模型拟合
clf.fit(X_train, y_train)
# 模型预测
y_pred = clf.predict(X_test)
# 计算模型预测准确率
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy of AdaBoost by numpy:", accuracy)

数据集

image.png

结果:

Accuracy of AdaBoost by numpy: 0.9939393939393939