Bootstrap

AdaBoost算法Python代码实现

### 定义AdaBoost算法类
class Adaboost:
    # 弱分类器个数
    def __init__(self, n_estimators=5):
        self.n_estimators = n_estimators
        
    # Adaboost拟合算法
    def fit(self, X, y):
        m, n = X.shape
        # (1) 初始化权重分布为均匀分布 1/N
        w = np.full(m, (1/m))
        # 处初始化基分类器列表
        self.estimators = []
        # (2) for m in (1,2,...,M)
        for _ in range(self.n_estimators):
            # (2.a) 训练一个弱分类器:决策树桩
            estimator = DecisionStump()
            # 设定一个最小化误差
            min_error = float('inf')
            # 遍历数据集特征,根据最小分类误差率选择最优划分特征
            for i in range(n):
                # 获取特征值
                values = np.expand_dims(X[:, i], axis=1)
                # 特征取值去重
                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
                    # 2.b 计算误差率
                    error = sum(w[y != pred])
                    
                    # 如果分类误差大于0.5,则进行正负预测翻转
                    # 例如 error = 0.6 => (1 - error) = 0.4
                    if error > 0.5:
                        error = 1 - error
                        p = -1

                    # 一旦获得最小误差则保存相关参数配置
                    if error < min_error:
                        estimator.label = p
                        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))
            # 初始化所有预测值为1
            preds = np.ones(np.shape(y))
            # 获取所有小于阈值的负类索引
            negative_idx = (estimator.label * X[:, estimator.feature_index] < estimator.label * 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()
        return y_pred

代码实现了一个 Adaboost 类,用于训练和预测分类任务中的数据。实现方式完全按照文章《AdaBoost基本原理》中的所述步骤。

1. 类定义与初始化

class Adaboost:
    def __init__(self, n_estimators=5):
        self.n_estimators = n_estimators

这里定义了 Adaboost 类,并且在初始化方法 __init__ 中定义了弱分类器的数量,即 n_estimators,默认为 5。这意味着 AdaBoost 会使用 5 个弱分类器进行训练。

2. 拟合方法 fit

fit 方法用于训练 AdaBoost 模型:

def fit(self, X, y):
    m, n = X.shape
    w = np.full(m, (1/m))
    self.estimators = []
  • 初始化样本权重 w 为均匀分布 1 m \frac{1}{m} m1
  • 初始化基分类器列表 self.estimators,用来保存每一轮训练的弱分类器。
主循环:对每个弱分类器进行训练
for _ in range(self.n_estimators):
    estimator = DecisionStump()
    min_error = float('inf')

循环 self.n_estimators 次,每次训练一个弱分类器。在每一轮中:

  • estimator 表示一个弱分类器,这里假设定义了 DecisionStump 类用于构建决策树桩
  • min_error 用于记录当前轮次中的最小分类误差,初始值设为无穷大。
遍历特征和阈值,寻找最佳分割点
for i in range(n):
    values = np.expand_dims(X[:, i], axis=1)
    unique_values = np.unique(values)
    for threshold in unique_values:
        p = 1
        pred = np.ones(np.shape(y))
        pred[X[:, i] < threshold] = -1
        error = sum(w[y != pred])

对每个特征进行遍历,并尝试每个特征值作为分类阈值:

  • pred 初始化为 1,将所有小于阈值的样本预测为 -1。
  • error 计算预测结果和真实标签的误差。
判断是否需要反转预测方向
if error > 0.5:
    error = 1 - error
    p = -1

如果误差大于 0.5,则反转预测方向,将正类和负类交换(即 p = -1)。

保存当前最优的分类器参数
if error < min_error:
    estimator.label = p
    estimator.threshold = threshold
    estimator.feature_index = i
    min_error = error

如果当前误差比 min_error 更小,则更新 estimator 的最佳参数:label(方向),threshold(阈值),和 feature_index(特征索引)。

计算弱分类器权重
estimator.alpha = 0.5 * np.log((1.0 - min_error) / (min_error + 1e-9))

计算当前弱分类器的权重 alpha,权重与分类误差成反比。误差越小,权重越大。

更新样本权重
preds = np.ones(np.shape(y))
negative_idx = (estimator.label * X[:, estimator.feature_index] < estimator.label * estimator.threshold)
preds[negative_idx] = -1
w *= np.exp(-estimator.alpha * y * preds)
w /= np.sum(w)

根据弱分类器的结果更新样本权重,预测错误的样本权重会增加。通过重新归一化 w 确保权重之和为 1。

3. 保存当前弱分类器

self.estimators.append(estimator)

将当前弱分类器添加到 self.estimators 列表中。

4. 预测方法 predict

def predict(self, X):
    m = len(X)
    y_pred = np.zeros((m, 1))
    for estimator in self.estimators:
        predictions = np.ones(np.shape(y_pred))
        negative_idx = (estimator.label * X[:, estimator.feature_index] < estimator.label * estimator.threshold)
        predictions[negative_idx] = -1
        y_pred += estimator.alpha * predictions
    y_pred = np.sign(y_pred).flatten()
    return y_pred
  • 初始化预测结果 y_pred 为 0。
  • 对每个弱分类器的预测结果进行加权累加,最终根据正负符号返回预测结果。

总结

这个 Adaboost 类实现了一个基于决策树桩的 AdaBoost 算法。通过循环迭代,每一轮选择一个最佳弱分类器,并根据误差更新权重,逐步提高分类效果。最终预测结果为所有弱分类器的加权和。

;