24/8/11算法笔记AdaBoost二分类算法进阶

AdaBoost(Adaptive Boosting)是一种流行的集成学习算法,特别适用于二分类问题。它通过组合多个弱分类器来构建一个强分类器,提高模型的预测性能。以下是 AdaBoost 算法的关键步骤和特点:

  1. 初始化:为训练数据集中的每个样本分配初始权重,通常所有样本的初始权重都是相等的。

  2. 迭代训练弱分类器:在每一轮迭代中,AdaBoost 训练一个弱分类器(通常是决策树桩或感知机)。每个弱分类器尝试基于当前样本权重进行预测。

  3. 计算错误率:计算当前弱分类器的错误率,即它错误分类的样本所占的比例。

  4. 计算弱分类器的权重:根据错误率计算弱分类器的权重(alpha)。错误率越低,分配给该弱分类器的权重越大。

  5. 更新样本权重:根据当前弱分类器的性能更新所有样本的权重。被当前弱分类器错误分类的样本的权重会增加,而正确分类的样本的权重会减少。

  6. 归一化:调整总权重,使得所有样本权重的总和为1。

  7. 组合弱分类器:将所有弱分类器的预测结果结合起来,形成最终的强分类器。对于二分类问题,通常是通过加权多数投票来实现的。

  8. 终止条件:达到预定的迭代次数,或者当添加更多的弱分类器不再显著提高模型性能时停止。

AdaBoost 算法的特点包括:

  • 适应性强:AdaBoost 能够适应数据的变化,通过不断调整样本权重来关注难以分类的样本。
  • 易于实现:AdaBoost 算法相对容易实现,并且可以应用于各种类型的弱分类器。
  • 对噪声和异常值敏感:AdaBoost 可能会过度关注被错误分类的样本,包括噪声和异常值。
  • 容易解释:AdaBoost 构建的模型相对容易解释,因为它是基于一系列简单的弱分类器。
  • 提高性能:AdaBoost 通过集成多个弱分类器,通常能够显著提高模型的预测性能。

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1,1,50)

y = np.exp(x)
print('--------------',np.exp(0))
print('**************',np.exp(-0.2))
print('++++++++++++++',np.exp(0.3))
plt.plot(x,np.full(50,fill_value=1))
plt.plot(x,y)

手撕代码

创建数据
import numpy as np
from sklearn.ensemble import AdaBoostClassifier
import graphviz
from sklearn import tree

X = np.arange(10).reshape(-1,1)#二维,机器学习要求数据必须是二维

y = np.array([1,1,1,-1,-1,-1,1,1,1,-1])
display(X,y)

使用AdaBoost建模
#SAMME 表示构建树的时候,采用相同的裂分方式
model = AdaBoostClassifier(n_estimators=3,algorithm='SAMME')

model.fit(X,y)

y_=model.predict(X)
display(y,y_)#准确率100%

第一棵树可视化
print('第一棵树是什么',model[0])
dot_data = tree.export_graphviz(model[0],filled=True,rounded=True)
graphviz.Source(dot_data)

第二第二棵树

dot_data = tree.export_graphviz(model[1],filled=True,rounded=True)
graphviz.Source(dot_data)

第三棵树
dot_data = tree.export_graphviz(model[2],filled=True,rounded=True)
graphviz.Source(dot_data)

第一棵树代码构建

w1 = np.full(shape = 10,fill_value=0.1)#初始的样本权重

cond = y ==1#类别1条件
p1 = w1[cond].sum()
p2 = 1-p1
display(p1,p2)

gini1 = p1*(1-p1)+p2*(1-p2)
print('方式1计算gini系数',gini1)

gini2 = 1-p1**2-p2**2
print('方式2计算gini系数',gini2)

拆分条件计算
gini_result = []
best_split = {}#最佳裂分条件,X[0]<=2.5
lower_gini = 1#比较

for i in range(len(X)-1):
    split = X[i:i+2].mean()#裂分条件
    cond = (X<=split).ravel()#变成一维的,左边数据
    left = y[cond]
    right = y[~cond]#取反
    
    gini_left = 0
    gini_right = 0
    for j in np.unique(y):#y表示类别
        p_left = (left ==j).sum()/left.size#计算左边某个类别的概率
        gini_left = p_left*(1-p_left)
        p_right = (right ==j).sum()/right.size
        gini_right = p_right*(1-p_right)
        
    #左右两边的gini系数合并
    left_p = cond.sum()/cond.size
    right_p = 1-left_p
    
    gini = gini_left*left_p+gini_right*right_p
    gini_result.append(gini)
    if gini<lower_gini:
        lower_gini = gini
        best_split.clear()
        best_split['X[0]<='] = split
print(gini_result)
print('最佳裂分条件是')
print(best_split)

计算误差
y1_ = model[0].predict(X)#某个大夫问诊情况

error1=(y!=y1_).mean()
error1
0.3
from sklearn.metrics import accuracy_score

accuracy_score(y,y1_)
0.7
accuracy = (y==y1_).mean()
accuracy

0.7

计算第一个弱学习器(决策树,大夫)的权重

alpha_1=1/2*np.log((1-error1)/error1)
alpha_1
0.42364893019360184
alpha_1=1/2*np.log(accuracy/(1-accuracy))
alpha_1
0.4236489301936017
更新样本权重
w1#没有训练学习,所以都一样

#上一次权重的基础上进行更新

#y表示真实的目标值
#ht[X]表示当前弱学习器预测的结果
w2 = w1*np.exp(-alpha_1*y*y1_)
w2 = w2/w2.sum()#权重归一化
display(w1,w2)
display(y,y1_)

#算法预测所悟的样本

第二棵树代码构建
y

cond = y ==-1
w2[cond].sum()
0.2857142857142857
cond2 =y ==1
np.round(w2[cond2].sum(),3)

gini系数计算

print(w2)

cond = y == 1#类别1条件
p1 = w2[cond].sum()   #使用新的样本权重分布
p2 = 1-p1
display(p1,p2)

gini1 = p1*(1-p1)+p2*(1-p2)
print('方式1计算gini系数',gini1)

gini2 = 1-p1**2-p2**2
print('方式2计算gini系数',gini2)

拆分条件的计算
gini_result = []
best_split = {}#最佳裂分条件,X[0]<=2.5
lower_gini = 1#比较

for i in range(len(X)-1):
    split = X[i:i+2].mean()#裂分条件
    cond = (X<=split).ravel()#变成一维的,左边数据
    left = y[cond]
    right = y[~cond]#取反
    
    # left_p = cond.sum()/cond.size  #这种方式适用于每个样本权重一样
    left_p = w2[cond]/w2[cond].sum() #归一化,左侧每个样本在自己组内的概率
    right_p = w2[~cond]/w2[~cond].sum()#归一化右侧每个样本在自己组内概率
    
    #左右两个边的gini系数
    gini_left = 0
    gini_right = 0
    for j in np.unique(y):#y表示类别
        cond_left = left ==j #左侧某个类别
        p1 = left_p[cond_left].sum()#左侧某个类别的概率
        gini_left += p1*(1-p1)
        
        cond_right = right ==j #右侧某个类别
        p2 = right_p[cond_right].sum()#右侧某个类别的概率
        gini_right += p2*(1-p2)
        
    #左右两边的gini系数合并

    p1=cond.sum()/cond.size #左侧划分数据所占比例
    p2 = 1 -p1#右侧划分数据所占比例
    gini = gini_left*p1+gini_right*p2
    gini_result.append(gini)
    if gini<lower_gini:
        lower_gini = gini
        best_split.clear()
        best_split['X[0]<='] = split
print(gini_result)
print('最佳裂分条件是')
print(best_split)

计算误差
y2_ = model[1].predict(X)#某个大夫问诊情况

error2=(y!=y2_).mean()
error2
0.3
error2 = ((y!=y2_)*w2).sum()#考虑权重,因为即使错了,误差也小
error2

0.21428571428571427

计算第二个弱学习器权重
alpha_2=1/2*np.log((1-error2)/error2)
alpha_2
0.6496414920651304
#上一次权重的基础上进行更新

#y表示真实的目标值
#ht[X]表示当前弱学习器预测的结果
w3 = w2*np.exp(-alpha_2*y*y2_)
w3 = w3/w3.sum()     #权重归一化
w3

第三棵树代码构建
gini系数
cond = y ==1#类别1条件
p1 = w3[cond].sum()   #使用新的样本权重分布
p2 = 1-p1
display(p1,p2)

gini1 = p1*(1-p1)+p2*(1-p2)
print('方式1计算gini系数',gini1)

gini2 = 1-p1**2-p2**2
print('方式2计算gini系数',gini2)

拆分条件计算
gini_result = []
best_split = {}#最佳裂分条件,X[0]<=2.5
lower_gini = 1#比较

for i in range(len(X)-1):
    split = X[i:i+2].mean()#裂分条件
    cond = (X<=split).ravel()#变成一维的,左边数据
    left = y[cond]
    right = y[~cond]#取反
    
    # left_p = cond.sum()/cond.size  #这种方式适用于每个样本权重一样
    left_p = w3[cond]/w3[cond].sum() #归一化,左侧每个样本在自己组内的概率
    right_p = w3[~cond]/w3[~cond].sum()#归一化右侧每个样本在自己组内概率
    
    #左右两个边的gini系数
    gini_left = 0
    gini_right = 0
    for j in np.unique(y):#y表示类别
        cond_left = left ==j #左侧某个类别
        p1 = left_p[cond_left].sum()#左侧某个类别的概率
        gini_left += p1*(1-p1)
        
        cond_right = right ==j #右侧某个类别
        p2 = right_p[cond_right].sum()#右侧某个类别的概率
        gini_right += p2*(1-p2)
        
    #左右两边的gini系数合并

    p1=cond.sum()/cond.size #左侧划分数据所占比例
    p2 = 1-p1#右侧划分数据所占比例
    gini = gini_left*p1+gini_right*p2
    gini_result.append(gini)
    if gini<lower_gini:
        lower_gini = gini
        best_split.clear()
        best_split['X[0]<='] = split
print(gini_result)
print('最佳裂分条件是')
print(best_split)

计算误差
y3_ = model[2].predict(X)#某个大夫问诊情况

error3=((y!=y3_)*w3).sum()
error3

计算第三棵树的权重
alpha_3=1/2*np.log((1-error3)/error3)
alpha_3
0.752038698388137
更新权重
#上一次权重的基础上进行更新

#y表示真实的目标值
#ht[X]表示当前弱学习器预测的结果
w4 = w3*np.exp(-alpha_3*y*y3_)
w4 = w4/w4.sum()     #权重归一化
w4

弱学习器聚合
print('每一个弱学习器的预测结果')
display(y1_,y2_,y3_)
F = alpha_1*y1_+alpha_2*y2_+alpha_3*y3_
#得多个弱学习器,整合,变成了强分类器F(x)
print('强分类器合并结果\n',F)
print('强分类器最终结果如下\n',np.array([1 if i>0 else-1 for i in F]))

print('算法预测结果\n',model.predict(X))

算法VS自己的代码
model.estimator_errors_#三棵树的误差情况
array([0.3       , 0.21428571, 0.18181818])
print('自己计算的误差',error1,np.round(error2,8),np.round(error3,8))
自己计算的误差 0.3 0.21428571 0.18181818
model.estimator_weights_ #权重==话语权
array([0.84729786, 1.29928298, 1.5040774 ])
print('自己计算的权重',np.round(2*alpha_1,8),np.round(2*alpha_2,8),np.round(2*alpha_3,8))
自己计算的权重 0.84729786 1.29928298 1.5040774
计算概率
model.predict_proba(X)#类别大,划分到对应哪个类别

model.predict(X)
array([ 1,  1,  1, -1, -1, -1,  1,  1,  1, -1])

手写代码计算概率

#计算类别
y1_proba = (y1_ == np.array([[-1],[1]])).T.astype(np.int8)
y1_proba

y2_proba = (y2_ == np.array([[-1],[1]])).T.astype(np.int8)
y2_proba

y3_proba = (y3_ == np.array([[-1],[1]])).T.astype(np.int8)
y3_proba

proba = y1_proba*alpha_1*2+y2_proba*alpha_2*2+y3_proba*alpha_3*2
proba/=(alpha_1+alpha_2+alpha_3)*2
proba

proba[:,0]*=-1
proba

proba = proba.sum(axis=1)
proba

#拆分
proba = np.vstack([-proba,proba]).T/2
proba

#softmax概率转换
np.exp(proba)/(np.exp(proba)).sum(axis=1).reshape(-1,1)

model.predict_proba(X)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值