当前位置: 首页 > news >正文

基于单决策树的AdaBoost

 

①起源:Boosting算法

Boosting算法的目的是每次基于全部数据集,通过使用同一种分类器不同的抽取参数方法(如决策树,每次都可以抽取不同的特征维度来剖分数据集)

训练一些不同弱分类器(单次分类错误率>0.5),然后将其组合起来,综合评估(默认认为每个分类器权重等价)进行分类。

AdaBoost算法进行了对其进行了改进。

一、每次训练分类器时,给予每条数据用于计算误差的不同权重D。

二、每个分类器,给予用于分类的不同权重alpha。

两种权的使用,使得AdaBoost在组合分类器时,能够根据当前的训练情况做一些调整。

 

②弱分类器:单决策树

单决策树应该是AdaBoost中使用最多的弱分类器。硬伤是单决策树是个0/1二类分类器。(实际是-1/+1二类)

一般决策树是通过DFS,连续选择不同维度多深度划分数据集。

单决策树就是一般决策树的1维版本,只搜dep=1就结束。

单决策树有两个好处:

一、单次分类效果很差(满足弱分类器定义)

二、根据决策属性不同,即便重复使用单决策树,也可以诞生出分类效果不同的分类器。

单决策树对于连续型数据,采用阈值(threshold)分类法。

首先确定所有数据中某个特征维度里的min/max范围。

然后采用步进的方法(每次阈值=min+当前步数*步长)来确定阈值。

基于同一个阈值,又可以把小于/大于阈值的分到不同类(2种分法)。

所以单决策树最多有【D*步数*2】种变形分类器。

 

②双权问题

一、分类器权alpha

 定义 $\alpha = \frac{1}{2} \ln \left ( \frac{1-\varepsilon }{\varepsilon } \right )$,其中$\varepsilon$为错误率。

P.S 实际Coding时,为防止$\varepsilon$为零,通常取max($\varepsilon$,eps)

$\alpha$这个函数比较有趣,求导之后,假设每次使用新的分类器,错误率都会变小,以错误率变小为x正轴,有图像:

这个图像告诉我们,AdaBoost对后续分类器,给的权是逐渐变大的,因为我们有理由相信,后续分类器更科学一点。

分类器权如何使用呢?假设当前分类器的二类分类结果向量[-1,1,-1,1],那么其加权之后就是$\alpha$ * [-1,1,-1,1]。

对于二类问题,将全部分类器的加权结果累加起来,通过判断加权结果符号的正负判断分类,因为$\alpha$的值域是R,所以只能看符号判断分类。

二、数据权D

每条数据都有权D,该权实际是个概率权,总权和=1。

如果一条数据被正确分类,则$D_{i}^{new}= \frac{D_{i}^{old}*e^{-\alpha}}{Sum(D)}$

如果一条数据被错误分类,则$D_{i}^{new}= \frac{D_{i}^{old}*e^{\alpha}}{Sum(D)}$

正确分类的权会被减小,错误分类的权会被增大。有趣的是,这两个式子如果单从$\alpha$角度来看,似乎有点问题。

如果某次$\alpha$为负,那么$D_{i}^{new}= \frac{D_{i}^{old}*e^{-\alpha}}{Sum(D)}$$D_{i}^{new}$似乎好像是变大了。

但如果你把$\alpha$的定义式拉到$D_{i}^{new}$里面就会发现:

$\left\{\begin{matrix}
D_{i}^{new}= \frac{1}{\sqrt{e}}*\frac{1-\varepsilon }{\varepsilon }
\\
D_{i}^{new}= \sqrt{e}*\frac{1-\varepsilon }{\varepsilon}
\end{matrix}\right.$

这样,很明显就可以看出,正确分类的权确实相对于错误分类的权被减小了。

数据权D的引入,主要是采用一种新的方式计算错误率。

一般的错误率$\varepsilon=\frac{errorExamples}{allExamples}$

但是AdaBoost里对错误率也进行加权了,$\varepsilon=\sum_{i=1}^{m}D_{i}*isError?1:0$

由于D是概率权,这样算出来的也算是错误率。如果一条数据分错了、又分错了、还是分错了,那么这条数据理应得到重视,其也会导致错误率比较大。

这种加权错误率对于在单决策树里,从众多变形分类器中,选择一个错误率最低分类器,有很好的参考意义。

 

③算法过程

★训练过程

while(true)

{

        一、构建一个错误率最低的单决策树分类器,并保存记录该分类器全部属性(α、阀值、选取维度、分类结果)

        二、利用构建的单决策树分类器计算α加权分类结果

        若数据样本分类全部正确(大数据情况下几乎不可能)、分类正确率到达一定情况(多见于正确率收敛)则break

}

★测试过程

for(全部构建的分类器)

      for(全部测试数据)

           一、使用当前分类器分类

           二、累计计算使用全部分类器的加权结果(记录α的原因)

根据加权结果的正负判断分类

 

不难发现,测试过程其实就是训练过程的某一步提取出来的。

 

④代码

#include "iostream"
#include "fstream"
#include "sstream"
#include "math.h"
#include "vector"
#include "string"
#include "cstdio"
using namespace std;
#define fin cin
#define inf 1e10
#define D dataSet[0].feature.size()
#define delta 100
int sign(double x) {return x<0?-1:1;}
struct Data
{
    vector<double> feature;
    int y;
    Data(vector<double> feature,int y):feature(feature),y(y) {}
};
struct Stump
{
    vector<int> classEst;
    int dim,symbol;
    double threshold,error,alpha;
    Stump() {}
    Stump(vector<int> classEst,int dim,double threshold,int symbol,double error):classEst(classEst),dim(dim),threshold(threshold),symbol(symbol),error(error) {}
};
vector<Data> dataSet;
void read()
{
    ifstream fin("traindata.txt");
    string line;
    double fea,cls;
    int id;
    while(getline(cin,line))
    {
        vector<double> feature;
        stringstream sin(line);
        sin>>id;
        while(sin>>fea) feature.push_back(fea);
        cls=feature.back();feature.pop_back();
        dataSet.push_back(Data(feature,cls==0?-1:1));
    }
}
vector<int> stumpClassify(int d,double threshold,int symbol)
{
    vector<int> ret(dataSet.size(),1);
    for(int i=0;i<dataSet.size();i++)
    {
        if(!symbol) {if(dataSet[i].feature[d]<=threshold) ret[i]=-1;}
        else {if(dataSet[i].feature[d]>threshold) ret[i]=-1;}
    }
    return ret;
}
Stump buildStump(vector<double> dataWeight)
{
    int step=10;
    Stump bestStump;
    double minError=inf,weightError;
    for(int i=0;i<D;i++)
    {
        double minRange=inf,maxRange=-inf,stepSize;
        for(int j=0;j<dataSet.size();j++)
        {
            minRange=min(minRange,dataSet[j].feature[i]);
            maxRange=max(maxRange,dataSet[j].feature[i]);
        }
        stepSize=(maxRange-minRange)/step;
        for(int j=0;j<=step;j++)
        {
            double threshold=minRange+stepSize*j;
            for(int k=0;k<=1;k++)
            {
                vector<int> classEst=stumpClassify(i,threshold,k);
                weightError=0.0;
                for(int t=0;t<classEst.size();t++)
                    if(classEst[t]!=dataSet[t].y) weightError+=dataWeight[t];
                if(weightError<minError)
                {
                    bestStump=Stump(classEst,i,threshold,k,weightError);
                    minError=weightError;
                }
            }
        }
    }
    return bestStump;
}
vector<Stump> mainProcess()
{
    int iter=0,repeat=0,last_cnt=-1;
    vector<double> dataWeight(dataSet.size(),1.0/dataSet.size());
    vector<double> aggClassEst(dataSet.size(),0.0);
    vector<Stump> stumpList;
    while(1)
    {
        iter++;
        Stump nowStump=buildStump(dataWeight);
        double alpha=0.5*log((1.0-nowStump.error)/max(nowStump.error,1e-16)),DSum=0.0;
        nowStump.alpha=alpha;
        stumpList.push_back(nowStump);
        int err_cnt=0;
        for(int i=0;i<dataSet.size();i++)
        {
            double ret=-1*alpha*dataSet[i].y*nowStump.classEst[i];
            dataWeight[i]=dataWeight[i]*exp(ret);
            DSum+=dataWeight[i];
        }
        for(int i=0;i<dataSet.size();i++)
        {
            dataWeight[i]/=DSum;
            aggClassEst[i]+=alpha*nowStump.classEst[i];
            if(sign(aggClassEst[i])!=dataSet[i].y) err_cnt++;
        }
        //cout<<err_cnt<<"/"<<dataSet.size()<<endl;
        if(err_cnt!=last_cnt) {last_cnt=err_cnt;repeat=0;}
        else repeat++;
        if(err_cnt==0||repeat>delta) break;
    }
    return stumpList;
}
void classify(vector<Stump> stumpList)
{
    ifstream fin("testdata.txt");
    dataSet.clear();
    string line;
    double fea,cls;
    int id;
    while(getline(cin,line))
    {
        vector<double> feature;
        stringstream sin(line);
        sin>>id;
        while(sin>>fea) feature.push_back(fea);
        cls=feature.back();feature.pop_back();
        dataSet.push_back(Data(feature,cls==0?-1:1));
    }
    vector<double> aggClassEst(dataSet.size(),0.0);
    for(int i=0;i<stumpList.size();i++)
    {
        vector<int> classEst=stumpClassify(stumpList[i].dim,stumpList[i].threshold,stumpList[i].symbol);
        for(int j=0;j<dataSet.size();j++)
          aggClassEst[j]+=(stumpList[i].alpha*classEst[j]);
    }
    for(int i=0;i<dataSet.size();i++)
        if(sign(aggClassEst[i])==-1) printf("%test #%d:  origin: %d class %d\n",i,dataSet[i].y,-1);
        else printf("%test #%d: origin: %d class %d\n",i,dataSet[i].y,1);
}
int main()
{
    read();
    vector<Stump> stumpList=mainProcess();
    classify(stumpList);
}

 

转载于:https://www.cnblogs.com/neopenx/p/4297289.html

相关文章:

  • 【SICP练习】53 练习2.21
  • php 基础算法(用*表示金字塔)通过hash 比較两个数组同样的数
  • sass带来的变革
  • QlikView ETL - 分隔字符串的方法 SubField
  • 微软职位内部推荐-Senior Development Lead
  • WSS(Windows Storage Server)2008R2使用指南(二)安装篇
  • javascript——DOM之元素的宽高
  • 【原创】开源Math.NET基础数学类库使用(08)C#进行数值积分
  • SQL Server 2008 R2 安全性专题(一):安全原则
  • the linux command line学习笔记之四
  • PS常见错误-无法完成请求,因为文件格式模块不能解析该文件
  • 多线程中sleep()、wait()方法等的区别
  • 转帖:改变。没有很宏大的目标没有关系,就从身边的小事开始,从最需要变化的地方开始,尽快。...
  • ***linux下安装xampp,XAMPP目录结构(阿里云安装xampp)
  • Java与iOS中的线程安全与线程同步
  • 【Leetcode】101. 对称二叉树
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 2017前端实习生面试总结
  • Angular4 模板式表单用法以及验证
  • CentOS学习笔记 - 12. Nginx搭建Centos7.5远程repo
  • codis proxy处理流程
  • Git同步原始仓库到Fork仓库中
  • nodejs:开发并发布一个nodejs包
  • Python学习之路13-记分
  • RxJS: 简单入门
  • SpiderData 2019年2月13日 DApp数据排行榜
  • thinkphp5.1 easywechat4 微信第三方开放平台
  • Vim Clutch | 面向脚踏板编程……
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 飞驰在Mesos的涡轮引擎上
  • 算法-图和图算法
  • 王永庆:技术创新改变教育未来
  • 我这样减少了26.5M Java内存!
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • #Java第九次作业--输入输出流和文件操作
  • #Linux(make工具和makefile文件以及makefile语法)
  • #NOIP 2014# day.2 T2 寻找道路
  • $jQuery 重写Alert样式方法
  • (12)目标检测_SSD基于pytorch搭建代码
  • (4) openssl rsa/pkey(查看私钥、从私钥中提取公钥、查看公钥)
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • (七)微服务分布式云架构spring cloud - common-service 项目构建过程
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .net 前台table如何加一列下拉框_如何用Word编辑参考文献
  • .NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • .NET框架
  • @SpringBootApplication 包含的三个注解及其含义
  • [2016.7 test.5] T1
  • [codeforces]Levko and Permutation