机器学习算法原理实现——adaboost,三个臭皮匠顶个诸葛亮

发布时间 2023-09-16 12:57:47作者: bonelee
adaboost算法的基本原理是什么?举一个简单的例子说明呢

AdaBoost(Adaptive Boosting)是一种集成学习方法,其基本原理是结合多个弱学习器来构建一个强学习器。AdaBoost的工作方式如下:

  1. 权重初始化:给定一个训练数据集,首先为每个训练样本分配一个权重,开始时这些权重都是相等的。

  2. 训练弱学习器:在每个迭代中,使用权重调整的数据集来训练一个弱学习器。

  3. 计算错误率:使用当前的弱学习器对数据集进行预测,然后计算加权的错误率。

  4. 计算学习器权重:基于当前弱学习器的错误率计算其权重,误差率较低的弱学习器获得较大的权重。

  5. 更新样本权重:增加那些被错误分类的样本的权重,并减少那些被正确分类的样本的权重。

  6. 迭代:重复上述步骤,直到满足迭代次数或错误率达到预定的阈值。

  7. 组合弱学习器:所有的弱学习器以其权重为基础进行组合,形成一个强学习器。

其实本质就是“三个臭皮匠(ABC),顶个诸葛亮”:

假设A对1/3样本预测正确擅长,则他对这部分样本的预测权重会增加,B和C对剩下的样本预测正确,则另外的样本权重会增加,则adaboost本质上就是集成了三人的智慧,让他们三个人的长处和优势都发挥出来!

 

说下里面的adaboost公式推导和计算流程:

 

代码实现:

import numpy as np


### 定义决策树桩类
### 作为AdaBoost弱分类器
class DecisionStump:
    def __init__(self):
        # 基于划分阈值决定样本分类为1还是-1
        self.label = 1
        # 特征索引
        self.feature_index = None
        # 特征划分阈值
        self.threshold = None
        # 指示分类准确率的值
        self.alpha = None

### 定义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
    
# 导入数据划分模块
from sklearn.model_selection import train_test_split
# 导入模拟二分类数据生成模块
from sklearn.datasets import make_blobs
# 导入准确率计算函数
from sklearn.metrics import accuracy_score
# 生成模拟二分类数据集
X, y =  make_blobs(n_samples=150, n_features=2, centers=2, cluster_std=1.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)
# 创建Adaboost模型实例
clf = Adaboost(n_estimators=5)
# 模型拟合
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)


# 导入AdaBoostClassifier模块
from sklearn.ensemble import AdaBoostClassifier
# 创建模型实例
clf_ = AdaBoostClassifier(n_estimators=5, random_state=0)
# 模型拟合
clf_.fit(X_train, y_train)
# 测试集预测
y_pred_ = clf_.predict(X_test)
# 计算分类准确率
accuracy = accuracy_score(y_test, y_pred_)
print("Accuracy of AdaBoost by sklearn:", accuracy)

  

输出:

Accuracy of AdaBoost by numpy: 0.9777777777777777
Accuracy of AdaBoost by sklearn: 0.9777777777777777
 
解释:

 

举几个例子说明代码运行原理:

 

 

 

 

其中体现样本权重会对预测误差造成影响的代码:

 

也就是说,让样本权重大的分类正确才可能使得该误差变小。从而达到了“三个臭皮匠,顶个诸葛亮”的效果!

这样说,adaboost还是非常优雅的!