AdaBoost:自适应提升算法(Numpy手写代码实战)

AdaBoost算法是通过改变训练样本权重来学习多个弱分类器并线性组合成强分类器的Boosting算法。

Boosting方法要解答的两个关键问题:

一是在训练过程中如何改变训练样本的权重或者概率分布;

二是如何将多个弱分类器组合成一个强分类器。

AdaBoost的做法是:一是提高前一轮被弱分类器分类错误的样本的权重,而降低分类正确的样本的权重;而是对多个弱分类器进行线性组合,提高分类效果好的弱分类器的权重,降低分类误差率高的弱分类器的权重。

1. 导入需要的包

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

2. 生成数据集并查看

X, y = make_blobs(n_samples=150, n_features=2, centers=2, cluster_std=1.2, random_state=40)
# 将标签转换为1/-1
y_=y.copy()
# 把数据集中的标签为0的数据转换成标签为-1
y_[y_==0]=-1
y_.astype(float)
# 训练/测试数据集划分
X_train, X_test, y_train, y_test = train_test_split(X, y_, test_size=0.3, random_state=43)
colors={1:'red',-1:'blue'}
plt.scatter(X[:,0],X[:,1],c=pd.Series(y_).map(colors))
plt.show()

3. 定义决策树桩类作为Adaboost的弱分类器

单层决策树(decision stump,也称决策树桩)是一种简单的决策树。由于这棵树只有一次分裂过程,因此它实际上就是一个树桩。

class decision_stump:
    def __init__(self):
         # 基于划分阈值决定样本分类为1还是-1
         self.label=1
         # 特征索引
         self.feature_index=None
         # 特征划分阈值
         self.threshold=None
         # 指示分类准确率
         self.alpha=None

4. 定义Adaboost算法类

这里叙述一下Adaboost算法。

假设给定一个二类分类的训练数据集:
T = { ( x 1 , y 1 )    ,    ( x 2 , y 2 )    ,    ⋯    ,    ( x N , y N ) } T=\{(x_1,y_1)\;,\;(x_2,y_2)\;,\;\cdots\;,\;(x_N,y_N)\} T={(x1,y1),(x2,y2),,(xN,yN)}
其中,每个样本点由实例与标记组成。实例 x i ∈ χ ⊆ R n x_i\in \chi \subseteq \R^n xiχRn,标记 y i ∈ Y = { − 1    ,    + 1 } y_i\in Y=\{-1\;,\;+1\} yiY={1,+1} χ \chi χ是实例空间, Y Y Y是标记集合。Adaboost利用以下算法,从训练数据中学习一系列弱分类器或基本分类器,并将这些弱分类器线性组合成一个强分类器。

算法步骤如下:

输入:训练数据集 T = { ( x 1 , y 1 )    ,    ( x 2 , y 2 )    ,    ⋯    ,    ( x N , y N ) T=\{(x_1,y_1)\;,\;(x_2,y_2)\;,\;\cdots\;,\;(x_N,y_N) T={(x1,y1),(x2,y2),,(xN,yN),其中 x i ∈ χ ⊆ R n x_i\in \chi \subseteq \R^n xiχRn y i ∈ Y = { − 1 , + 1 } y_i\in Y=\{-1,+1\} yiY={1,+1};弱学习算法;

输出:最终分类器 G ( x ) G(x) G(x)

(1)初始化训练数据的权值分布:
D 1 = ( ω 11    ,    ⋯    ,    ω 1 i    ,    ⋯    ,    ω 1 n )    ,    ω 1 i = 1 N    ,    i = 1 , 2 , ⋯   , N D_1=(\omega_{11}\;,\;\cdots\;,\;\omega_{1i}\;,\;\cdots\;,\;\omega_{1n})\;,\;\omega_{1i}=\frac{1}{N}\;,\;i=1,2,\cdots,N D1=(ω11,,ω1i,,ω1n),ω1i=N1,i=1,2,,N
(2)对 m = 1 , 2 , ⋯   , M m=1,2,\cdots,M m=1,2,,M

  1. 使用具有权值分布 D m D_m Dm的训练数据集学习,得到基本分类器:
    G m ( x ) : χ → { − 1 , + 1 } G_m(x):\chi \rightarrow\{-1,+1\} Gm(x):χ{1,+1}

  2. 计算 G m ( x ) G_m(x) Gm(x)在训练数据集上的分类误差率:
    e m = ∑ i = 1 N P ( G m ( x i ) ≠ y i ) = ∑ i = 1 N ω m i I ( G m ( x i ) ≠ y i ) e_m=\sum_{i=1}^NP(G_m(x_i)\neq y_i)=\sum_{i=1}^N\omega_{mi}I(G_m(x_i)\neq y_i) em=i=1NP(Gm(xi)=yi)=i=1NωmiI(Gm(xi)=yi)

  3. 计算 G m ( x ) G_m(x) Gm(x)的系数:
    α m = 1 2 log ⁡ 1 − e m e m \alpha_m=\frac{1}{2}\log\frac{1-e_m}{e_m} αm=21logem1em

  4. 更新训练数据集的权值分布:
    D m + 1 = ( ω m + 1 , 1    ,    ⋯    ,    ω m + 1 , i    ,    ⋯    ,    ω m + 1 , N D_{m+1}=(\omega_{m+1,1}\;,\;\cdots\;,\;\omega_{m+1,i}\;,\;\cdots\;,\;\omega_{m+1,N} Dm+1=(ωm+1,1,,ωm+1,i,,ωm+1,N
    ω m + 1 , i = ω m i Z m exp ⁡ ( − α m y i G m ( x i ) )    ,    i = 1 , 2 , ⋯   , N \omega_{m+1,i}=\frac{\omega_{mi}}{Z_m}\exp(-\alpha_my_iG_m(x_i))\;,\;i=1,2,\cdots,N ωm+1,i=Zmωmiexp(αmyiGm(xi)),i=1,2,,N
    这里, Z m Z_m Zm是规范化因子:
    Z m = ∑ i = 1 N ω m i exp ⁡ ( − α m y i G m ( x ) ) Z_m=\sum_{i=1}^N\omega_{mi}\exp(-\alpha_my_iG_m(x)) Zm=i=1Nωmiexp(αmyiGm(x))
    它使得 D m + 1 D_{m+1} Dm+1成为一个概率分布。

(3)构建基本分类器的线性组合:
f ( x ) = ∑ m = 1 M α m G m ( x ) f(x)=\sum_{m=1}^M\alpha_mG_m(x) f(x)=m=1MαmGm(x)
得到最终分类器:
G ( x ) = s i g n ( f ( x ) ) = s i g n ( ∑ m = 1 M α m G m ( x ) ) G(x)=sign(f(x))=sign(\sum_{m=1}^M\alpha_mG_m(x)) G(x)=sign(f(x))=sign(m=1MαmGm(x))

弱分类器的个数

def __init__(self,n_estimators=10):
     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 = decision_stump()
        # 设定一个最小化误差
        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.lable=p
                   estimator.threshold=threshold
                   estimator.feature_index=i
                   min_error=error

计算基分类器的权重

estimator.alpha=0.5*np.log((1-min_error)/(min_error+1e-9))
# 初始化所有预测值为1
preds=np.ones(np.shape(y))
# 获取所有小于阈值的负类索引
negative_idx=(estimator.lable*X[:,estimator.feature_index]<estimator.lable*estimator.threshold)
# 将负类设为 '-1'
preds[negative_idx]=-1

更新样本权重

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模型实例
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)

运行结果

0.9777777777777777

完整版代码

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
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()
# 把数据集中的标签为0的数据转换成标签为-1
y_[y_==0]=-1
y_.astype(float)
# 训练/测试数据集划分
X_train, X_test, y_train, y_test = train_test_split(X, y_, test_size=0.3, random_state=43)
colors={1:'red',-1:'blue'}
plt.scatter(X[:,0],X[:,1],c=pd.Series(y_).map(colors))
plt.show()

# 定义决策树桩类作为Adaboost的弱分类器,树桩,只有一个分类节点
class decision_stump:
    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=10):
        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 = decision_stump()
            # 设定一个最小化误差
            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.lable=p
                        estimator.threshold=threshold
                        estimator.feature_index=i
                        min_error=error

            # 2.c 计算基分类器的权重
            estimator.alpha=0.5*np.log((1-min_error)/(min_error+1e-9))
            # 初始化所有预测值为1
            preds=np.ones(np.shape(y))
            # 获取所有小于阈值的负类索引
            negative_idx=(estimator.lable*X[:,estimator.feature_index]<estimator.lable*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模型实例
clf=Adaboost(n_estimators=10)
# 模型拟合
clf.fit(X_train,y_train)
# 模型预测
y_pred=clf.predict(X_test)
# 计算模型预测准确率
accuracy=accuracy_score(y_test,y_pred)
print(accuracy)
  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旅途中的宽~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值