杰瑞科技汇

如何用Python实现AdaBoost算法代码?

目录

  1. AdaBoost 算法核心思想回顾
  2. 从零开始实现 AdaBoost
    • 代码实现
    • 代码逐行解析
    • 完整可运行示例
  3. 使用 Scikit-learn 实现 AdaBoost
    • 代码实现
    • 参数解释
    • 完整可运行示例
  4. 两种方法的比较

AdaBoost 算法核心思想回顾

AdaBoost (Adaptive Boosting) 是一种非常经典的集成学习算法,它的核心思想是:

  • 迭代训练弱分类器:AdaBoost 不会一次性训练一个强大的模型,而是通过多轮迭代,每一轮都训练一个“弱分类器”(Weak Classifier),其性能仅比随机猜测稍好(准确率略高于50%)。
  • 关注错误样本:在每一轮训练中,它会增加上一轮被错误分类的样本的权重,这样,新的弱分类器就会被“激励”去重点关注那些之前被分错的样本。
  • 加权投票:最终的强分类器是由所有弱分类器加权组合而成的,分类效果好的弱分类器(即错误率低的)会被赋予更高的权重,在最终决策中拥有更大的话语权。

这个过程可以形象地理解为:一个班级里,老师通过多次小测验来评定学生,每次测验后,老师会更关注那些上次答错题的学生,下次出题时会侧重考察他们上次的知识盲区,综合所有测验的成绩,并且给那些一直表现很好的学生的成绩更高的权重,来给出最终的总评。


方法一:从零开始实现 AdaBoost

这个实现将帮助我们理解 AdaBoost 的内部工作原理,我们将使用最简单的决策树桩(Decision Stump,即深度为1的决策树)作为弱分类器。

代码实现

import numpy as np
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import make_classification
from sklearn.metrics import accuracy_score
class AdaBoostFromScratch:
    def __init__(self, n_estimators=50):
        """
        初始化 AdaBoost 模型
        参数:
        n_estimators (int): 弱分类器的数量
        """
        self.n_estimators = n_estimators
        self.estimators = []      # 存储训练好的弱分类器
        self.alphas = []          # 存储每个弱分类器的权重
    def _calculate_error(self, y, y_pred, sample_weights):
        """
        计算加权错误率
        """
        # 找出被错误分类的样本
        misclassified = (y != y_pred)
        # 计算错误样本的权重之和
        error = np.sum(sample_weights * misclassified)
        return error
    def _calculate_alpha(self, error):
        """
        计算弱分类器的权重 alpha
        """
        # 避免错误率为0或1,导致log计算错误或无穷大
        error = np.clip(error, 1e-10, 1 - 1e-10)
        alpha = 0.5 * np.log((1 - error) / error)
        return alpha
    def _update_sample_weights(self, sample_weights, alpha, y, y_pred):
        """
        更新样本权重
        """
        # 计算权重更新因子
        # 正确分类的样本:exp(-alpha),错误分类的样本:exp(alpha)
        update_factor = np.exp(-alpha * y * y_pred)
        # 归一化,使权重之和为1
        sample_weights = sample_weights * update_factor
        sample_weights /= np.sum(sample_weights)
        return sample_weights
    def fit(self, X, y):
        """
        训练 AdaBoost 模型
        """
        # 将标签 y 转换为 -1 和 1,方便计算
        y = np.where(y == 0, -1, 1)
        n_samples = X.shape[0]
        # 1. 初始化样本权重为均匀分布
        sample_weights = np.ones(n_samples) / n_samples
        for _ in range(self.n_estimators):
            # 2. 训练一个弱分类器(决策树桩)
            estimator = DecisionTreeClassifier(max_depth=1)
            estimator.fit(X, y, sample_weight=sample_weights)
            # 3. 用当前弱分类器进行预测
            y_pred = estimator.predict(X)
            # 4. 计算加权错误率
            error = self._calculate_error(y, y_pred, sample_weights)
            # 如果错误率大于等于0.5,说明这个分类器比随机猜还差,停止本轮循环
            if error >= 0.5:
                break
            # 5. 计算该弱分类器的权重 alpha
            alpha = self._calculate_alpha(error)
            # 6. 更新样本权重
            sample_weights = self._update_sample_weights(sample_weights, alpha, y, y_pred)
            # 7. 保存弱分类器和其权重
            self.estimators.append(estimator)
            self.alphas.append(alpha)
            # 如果错误率为0,完美分类,可以提前停止
            if error == 0:
                break
    def predict(self, X):
        """
        使用训练好的模型进行预测
        """
        # 初始化预测结果为0
        predictions = np.zeros(X.shape[0])
        # 对每个弱分类器进行预测,并根据其权重 alpha 进行累加
        for alpha, estimator in zip(self.alphas, self.estimators):
            predictions += alpha * estimator.predict(X)
        # 根据累加值的符号决定最终类别 (-1 或 1)
        final_predictions = np.sign(predictions)
        # 将预测结果从 -1, 1 转换回 0, 1
        return np.where(final_predictions == -1, 0, 1)

代码逐行解析

  1. __init__: 初始化函数,设定要训练的弱分类器数量 n_estimators,并创建两个列表 estimatorsalphas 用于存储它们。
  2. fit (训练过程):
    • 标签转换: 将 y{0, 1} 转换为 {-1, 1},这简化了权重更新公式的计算。
    • 权重初始化: 所有样本的初始权重相等,为 1/n_samples
    • 主循环: 循环 n_estimators 次。
      • 训练弱分类器: 使用当前样本权重 sample_weight 训练一个决策树桩。scikit-learnDecisionTreeClassifier 支持样本权重。
      • 计算错误率: 计算当前分类器在加权样本上的错误率。
      • 计算 alpha: 使用公式 alpha = 0.5 * log((1-error)/error) 计算该分类器的权重,错误率越低,alpha 越大。
      • 更新样本权重: 根据公式 w_i = w_i * exp(-alpha * y_i * h_i(x_i)) 更新权重,被正确分类的样本权重会减小,被错误分类的样本权重会增大,最后进行归一化。
      • 存储结果: 将训练好的弱分类器和其 alpha 值保存起来。
  3. predict (预测过程):
    • 加权投票: 对于新数据,让所有弱分类器进行预测,将每个预测结果乘以其对应的 alpha 权重,然后全部累加起来。
    • 符号决策: 如果累加结果为正,则预测为类别 1;如果为负,则预测为类别 0,这相当于一个加权投票,alpha 值越大的分类器对最终结果影响越大。

完整可运行示例

# 生成示例数据
X, y = make_classification(
    n_samples=1000, 
    n_features=20, 
    n_informative=10, 
    n_redundant=5, 
    n_classes=2, 
    random_state=42
)
# 分割数据集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 创建并训练我们自己的 AdaBoost 模型
# n_estimators=50 表示使用50个弱分类器
adaboost_scratch = AdaBoostFromScratch(n_estimators=50)
adaboost_scratch.fit(X_train, y_train)
# 在测试集上进行预测
y_pred_scratch = adaboost_scratch.predict(X_test)
# 评估模型性能
accuracy_scratch = accuracy_score(y_test, y_pred_scratch)
print(f"从零实现的 AdaBoost 模型准确率: {accuracy_scratch:.4f}")
# 可以查看训练了多少个弱分类器
print(f"实际训练的弱分类器数量: {len(adaboost_scratch.estimators)}")

方法二:使用 Scikit-learn 实现 AdaBoost

在实际应用中,我们几乎总是使用 scikit-learn 这样的成熟库,因为它经过了高度优化,稳定且易于使用。

代码实现

from sklearn.ensemble import AdaBoostClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 生成与上面相同的数据集,以便公平比较
X, y = make_classification(
    n_samples=1000, 
    n_features=20, 
    n_informative=10, 
    n_redundant=5, 
    n_classes=2, 
    random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 创建并训练 scikit-learn 的 AdaBoost 模型
# estimator='SAMME.R' 是一种更现代的算法,通常性能更好
# n_estimators=50 表示使用50个弱分类器
adaboost_sklearn = AdaBoostClassifier(
    n_estimators=50,
    learning_rate=1.0, # 学习率,与我们的 alpha 相关
    random_state=42
)
adaboost_sklearn.fit(X_train, y_train)
# 在测试集上进行预测
y_pred_sklearn = adaboost_sklearn.predict(X_test)
# 评估模型性能
accuracy_sklearn = accuracy_score(y_test, y_pred_sklearn)
print(f"Scikit-learn 的 AdaBoost 模型准确率: {accuracy_sklearn:.4f}")

参数解释

  • estimator: 弱分类器,默认为 DecisionTreeClassifier(max_depth=1),也就是决策树桩,你也可以指定其他任何分类器,只要它的参数 sample_weight 可用。
  • n_estimators: 弱分类器的最大数量,如果模型在达到此数量之前已完美拟合,则训练会提前停止。
  • learning_rate (学习率): 它是一个缩放因子,用于乘以每个弱分类器的权重(即我们的 alpha),较小的 learning_rate 意味着需要更多的弱分类器来达到同样的效果,但有时能获得更好的泛化性能。
  • algorithm: 有两种实现算法:
    • 'SAMME': 是 AdaBoost 的离散多类别版本,它适用于分类标签为 {0, 1, ..., K-1} 的情况,我们的从零实现就遵循这个逻辑。
    • 'SAMME.R': 是 SAMME 的改进版,使用概率输出进行更新,通常收敛更快,效果更好,这是 scikit-learn 的默认算法。

两种方法的比较

特性 从零开始实现 Scikit-learn 实现
目的 学习原理,深入理解 AdaBoost 的每一步计算过程。 实际应用,快速、高效地解决实际问题。
代码复杂度 较高,需要手动实现权重更新、alpha 计算等核心逻辑。 非常低,几行代码即可完成,封装得非常好。
性能和稳定性 较低,没有经过优化,可能在边界情况(如 error=0)处理不当。 ,经过充分测试和优化,稳定可靠,处理各种边界情况。
功能丰富度 有限,通常只实现核心逻辑。 丰富,提供了 learning_ratealgorithm 选择等多种参数,支持 partial_fit(增量训练)等高级功能。
适用场景 机器学习课程作业、算法面试、学术研究。 数据科学项目、工业界应用、任何需要快速构建模型的场景。

从零开始实现是为了“知其所以然”,而使用 scikit-learn 是为了“高效解决问题”,强烈建议在掌握原理后,在实际项目中优先使用 scikit-learn


  • 我们首先回顾了 AdaBoost 的核心思想:迭代训练弱分类器,关注错误样本,并进行加权投票。
  • 我们提供了一个从零开始的 Python 实现,详细解释了训练和预测的每一步,这对于深入理解算法非常有帮助。
  • 我们展示了如何使用 scikit-learn 库轻松、高效地实现 AdaBoost,并介绍了其关键参数。
  • 我们对两种方法进行了比较,明确了它们各自的应用场景。

希望这份详细的指南能帮助你全面掌握 AdaBoost 算法!

分享:
扫描分享到社交APP
上一篇
下一篇