【机器学习BDT】python代码实现(下)

BDT(Bootstrap Decision Tree)python实现

代码仅供参考

导入库

import CART树_自己写 
import numpy

其中一个库之前写的

分类树主体代码

在原始数据权重基础上使用更新的数据权重以更新BDT模型.

    def 分类树(self,BDT_M,setA,标签,D,权重,验标签,验D,验权重,
              树的最大深度=5,最小样本数=10,最小不纯度=0.1,损失函数='平方',输出='平均'
              ):
        '''
        BDT回归树
            由CART回归数合并得到
            Loss为指数
        CART回归树(连续变量)
        这个是确定了给定Loss下分类模型,确定了输入空间分开的M个区间,
          如果输入落入第m个区域,那么输出是其实应该是输出的相应y值的平均
                                        还可以是输出y值的众数(分类问题)
        #此函数对Loss最小方式为暴力搜索,后续可能添加剃度下降版本
        输入
        BDT_M:M个CART树
        setA:划分的步长,按分bin来
            (binN,xmin,xmax) (区间划分点数N-1)
            [(50,0,1,0.01),(2,-0.1,1.1),...]
        最小样本数:划分阶段实现
        最小不纯度:###还在施工###
        损失函数:str|'function''平方':平方差,'Gini',Gini函数
                其他可定义,格式为 float=fun(标签,select:numpy.ndarray=None)
                当有select表示划分后Loss
        输出:str|'function''平均':标签平均,'众数':最大标签数(离散输出)
            其他可定义,格式为 number=fun(标签)

        输出
        Tree: ([左子,右子],besti,bestj,xj,output)
            output:[[-1,sumevent],[标签i,counti],...]
        '''
        FM=[]
        aM=[]
        set标签=numpy.array(list(set(标签)))
        WM=numpy.ones(len(D))/len(D)
        for mi in range(BDT_M):
            fmi=CART树_自己写.CART模型(标签,D)
            T,acu,output0_var=fmi.训练(setA,标签,D,WM*权重,验标签,验D,验权重,#此处验证集就是原本
                      树的最大深度=树的最大深度,最小样本数=最小样本数,最小不纯度=最小不纯度,损失函数=损失函数,输出=输出)
            FM.append(fmi)

            # fmibefore=self.函数输出(FM,aM,D)
            output0=fmi.叶子输出_批量(T,D)#输出必须是训练集
            outputlabel=output0.copy().astype(标签[0])
            for i,oi in enumerate(output0):
                oilabelindex=numpy.argmin(abs(oi-set标签))
                outputlabel[i]=set标签[oilabelindex]
            acu=float(sum((outputlabel==标签)*权重))/sum(权重)#真实正确率
            if acu>0.9999:acu=0.9999
            if acu<0.0001:acu=0.0001
            em=1-acu
            ami=0.5*numpy.log(acu/em)
            aM.append(ami)
            WMnew=WM*numpy.exp(-ami*标签*output0);WMnew=WMnew/sum(WMnew)
            WM[:]=WMnew[:]
        return FM,aM

回归树主体代码

新的基学习器拟合残差。

    def 回归树(self,BDT_M,setA,标签,D,权重,验标签,验D,验权重,
              树的最大深度=5,最小样本数=10,最小不纯度=0.1,损失函数='平方',输出='平均'
              ):
        '''
        BDT回归树
            由CART回归数合并得到
            Loss为残差平方#此方法下损失函数只能为连续的,不能为Gini!!
        CART回归树(连续变量)
        这个是确定了给定Loss下分类模型,确定了输入空间分开的M个区间,
          如果输入落入第m个区域,那么输出是其实应该是输出的相应y值的平均
                                        还可以是输出y值的众数(分类问题)
        #此函数对Loss最小方式为暴力搜索,后续可能添加剃度下降版本
        输入
        BDT_M:M个CART树
        setA:划分的步长,按分bin来
            (binN,xmin,xmax) (区间划分点数N-1)
            [(50,0,1,0.01),(2,-0.1,1.1),...]
        最小样本数:划分阶段实现
        最小不纯度:###还在施工###
        损失函数:str|'function':'平方':平方差,'Gini',Gini函数
                其他可定义,格式为 float=fun(标签,select:numpy.ndarray=None)
                当有select表示划分后Loss
        输出:str|'function':'平均':标签平均,'众数':最大标签数(离散输出)
            其他可定义,格式为 number=fun(标签)

        输出
        Tree: ([左子,右子],besti,bestj,xj,output)
            output:[[-1,sumevent],[标签i,counti],...]
        '''
        FM=[]
        aM=[]
        WM=numpy.ones(len(D))
        for mi in range(BDT_M):
            fmi=CART树_自己写.CART模型(标签,D)
            fmibefore=self.函数输出(FM,aM,D)
            T,acu,output0=fmi.训练(setA,标签-fmibefore,D,权重,验标签,验D,验权重,#此处验证集就是原本
                      树的最大深度=树的最大深度,最小样本数=最小样本数,最小不纯度=最小不纯度,损失函数=损失函数,输出=输出)
            FM.append(fmi)
            aM.append(1.0)
        return FM,aM

输出函数

对于分类树的输出可以提前算好输出的整体权重

    def 函数输出(self,FM:list[CART树_自己写.CART模型],aM,D):
        aM=numpy.array(aM);#aM=aM/sum(abs(aM))
        F=numpy.zeros(len(D))
        for i,x in enumerate(FM):
            F+=aM[i]*FM[i].叶子输出_批量(FM[i].Tree,D)
        return F

完整代码

import CART树_自己写 
import numpy

class BDT():
    '''
    简单BDT

    '''
    def __init__(self, 标签, Data):
        assert len(标签)==len(Data),'标签应该和数据个数一致!'
        self.数据个数=len(标签)
        self.维数=len(Data[0])
        self.标签=numpy.array(标签)
        self.Data=numpy.array(Data)

    def 分类树(self,BDT_M,setA,标签,D,权重,验标签,验D,验权重,
              树的最大深度=5,最小样本数=10,最小不纯度=0.1,损失函数='平方',输出='平均'
              ):
        '''
        BDT回归树
            由CART回归数合并得到
            Loss为指数
        CART回归树(连续变量)
        这个是确定了给定Loss下分类模型,确定了输入空间分开的M个区间,
          如果输入落入第m个区域,那么输出是其实应该是输出的相应y值的平均
                                        还可以是输出y值的众数(分类问题)
        #此函数对Loss最小方式为暴力搜索,后续可能添加剃度下降版本
        输入
        BDT_M:M个CART树
        setA:划分的步长,按分bin来
            (binN,xmin,xmax) (区间划分点数N-1)
            [(50,0,1,0.01),(2,-0.1,1.1),...]
        最小样本数:划分阶段实现
        最小不纯度:###还在施工###
        损失函数:str|'function''平方':平方差,'Gini',Gini函数
                其他可定义,格式为 float=fun(标签,select:numpy.ndarray=None)
                当有select表示划分后Loss
        输出:str|'function''平均':标签平均,'众数':最大标签数(离散输出)
            其他可定义,格式为 number=fun(标签)

        输出
        Tree: ([左子,右子],besti,bestj,xj,output)
            output:[[-1,sumevent],[标签i,counti],...]
        '''
        FM=[]
        aM=[]
        set标签=numpy.array(list(set(标签)))
        WM=numpy.ones(len(D))/len(D)
        for mi in range(BDT_M):
            fmi=CART树_自己写.CART模型(标签,D)
            T,acu,output0_var=fmi.训练(setA,标签,D,WM*权重,验标签,验D,验权重,#此处验证集就是原本
                      树的最大深度=树的最大深度,最小样本数=最小样本数,最小不纯度=最小不纯度,损失函数=损失函数,输出=输出)
            FM.append(fmi)

            # fmibefore=self.函数输出(FM,aM,D)
            output0=fmi.叶子输出_批量(T,D)#输出必须是训练集
            outputlabel=output0.copy().astype(标签[0])
            for i,oi in enumerate(output0):
                oilabelindex=numpy.argmin(abs(oi-set标签))
                outputlabel[i]=set标签[oilabelindex]
            acu=float(sum((outputlabel==标签)*权重))/sum(权重)#真实正确率
            if acu>0.9999:acu=0.9999
            if acu<0.0001:acu=0.0001
            em=1-acu
            ami=0.5*numpy.log(acu/em)
            aM.append(ami)
            WMnew=WM*numpy.exp(-ami*标签*output0);WMnew=WMnew/sum(WMnew)
            WM[:]=WMnew[:]
        return FM,aM
    
    def 回归树(self,BDT_M,setA,标签,D,权重,验标签,验D,验权重,
              树的最大深度=5,最小样本数=10,最小不纯度=0.1,损失函数='平方',输出='平均'
              ):
        '''
        BDT回归树
            由CART回归数合并得到
            Loss为残差平方#此方法下损失函数只能为连续的,不能为Gini!!
        CART回归树(连续变量)
        这个是确定了给定Loss下分类模型,确定了输入空间分开的M个区间,
          如果输入落入第m个区域,那么输出是其实应该是输出的相应y值的平均
                                        还可以是输出y值的众数(分类问题)
        #此函数对Loss最小方式为暴力搜索,后续可能添加剃度下降版本
        输入
        BDT_M:M个CART树
        setA:划分的步长,按分bin来
            (binN,xmin,xmax) (区间划分点数N-1)
            [(50,0,1,0.01),(2,-0.1,1.1),...]
        最小样本数:划分阶段实现
        最小不纯度:###还在施工###
        损失函数:str|'function':'平方':平方差,'Gini',Gini函数
                其他可定义,格式为 float=fun(标签,select:numpy.ndarray=None)
                当有select表示划分后Loss
        输出:str|'function':'平均':标签平均,'众数':最大标签数(离散输出)
            其他可定义,格式为 number=fun(标签)

        输出
        Tree: ([左子,右子],besti,bestj,xj,output)
            output:[[-1,sumevent],[标签i,counti],...]
        '''
        FM=[]
        aM=[]
        WM=numpy.ones(len(D))
        for mi in range(BDT_M):
            fmi=CART树_自己写.CART模型(标签,D)
            fmibefore=self.函数输出(FM,aM,D)
            T,acu,output0=fmi.训练(setA,标签-fmibefore,D,权重,验标签,验D,验权重,#此处验证集就是原本
                      树的最大深度=树的最大深度,最小样本数=最小样本数,最小不纯度=最小不纯度,损失函数=损失函数,输出=输出)
            FM.append(fmi)
            aM.append(1.0)
        return FM,aM
    
    def 函数输出(self,FM:list[CART树_自己写.CART模型],aM,D):
        aM=numpy.array(aM);#aM=aM/sum(abs(aM))
        F=numpy.zeros(len(D))
        for i,x in enumerate(FM):
            F+=aM[i]*FM[i].叶子输出_批量(FM[i].Tree,D)
        return F
    
    def 准确矩阵(self,output,标签):
        '''
        Mij,i是输出
        '''
        set标签=set(标签)
        M=numpy.zeros((len(set标签),len(set标签)),dtype=int)
        for i,labeli in enumerate(list(set标签)):
            for j,labelj in enumerate(list(set标签)):
                M[i,j]=numpy.all([output==labeli,标签==labelj],axis=0)
        return M
    

后续可能添加的功能

  1. 需要用K-S检验来确定验证和训练输出是否一致.
  2. K-fold交叉验证.
  3. 剃度下降法(CART).
  4. 修正使得定义类的时候就定义好超参数,类内保存训练结果.
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值