今天,我来讲解的是决策树。对于决策树来说,主要有两种算法:ID3算法C4.5算法。C4.5算法是

对ID3算法的改进。今天主要先讲ID3算法,之后会讲C4.5算法随机森林等。

Contents

     1. 决策树的基本认识

     2. ID3算法介绍

     3. 信息熵与信息增益

     4. ID3算法的C++实现

1. 决策树的基本认识

   决策树是一种依托决策而建立起来的一种树。在机器学习中,决策树是一种预测模型,代表的是一种对

象属性与对象值之间的一种映射关系,每一个节点代表某个对象,树中的每一个分叉路径代表某个可能

的属性值,而每一个叶子节点则对应从根节点到该叶子节点所经历的路径所表示的对象的值。决策树仅

有单一输出,如果有多个输出,可以分别建立独立的决策树以处理不同的输出。接下来讲解ID3算法。

2. ID3算法介绍

ID3算法是决策树的一种,它是基于奥卡姆剃刀原理的,即用尽量用较少的东西做更多的事。ID3算法

Iterative Dichotomiser 3迭代二叉树3代,是Ross Quinlan发明的一种决策树算法,这个

算法的基础就是上面提到的奥卡姆剃刀原理,越是小型的决策树越优于大的决策树,尽管如此,也不总

是生成最小的树型结构,而是一个启发式算法。

在信息论中,期望信息越小,那么信息增益就越大,从而纯度就越高。ID3算法的核心思想就是以信息

增益来度量属性的选择,选择分裂后信息增益最大的属性进行分裂。该算法采用自顶向下的贪婪搜索遍

历可能的决策空间。

3. 信息熵与信息增益

在信息增益中,重要性的衡量标准就是看特征能够为分类系统带来多少信息,带来的信息越多,该特征越

重要。在认识信息增益之前,先来看看信息熵的定义

这个概念最早起源于物理学,在物理学中是用来度量一个热力学系统的无序程度,而在信息学里面,熵

是对不确定性的度量。在1948年,香农引入了信息熵,将其定义为离散随机事件出现的概率,一个系统越

是有序,信息熵就越低,反之一个系统越是混乱,它的信息熵就越高。所以信息熵可以被认为是系统有序

化程度的一个度量。

假如一个随机变量的取值为,每一种取到的概率分别是,那么

的熵定义为

意思是一个变量的变化情况可能越多,那么它携带的信息量就越大。

对于分类系统来说,类别是变量,它的取值是,而每一个类别出现的概率分别是

而这里的就是类别的总数,此时分类系统的熵就可以表示为

以上就是信息熵的定义,接下来介绍信息增益

信息增益是针对一个一个特征而言的,就是看一个特征,系统有它和没有它时的信息量各是多少,两者

的差值就是这个特征给系统带来的信息量,即信息增益

接下来以天气预报的例子来说明。下面是描述天气数据表,学习目标是play或者not play

可以看出,一共14个样例,包括9个正例和5个负例。那么当前信息的熵计算如下

在决策树分类问题中,信息增益就是决策树在进行属性选择划分前和划分后信息的差值。假设利用

属性Outlook来分类,那么如下图

划分后,数据被分为三部分了,那么各个分支的信息熵计算如下

那么划分后的信息熵为(下面的T是数据属性)

代表在特征属性的条件下样本的条件熵。那么最终得到特征属性带来的信息增益为

 信息增益的计算公式如下

其中为全部样本集合,是属性所有取值的集合,的其中一个属性值,中属性

值为的样例集合,中所含样例数。

在决策树的每一个非叶子结点划分之前,先计算每一个属性所带来的信息增益,选择最大信息增益的属性来划

分,因为信息增益越大,区分样本的能力就越强,越具有代表性,很显然这是一种自顶向下的贪心策略。以上

就是ID3算法的核心思想。

4. ID3算法的C++实现

接下来开始用C++实现ID3算法,包括以下文件

ID3.h

[cpp] view plain copy

  1. #ifndef _ID3_H_
  2. #define _ID3_H_
  3. #include <utility>
  4. #include <list>
  5. #include <map>
  6. #define Type int   //样本数据类型
  7. #define   Map1        std::map< int, Type >    //定义一维map
  8. #define   Map2        std::map< int, Map1 >    //定义二维map
  9. #define   Map3        std::map< int, Map2 >    //定义三维map
  10. #define   Pair        std::pair<int, Type>
  11. #define   List        std::list< Pair >        //一维list
  12. #define   SampleSpace std::list< List >        //二维list 用于存放样本数据
  13. #define   Child       std::map< int, Node* >   //定义后继节点集合
  14. #define   CI          const_iterator
  15. /*
  16. *   在ID3算法中,用二维链表存放样本,结构为list< list< pair<int, int> > >,简记为SampleSpace,取名样本空间
  17. *   样本数据从根节点开始往下遍历。每一个节点的定义如下结构体
  18. */
  19. struct Node
  20. {
  21. int index;                    //当前节点样本最大增益对应第index个属性,根据这个进行分类的
  22. int type;                     //当前节点的类型
  23. Child next;                   //当前节点的后继节点集合
  24. SampleSpace sample;           //未分类的样本集合
  25. };
  26. class ID3{
  27. public:
  28. ID3(int );
  29. ~ID3();
  30. void PushData(const Type*, const Type);   //将样本数据Push给二维链表
  31. void Build();                             //构建决策树
  32. int  Match(const Type*);                  //根据新的样本预测结果
  33. void Print();                             //打印决策树的节点的值
  34. private:
  35. void   _clear(Node*);
  36. void   _build(Node*, int);
  37. int    _match(const int*, Node*);
  38. void   _work(Node*);
  39. double _entropy(const Map1&, double);
  40. int    _get_max_gain(const SampleSpace&);
  41. void   _split(Node*, int);
  42. void   _get_data(const SampleSpace&, Map1&, Map2&, Map3&);
  43. double _info_gain(Map1&, Map2&, double, double);
  44. int    _same_class(const SampleSpace&);
  45. void   _print(Node*);
  46. private:
  47. int dimension;
  48. Node *root;
  49. };
  50. #endif // _ID3_H_

ID3.cpp

[cpp] view plain copy

  1. #include <iostream>
  2. #include <cassert>
  3. #include <cmath>
  4. #include "ID3.h"
  5. using namespace std;
  6. //初始化ID3的数据成员
  7. ID3::ID3(int dimension)
  8. {
  9. this->dimension = dimension;
  10. root = new Node();
  11. root->index = -1;
  12. root->type = -1;
  13. root->next.clear();
  14. root->sample.clear();
  15. }
  16. //清空整个决策树
  17. ID3::~ID3()
  18. {
  19. this->dimension = 0;
  20. _clear(root);
  21. }
  22. //x为dimension维的属性向量,y为向量x对应的值
  23. void ID3::PushData(const Type *x, const Type y)
  24. {
  25. List single;
  26. single.clear();
  27. for(int i = 0; i < dimension; i++)
  28. single.push_back(make_pair(i + 1, x[i]));
  29. single.push_back(make_pair(0, y));
  30. root->sample.push_back(single);
  31. }
  32. void ID3::_clear(Node *node)
  33. {
  34. Child &next = node->next;
  35. Child::iterator it;
  36. for(it = next.begin(); it != next.end(); it++)
  37. _clear(it->second);
  38. next.clear();
  39. delete node;
  40. }
  41. void ID3::Build()
  42. {
  43. _build(root, dimension);
  44. }
  45. void ID3::_build(Node *node, int dimension)
  46. {
  47. //获取当前节点未分类的样本数据
  48. SampleSpace &sample = node->sample;
  49. //判断当前所有样本是否是同一类,如果不是则返回-1
  50. int y = _same_class(sample);
  51. //如果所有样本是属于同一类
  52. if(y >= 0)
  53. {
  54. node->index = -1;
  55. node->type = y;
  56. return;
  57. }
  58. //在_max_gain()函数中计算出当前节点的最大增益对应的属性,并根据这个属性对数据进行划分
  59. _work(node);
  60. //Split完成后清空当前节点的所有数据,以免占用太多内存
  61. sample.clear();
  62. Child &next = node->next;
  63. for(Child::iterator it = next.begin(); it != next.end(); it++)
  64. _build(it->second, dimension - 1);
  65. }
  66. //判断当前所有样本是否是同一类,如果不是则返回-1
  67. int ID3::_same_class(const SampleSpace &ss)
  68. {
  69. //取出当前样本数据的一个Sample
  70. const List &f = ss.front();
  71. //如果没有x属性,而只有y,直接返回y
  72. if(f.size() == 1)
  73. return f.front().second;
  74. Type y = 0;
  75. //取出第一个样本数据y的结果值
  76. for(List::CI it = f.begin(); it != f.end(); it++)
  77. {
  78. if(!it->first)
  79. {
  80. y = it->second;
  81. break;
  82. }
  83. }
  84. //接下来进行判断,因为list是有序的,所以从前往后遍历,发现有一对不一样,则所有样本不是同一类
  85. for(SampleSpace::CI it = ss.begin(); it != ss.end(); it++)
  86. {
  87. const List &single = *it;
  88. for(List::CI i = single.begin(); i != single.end(); i++)
  89. {
  90. if(!i->first)
  91. {
  92. if(y != i->second)
  93. return -1;         //发现不是同一类则返回-1
  94. else
  95. break;
  96. }
  97. }
  98. }
  99. return y;     //比较完所有样本的输出值y后,发现是同一类,返回y值。
  100. }
  101. void ID3::_work(Node *node)
  102. {
  103. int mai = _get_max_gain(node->sample);
  104. assert(mai >= 0);
  105. node->index = mai;
  106. _split(node, mai);
  107. }
  108. //获取最大的信息增益对应的属性
  109. int ID3::_get_max_gain(const SampleSpace &ss)
  110. {
  111. Map1 y;
  112. Map2 x;
  113. Map3 xy;
  114. _get_data(ss, y, x, xy);
  115. double s = ss.size();
  116. double entropy = _entropy(y, s);   //计算熵值
  117. int mai = -1;
  118. double mag = -1;
  119. for(Map2::iterator it = x.begin(); it != x.end(); it++)
  120. {
  121. double g = _info_gain(it->second, xy[it->first], s, entropy);    //计算信息增益值
  122. if(g > mag)
  123. {
  124. mag = g;
  125. mai = it->first;
  126. }
  127. }
  128. if(!x.size() && !xy.size() && y.size())   //如果只有y数据
  129. return 0;
  130. return mai;
  131. }
  132. //获取数据,提取出所有样本的y值,x[]属性值,以及属性值和结果值xy。
  133. void ID3::_get_data(const SampleSpace &ss, Map1 &y, Map2 &x, Map3 &xy)
  134. {
  135. for(SampleSpace::CI it = ss.begin(); it != ss.end(); it++)
  136. {
  137. int c = 0;
  138. const List &v = *it;
  139. for(List::CI p = v.begin(); p != v.end(); p++)
  140. {
  141. if(!p->first)
  142. {
  143. c = p->second;
  144. break;
  145. }
  146. }
  147. ++y[c];
  148. for(List::CI p = v.begin(); p != v.end(); p++)
  149. {
  150. if(p->first)
  151. {
  152. ++x[p->first][p->second];
  153. ++xy[p->first][p->second][c];
  154. }
  155. }
  156. }
  157. }
  158. //计算熵值
  159. double ID3::_entropy(const Map1 &x, double s)
  160. {
  161. double ans = 0;
  162. for(Map1::CI it = x.begin(); it != x.end(); it++)
  163. {
  164. double t = it->second / s;
  165. ans += t * log2(t);
  166. }
  167. return -ans;
  168. }
  169. //计算信息增益
  170. double ID3::_info_gain(Map1 &att_val, Map2 &val_cls, double s, double entropy)
  171. {
  172. double gain = entropy;
  173. for(Map1::CI it = att_val.begin(); it != att_val.end(); it++)
  174. {
  175. double r = it->second / s;
  176. double e = _entropy(val_cls[it->first], it->second);
  177. gain -= r * e;
  178. }
  179. return gain;
  180. }
  181. //对当前节点的sample进行划分
  182. void ID3::_split(Node *node, int idx)
  183. {
  184. Child &next = node->next;
  185. SampleSpace &sample = node->sample;
  186. for(SampleSpace::iterator it = sample.begin(); it != sample.end(); it++)
  187. {
  188. List &v = *it;
  189. for(List::iterator p = v.begin(); p != v.end(); p++)
  190. {
  191. if(p->first == idx)
  192. {
  193. Node *tmp = next[p->second];
  194. if(!tmp)
  195. {
  196. tmp = new Node();
  197. tmp->index = -1;
  198. tmp->type = -1;
  199. next[p->second] = tmp;
  200. }
  201. v.erase(p);
  202. tmp->sample.push_back(v);
  203. break;
  204. }
  205. }
  206. }
  207. }
  208. int ID3::Match(const Type *x)
  209. {
  210. return _match(x, root);
  211. }
  212. int ID3::_match(const Type *v, Node *node)
  213. {
  214. if(node->index < 0)
  215. return node->type;
  216. Child &next = node->next;
  217. Child::iterator p = next.find(v[node->index - 1]);
  218. if(p == next.end())
  219. return -1;
  220. return _match(v, p->second);
  221. }
  222. void ID3::Print()
  223. {
  224. _print(root);
  225. }
  226. void ID3::_print(Node *node)
  227. {
  228. cout << "Index    = " << node->index << endl;
  229. cout << "Type     = " << node->type << endl;
  230. cout << "NextSize = " << node->next.size() << endl;
  231. cout << endl;
  232. Child &next = node->next;
  233. Child::iterator p;
  234. for(p = next.begin(); p != next.end(); ++p)
  235. _print(p->second);
  236. }

main.cpp

[cpp] view plain copy

  1. #include <iostream>
  2. #include "ID3.h"
  3. using namespace std;
  4. enum outlook {SUNNY, OVERCAST, RAIN };
  5. enum temp    {HOT,   MILD,     COOL };
  6. enum hum     {HIGH,  NORMAL         };
  7. enum windy   {WEAK,  STRONG         };
  8. int samples[14][4] =
  9. {
  10. {SUNNY   ,       HOT ,      HIGH  ,       WEAK  },
  11. {SUNNY   ,       HOT ,      HIGH  ,       STRONG},
  12. {OVERCAST,       HOT ,      HIGH  ,       WEAK  },
  13. {RAIN    ,       MILD,      HIGH  ,       WEAK  },
  14. {RAIN    ,       COOL,      NORMAL,       WEAK  },
  15. {RAIN    ,       COOL,      NORMAL,       STRONG},
  16. {OVERCAST,       COOL,      NORMAL,       STRONG},
  17. {SUNNY   ,       MILD,      HIGH  ,       WEAK  },
  18. {SUNNY   ,       COOL,      NORMAL,       WEAK  },
  19. {RAIN    ,       MILD,      NORMAL,       WEAK  },
  20. {SUNNY   ,       MILD,      NORMAL,       STRONG},
  21. {OVERCAST,       MILD,      HIGH  ,       STRONG},
  22. {OVERCAST,       HOT ,      NORMAL,       WEAK  },
  23. {RAIN    ,       MILD,      HIGH  ,       STRONG}
  24. };
  25. int main()
  26. {
  27. ID3 Tree(4);
  28. Tree.PushData((int *)&samples[0], 0);
  29. Tree.PushData((int *)&samples[1], 0);
  30. Tree.PushData((int *)&samples[2], 1);
  31. Tree.PushData((int *)&samples[3], 1);
  32. Tree.PushData((int *)&samples[4], 1);
  33. Tree.PushData((int *)&samples[5], 0);
  34. Tree.PushData((int *)&samples[6], 1);
  35. Tree.PushData((int *)&samples[7], 0);
  36. Tree.PushData((int *)&samples[8], 1);
  37. Tree.PushData((int *)&samples[9], 1);
  38. Tree.PushData((int *)&samples[10], 1);
  39. Tree.PushData((int *)&samples[11], 1);
  40. Tree.PushData((int *)&samples[12], 1);
  41. Tree.PushData((int *)&samples[13], 0);
  42. Tree.Build();
  43. Tree.Print();
  44. cout << endl;
  45. for(int i = 0; i < 14; ++i)
  46. cout << "predict value :    " <<Tree.Match( (int *)&samples[i] ) << endl;
  47. return 0;
  48. }

Makefile

[cpp] view plain copy

  1. Test: main.cpp ID3.h ID3.cpp
  2. g++ -o Test ID3.cpp main.cpp
  3. clean:
  4. rm Test

决策树-熵计算-ID3算法(转)相关推荐

  1. 【机器学习】深刻理解决策树-动手计算ID3算法

     一.决策树概述 决策树算法易于理解.可解释性强,是一个非常常见并且优秀的机器学习算法,可分类,也可回归.现在许多最优秀的集成模型,基础也是决策树.因此,决策树系列算法是机器学习绕不过的大山.需要进行 ...

  2. 自编程实现决策树(使用ID3算法)

    自编程实现决策树(使用ID3算法) 不使用sklearn中的决策树方法,根据以下数据集自己编写决策树构建程序(建议用python语言). Tid Refund Marital Status Taxab ...

  3. 决策树的生成—ID3算法

    决策树的生成-ID3算法 算法由来: 决策树算法最开始是由Hunt Earl B提出的CLS(Concept Learning System),但是没有给出采用什么方法选择最优特征,后面罗斯昆(J. ...

  4. 决策树模型 - (ID3算法、C4.5算法) - Python代码实现

    目录 算法简介 信息熵(Entropy) 信息增益(Information gain) - ID3算法 信息增益率(gain ratio) - C4.5算法 源数据 代码实现 - ID3算法 代码实现 ...

  5. 机器学习:决策树(Decision Tree)--ID3算法

    决策树的主要算法 构建决策树的关键:按照什么样的次序来选择变量(属性/特征)作为分类依据. 根据不同的目标函数,建立决策树主要有以下三种算法 ID3(J.Ross Quinlan-1975) 核心:信 ...

  6. 决策树的生成-ID3算法生成_例题详解

    数据集: 判断特征集是否是空集,进行下一步 选择信息增益最大的那个特征作为根节点,以"有自己的房子"作为限定条件看其他特征 D1(有自己的房子)不确定性为0,以D2(没有自己的房子 ...

  7. 利用计算机语言实现ID3算法,机器学习之决策树学习-id3算法-原理分析及c语言代码实现.pdf...

    机器学习之决策树学习-id3算法-原理分析及c语言代码实现.pdf 还剩 23页未读, 继续阅读 下载文档到电脑,马上远离加班熬夜! 亲,很抱歉,此页已超出免费预览范围啦! 如果喜欢就下载吧,价低环保 ...

  8. 决策树(1)——ID3算法与C4.5算法的理论基础与python实现

    1.前言   决策树是一种常用的机器学习算法,既可以做分类任务也可以做回归任务,本文主要讨论用于分类任务的决策树.决策树与数据结构中的树相同,也是由一个根结点.若干个内部结点和若干个叶结点构成.这里我 ...

  9. 使用ID3算法构造决策树——python

    任务描述 本关任务:补充python代码,完成DecisionTree类中的fit和predict函数. 相关知识 为了完成本关任务,你需要掌握: ID3算法构造决策树的流程 如何使用构造好的决策树进 ...

  10. 机器学习题5:请简述ID3算法的实现步骤,并利用ID3算法构建天气数据集的决策树模型,实现决策树的可视化。

    ID3算法的实现步骤: 输入:数据集(训练集)S及属性A 输出:属性A对训练数据集S的信息增益 ① 先将S作为根节点,其目标属性y有c个类别属性.假设S中出现的概率,计算数据集S的信息熵. ② 假设属 ...

最新文章

  1. 【青少年编程】【四级】创意画图
  2. 生产计划到底该怎样改进?这6点帮你节省至少60%的计划时间
  3. 轻量级 Kubernetes K3s - Github热点
  4. office工作日志文件_风暴事件处理器–每个工作者的GC日志文件
  5. python wheel使用_Python wheel.Wheel方法代碼示例
  6. 分治 —— 莫队算法 —— 普通莫队
  7. c语言编程继承例子,C语言模拟实现C++的继承与多态示例
  8. git生成SSH密钥提示ssh文件不存在-已解决
  9. OpenCV-Python 识别万用表七段数码管电流值
  10. 【语音定时播报系统】基于树莓派+百度语音合成打造语音定时播报系统
  11. SystemTap笔记02 stap的编译运行
  12. Windows10 LTSB/LTSC 企业版安装应用商店
  13. 服务器raid5阵列修复,RAID5磁盘阵列的安装与故障修复
  14. 微商扫码发货管理系统开发
  15. Navicat 全部产品破解方法(MySQL/PostgreSQL等所有navicat的产品均可破解)
  16. stm32f105vct6例程_stm32f105例程 CAN500K USART_LED
  17. Linux 指令学习之ping
  18. 任务调度 cron使用
  19. 虚拟机安装mysql步骤
  20. jackson学习之五:JsonInclude注解,颠覆认知

热门文章

  1. Android Service学习之IntentService 深入分析
  2. 【自然框架】之通用权限(四):角色表组
  3. 上海博彦科技 千万别来_这个年产值2870亿元的科技园区,将率多家企业亮相本届服贸会...
  4. node js的模块化的分类
  5. ubuntuKylin17.04重装KDE
  6. OpenCV中的模板匹配/Filter2d
  7. SVD分解.潜语义分析.PythonCode
  8. Eigen库对齐问题:declspec(align('16')) 的形参将不被对齐
  9. awk文本工具按列计算和
  10. 提高JS性能注意事项(转载)