目录
- AdaBoost 算法核心思想回顾
- 从零开始实现 AdaBoost
- 代码实现
- 代码逐行解析
- 完整可运行示例
- 使用 Scikit-learn 实现 AdaBoost
- 代码实现
- 参数解释
- 完整可运行示例
- 两种方法的比较
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)
代码逐行解析
__init__: 初始化函数,设定要训练的弱分类器数量n_estimators,并创建两个列表estimators和alphas用于存储它们。fit(训练过程):- 标签转换: 将
y从{0, 1}转换为{-1, 1},这简化了权重更新公式的计算。 - 权重初始化: 所有样本的初始权重相等,为
1/n_samples。 - 主循环: 循环
n_estimators次。- 训练弱分类器: 使用当前样本权重
sample_weight训练一个决策树桩。scikit-learn的DecisionTreeClassifier支持样本权重。 - 计算错误率: 计算当前分类器在加权样本上的错误率。
- 计算
alpha: 使用公式alpha = 0.5 * log((1-error)/error)计算该分类器的权重,错误率越低,alpha越大。 - 更新样本权重: 根据公式
w_i = w_i * exp(-alpha * y_i * h_i(x_i))更新权重,被正确分类的样本权重会减小,被错误分类的样本权重会增大,最后进行归一化。 - 存储结果: 将训练好的弱分类器和其
alpha值保存起来。
- 训练弱分类器: 使用当前样本权重
- 标签转换: 将
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_rate、algorithm 选择等多种参数,支持 partial_fit(增量训练)等高级功能。 |
| 适用场景 | 机器学习课程作业、算法面试、学术研究。 | 数据科学项目、工业界应用、任何需要快速构建模型的场景。 |
从零开始实现是为了“知其所以然”,而使用 scikit-learn 是为了“高效解决问题”,强烈建议在掌握原理后,在实际项目中优先使用 scikit-learn。
- 我们首先回顾了 AdaBoost 的核心思想:迭代训练弱分类器,关注错误样本,并进行加权投票。
- 我们提供了一个从零开始的 Python 实现,详细解释了训练和预测的每一步,这对于深入理解算法非常有帮助。
- 我们展示了如何使用
scikit-learn库轻松、高效地实现 AdaBoost,并介绍了其关键参数。 - 我们对两种方法进行了比较,明确了它们各自的应用场景。
希望这份详细的指南能帮助你全面掌握 AdaBoost 算法!
