瓜皮机器学习——用java对ID3算法进行实现


琢磨了两天的决策树算法,还是有一些心得。首先来说一说什么是决策树,引用一下度娘百科的官方解释:

****分类树(决策树)是一种十分常用的分类方法。他是一种监管学习,所谓监管学习就是给定一堆样本,每个样本都有一组属性和一个类别,这些类别是事先确定的,那么通过学习得到一个分类器,这个分类器能够对新出现的对象给出正确的分类。这样的机器学习就被称之为监督学习****

嗯。。。这个解释的确很官方,通俗来说,就好比我们去购物,你要先对要买的物品有一个最初步的概念(*属性1:这是不是你要买的?*),其次考虑它的质量(*属性二:是否耐用?是否存在质量问题?*),然后考虑它的价格(*属性三:是否高于预期价格?*),等等等等blabla的问题之后,你可以对这个物品做出自己的一个决断(结果:属于“该买类”还是“不买类”),这就是一颗Decision Tree的工作过程。

   针对我们上边举出的例子,可以画出如下所示的一颗决策树(PPT画的,略丑):
![购物过程中的一个简单的决策过程](https://img-blog.csdn.net/20170731140458474?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzA0NzQzNjE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

   一颗上图展示了一颗简单的DT(Decision Tree)的结构,可以看出,实际上就是一个典型的多叉树形结构,其包含了一个Root Node,并且除去Root所有的节点都有且只有一个Parent Node,其leave node(这里用圆圈表示,代表最后的决策结果)包含的值均属于最后的分类值集合中(“买”与“不买”),且每一条“根茎”(表示节点与节点之间连接处)都代表了一个condition,这里的condition就是我们所说的决策标准。

   可以看出的是,决策树在指定决策的过程中的运作方式与人脑在做claasification问题的时候的流程是很相像的,这就赋予了Decission Tree很高的可解释性,拥有可解释性意味着Decisicion Tree在工作过程中可以很好地对过程中的operation进行理解,而不像神经网络那样包含了一堆复杂的参数。如果我们能够从训练集中提取合适的属性以及判决标准(对应于购物决策过程中的思路),那么在下一次购物的过程中,我们就可以利用脑子存在的这些既定规则,对“某个物品该不该买”这样的决策问题进行决断,这就对应了一个很简单的二分类问题。但是在实际过程中,并不是只有“买”和“不买”这样简单的分类问题的,我们需要对决策过程中起到关键作用的属性做一个比较科学地挑选(到底是选价格?还是质量?还是产地?另外它们之间排序的优先级是怎么样的?等等问题)。



   OK,让我们从最简单的情况开始考虑,并开始逐步地使用一些ML的术语。上边的问题可以归结为:
   给定一个TrainingSet,记为D,再给定一个属性集AttributeSet,记为A,如何从D何A给出的data中刻画Decision Tree?

   最简单的情况,购物的过程中所有的物品都是同属于一个类别,比如我们不用经过任何思考就可以判定其为“该买”类,D中所有的data记录均属于正例集,那么此时的DT就非常简单,可以表示为一个Root Node与一个leave Node连接的单子树形式,如图:
   ![最简单情况下的单子树](https://img-blog.csdn.net/20170731142412283?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzA0NzQzNjE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

   下面考虑另一种也是很简单的情况,如果我们在超市看到所有的物品的属性都是一模一样的(实际上这是几乎不可能的),那么该如何做决策呢?那这就要case-by-case了。正常的思维应该就是如果平时用得多就买,几乎不怎么用就不买吧?OK,DT在处理这种情况时的思维也是类似的。若D在A上的取值完全相同,则取D中类别标记样本数最多的类。举个栗子,如果你看到10个一模一样的西瓜,它们是完全相同的(注意:是完全相同),但是它们存在着不同的类别标记(比如7个是好瓜,3个是坏瓜),那么如果再拿一个一模一样的瓜让我们做claasification的话,我们可以有70%的把握说它是好瓜,因此可以将其归入好瓜类。这种情况下刻画出的决策树如下图:
   ![另一种简单的情况](https://img-blog.csdn.net/20170731143403879?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzA0NzQzNjE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

    第三种情况,对英语DT算法的核心部分,对于在A上具有不同属性,且分别在不同类的D,如何进行处理呢?
    我们上边提到了一个很重要的处理步骤:属性的选择及其优先级的排序。从上边超市购物的例子可以看出实际上我们所考虑的属性集是一个有序的队列(是否需要->质量->价格),实际上这个步骤属于传统机器学习过程中的一个十分重要的部分——Feature Engeneering(特征工程),我们需要从已知的A种提取出一个序列化的特征组,将其标记为每一棵子树的Root。
    那么,如何从A种选取最优的划分属性a呢?我们稍等一下讲,实际上不同的划分属性a的方法也就对应了不同的DT算法(如ID3和C4.5算法)。
    假设我们现在已经取得了一个最优划分属性,也就是说我们获得了一个node,该node作为一个新的生成子树的Root,并且该node下应该有v个分支,v代表了D在该属性a下的v种取值情况,我们令Dv表示D在a属性下取值为v的集合。考虑一下Dv的取值情况,如果Dv为空,代表D在属性a下没有取值为av的元素。在这种情况下我们的处理方式与情况2类似,即将其标记为leave node,并且将其类别处理为D种样本最多的类。这种情况的处理是为了增强整个DT算法的鲁棒性。如果Dv不为空,我们采用递归的思想,即将Dv作为新的D,A\a作为新的A进行递归,对应于人在思考过程中一个循序渐进的过程,最终可以获得一棵DT。算法的流程步骤如下图(参考周志华老师的机器学习西瓜书):

算法流程



    OK,基本的算法流程我们已经介绍完了,回过头来解决上边的一个问题:如何选择最优的划分属性a呢?我们需要一个能够评价这个最优属性a的标准。一般而言,随着划分过程的不断进行,我们希望DT的分支节点包含的样本尽量属于同一个类别,即节点的purity越来越高。这样,当我们取到leave node的时候,我们就可以在该node上获得纯度最高的一个集合,进而在D上获得一个最大的区分度。
    有信息论基础的同学应该知道度量样本集合纯度的一个常用指标:information entropy(信息熵),它的定义如下:假定当前样本集合D种第k类样本所占的比例为$$p_k$$(k=1,2,...,n),那么,对于该集合D的信息熵可以定义为:
    Ent(D)=-\sum_k=1^|n|$$p_k$$log($$p_k$$)
    其中log是以2为底的对数,对于该公式,我们约定当p为0时,$$p_k$$log($$p_k$$)=0。
    对于给定的D,Ent(D)越小,则说明D的样本纯度越高,考虑一下如果D中的样本均属于同一类,纯度达到最高,则$$p_k$$ =1,Ent(D)的值为0。类似的,若D中的样本呢分属n个类,则此时的Ent(D)=log(n)。

    根据上面的定义,我们获得了一个衡量D纯度的方式,但是这并不是我们真正需要的,我们需要的是一个能够在每一步算法进行的过程中能够对D分出的每一个Dv具有最大纯度提升的衡量标准。由于每个Dv所包含的样本数不同,可以考虑给每一个分支节点赋予一个权重|Dv|/|D|,即样本数越多的分支节点影响越大。于是我们可以得出用某个属性a(a包含于A中)来对D进行划分的information gain(信息增益),表达式如下:
    Gain(D,a)=Ent(D)-sum_v=1^|V||$$D^v$$|/|D|Ent($$$D^v$)
    一般来说,信息增益越大,则意味着使用属性a来进行划分的“纯度提升”越大,这里不多加证明。总结一下,我们在选择最优划分属性时,需要对每一个属性a在D上进行划分获得的Gain进行计算,然后取当前获得最大Gain值得a作为D的当前最优划分属性。这种采用Gain来进行属性划分的DT构建方法我们称之为**ID3决策树算法**。

    OK,啰嗦了这么多,终于要开始进行激动人心的写代码环节了。我们采用一个简单的例子,即周志华老师编著的《机器学习》一书中的西瓜数据集2.0,先来看看这个数据集大概的样子:
    ![西瓜数据集 2.0](https://img-blog.csdn.net/20170801120427611?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzA0NzQzNjE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
    对了,在写具体的代码之前,我们先来定义一下树形的数据结构。这里我仅采用子节点表示父节点的方法来构建这棵树,直接上代码:
/**
 * 构建Decision Tree的节点结构
 * Attributes:
 * Parent_Key:记录当前节点父节点的Key,String,默认为“”
 * Parent_Value:记录当前节点父节点的Value,String,默认为“”
 * Self_Key:记录当前节点的Key,String,默认为“”
 * if_leaves_node:记录当前节点的类别index,当且仅当为叶结点时有值,String,默认为""
 * Child_Node_List:记录当前节点下的所有子节点,ArrayList<Tree_Node>,默认长度为0
 *
 *
 * Functions:
 * PK,PV,SK,if_leaves_node,Child_Node_List的getter和setter方法
 * */
class Tree_Node{
    private String Parent_Key;
    private String Parent_Value;
    private String Self_Key;
    private String if_leaves_node;
    private ArrayList<Tree_Node> Child_Node_List;
    public Tree_Node(){
        this.if_leaves_node="";
        Child_Node_List=new ArrayList<Tree_Node>();
    }
    public Tree_Node(String PK, String PV, String SK){
        this.Parent_Key=PK;
        this.Parent_Value=PV;
        this.Self_Key=SK;
        this.if_leaves_node="";
        this.Child_Node_List=new ArrayList<Tree_Node>();
    }

    public String getParent_Key() {
        return Parent_Key;
    }

    public void setParent_Key(String parent_Key) {
        Parent_Key = parent_Key;
    }

    public String getParent_Value() {
        return Parent_Value;
    }

    public void setParent_Value(String parent_Value) {
        Parent_Value = parent_Value;
    }

    public String getSelf_Key() {
        return Self_Key;
    }

    public void setSelf_Key(String self_Key) {
        Self_Key = self_Key;
    }

    public String isIf_leaves_node() {
        return if_leaves_node;
    }

    public void setIf_leaves_node(String if_leaves_node) {
        this.if_leaves_node = if_leaves_node;
    }

    public ArrayList<Tree_Node> getChild_Node_List() {
        return Child_Node_List;
    }

    public void setChild_Node_List(ArrayList<Tree_Node> child_Node_List) {
        Child_Node_List = child_Node_List;
    }

}
    篇幅限制,如果把所有的代码贴上来估计就太长了,有兴趣的朋友可以移步到我的githut上看我写的代码,代码能力有限,欢迎指教讨论。
    我的github地址:[基于java的ID3算法实现](https://github.com/ricarvy/Desicion_Tree_Code)
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值