机器学习-周志华-课后习题答案-决策树

时间:2022-03-15 11:41:23

本文为博主原创,由于没有可以参考的答案,所以内容中若有错误的地方烦请指正,不甚感激。

注:本文中的代码均使用python,常用工具包包括 pandas,scikit-learn,numpy, scipy,matplotlib等。


4.1试证明对于不含冲突数据(即特征向量完全相同但标记不同)的训练集,必存在与训练集一致(即训练误差为0)的决策树

答:假设不存在与训练集一致的决策树,那么训练集训练得到的决策树至少有一个节点上存在无法划分的多个数据(若节点上没有冲突数据,那么总是能够将数据分开的)。这与前提-不含冲突数据 矛盾,因此必存在与训练集一致的决策树

 

4.2试析使用“最小训练误差”作为决策树划分选择的缺陷。

答:若以最小训练误差作为决策树划分的依据,由于训练集和真是情况总是会存在一定偏差,这使得这样得到的决策树会存在过拟合的情况,对于未知的数据的泛化能力较差。因此最小训练误差不适合用来作为决策树划分的依据。

 

4.3试编程实现基于信息熵进行划分选择的决策树算法,并为表4.3中数据生成一棵决策树

答:基于信息熵进行划分选择的决策树算法即ID3决策树,代码见我的另外一篇博文:ID3决策树的Python实现


4.4试编程实现基于基尼指数进行划分选择的决策树算法,并为表4.2中数据生成预剪枝、后剪枝决策树,并与未剪枝决策树进行比较。

答:基于基尼指数进行划分选择的决策树算法即CART决策树,代码见我的另外一篇博文:CART决策树与剪枝处理


4.5试编程实现基于对率回归进行划分选择的决策树算法,并为表4.3中数据生成一棵决策树

答:由于不知道该如何用对率回归做划分选择,所以此题暂时不会写。若读到此文的朋友了解相关内容可以在文后评论。非常感激~

 

4.6试选择4UCI数据集,对上述3种算法所产生的未剪枝、预剪枝、后剪枝决策树进行实验比较,并进行适当的统计显著性检验。

 答:我选了一个UCI数据集:Wine Data Set。这个数据集共有大约180条数据,12种特征,其标签为1、2、3共三种(应该是三种酒吧)。我对数据的顺序进行了随机,取前140个数据作为训练集,后40个数据作为测试集

按照4.3与4.4中的代码,得到的决策树如下:

ID3决策树:

机器学习-周志华-课后习题答案-决策树

CART未剪枝决策树:

机器学习-周志华-课后习题答案-决策树

CART预剪枝决策树:

机器学习-周志华-课后习题答案-决策树


CART后剪枝决策树:

机器学习-周志华-课后习题答案-决策树

可以看出,后剪枝比起预剪枝不容易出现欠拟合的情况。


4.74.2是一个递归算法,若面临巨量数据,则决策树的层数会很深,使用递归方法易导致“栈”溢出,试使用“队列”数据结构,以参数maxDepth控制数的最大深度,写出与图4.2等价、但不使用递归的决策树生成算法。

 答:下面算法我没有尝试写对应的代码,若有朋友写过欢迎交流/指正。

以下代码为 队列+MaxDepth控制,即广度优先搜索。其实我觉得这里如果要用MaxDepth进行控制的话,应该选择堆栈而非队列,即应该用深度优先搜索。但下面还是给出队列的形式。若要改为深度优先搜索只需要将先进后出 改成 先进先出即可(即数据存取都在一端)。

——————————————————————————————————————————————————————

输入:训练集 D={(x1,y1),(x2,y2),...,(xm,ym)};

      属性集 A={a1,a2,...,ad}

      最大深度 MaxDepth

过程:函数TreeGenerate(D,A,MaxDepth)

1:生成节点root

2:if D中样本全部属于同一类别C then

3:     将root标记为C类叶节点;return

4:end if

5:if A=空集 or D中样本在A上取值相同 then

6:     将root标记为叶节点,其类别标记为D中样本数最多的类;return

7:end if

8:从A中选择最优划分属性a*;

9:将root标记为分支节点,属性为属性a*;

10:将root放入NodeQueue;

11:将D放入DataQueue;

12:将A\{a*}放入AQueue;

13:初始化深度depth=1;

14:将depth放入DepthQueue;

15:while NodeQueue 非空:

16:     取出NodeQueue队尾的节点rNode,其对应的属性是ra*;

17:     取出DataQueue队尾的数据集rD;     #此处r均指队尾rear

18:     取出AQueue队尾的属性集rA;

19:     取出DepthQueue队尾的元素rdepth;

20:     if rdepth==MaxDepth:

21:          将rNode标记为叶节点,类别标记为rD中样本最多的类;

22:          continue;     #跳过本次循环,即不再对这个节点做展开

23:     for ra*的每一个取值ra*v do:

24:          为rNode生成一个分支节点,令rDv表示rD在ra*上取值为ra*v的样本子集;

25:          if rDv为空 then:

26:               将分支节点标记为叶节点,其类别标记为rD中样本最多的类;

27:          else if rD中样本全部属于同一类别C then

28:               将分支节点标记为C类叶节点;

29:          else if rA=空集 orrD中样本在A上取值相同 then

30:               将分支节点标记为叶节点,其类别标记为rD中样本数最多的类;

31:          else:

32:               从rA中选择最优划分属性a*v;

33:               将分支节点的属性记为a*v;

34:               将分支节点放入NodeQueue的队头;

35:               将rDv放入DataQueue的队头;

36:               将rA\{a*v}放入AQueue的队头;

37:               将(rDepth+1)放入DepthQueue的队头;

38:          end if

39:     end for

40:end while

输入:以root为根节点的一棵决策树

——————————————————————————————————————————————————————


4.8试将决策树生成的深度优先搜索过程修改为广度优先搜索,以参数MaxNode控制树的最大结点数,将题4.7中基于队列的决策树算法进行改写。对比题4.7中的算法,试分析哪种方式更易于控制决策树所需储存不超过内存。

 答:在4.7中写的基于队列的算法本身就是广度优先搜索的。若要写成深度优先搜索的方式应该将队列换成堆栈即可。

以下对4.7中的代码进行改写,用队列+MaxNode控制

——————————————————————————————————————————————————————

输入:训练集 D={(x1,y1),(x2,y2),...,(xm,ym)};

      属性集 A={a1,a2,...,ad}

      最大节点数 MaxNode

过程:函数TreeGenerate(D,A,MaxNode)

1:生成节点root

2:if D中样本全部属于同一类别C then

3:     将root标记为C类叶节点;return

4:end if

5:if A=空集 or D中样本在A上取值相同 then

6:     将root标记为叶节点,其类别标记为D中样本数最多的类;return

7:end if

8:从A中选择最优划分属性a*;

9:将root标记为分支节点,属性为属性a*;

10:将root放入NodeQueue;

11:将D放入DataQueue;

12:将A\{a*}放入AQueue;

13:初始化节点数numNode=1;

14:while NodeQueue 非空:

15:     取出NodeQueue队尾的节点rNode,其对应的属性是ra*;

16:     取出DataQueue队尾的数据集rD;     #此处r均指队尾rear

17:     取出AQueue队尾的属性集rA;

18:     if numNode==MaxNode:

19:          将rNode标记为叶节点,类别标记为rD中样本最多的类;

20:          continue;     #跳过本次循环,即不再对这个节点做展开。对于下一个节点,由于条件任然成立,故任然不展开

21:     for ra*的每一个取值ra*v do:

22:          为rNode生成一个分支节点,令rDv表示rD在ra*上取值为ra*v的样本子集;

23:          if rDv为空 then:

24:               将分支节点标记为叶节点,其类别标记为rD中样本最多的类;

25:          else if rD中样本全部属于同一类别C then

26:               将分支节点标记为C类叶节点;

27:          else if rA=空集 or rD中样本在A上取值相同 then

28:               将分支节点标记为叶节点,其类别标记为rD中样本数最多的类;

29:          else:

30:               从rA中选择最优划分属性a*v;

31:               将分支节点的属性记为a*v;

32:               将分支节点放入NodeQueue的队头;

33:               将rDv放入DataQueue的队头;

34:               将rA\{a*v}放入AQueue的队头;

35:               将(rDepth+1)放入DepthQueue的队头;

36:          end if

37:         numNode+=1

38:     end for

39:end while

输入:以root为根节点的一棵决策树

——————————————————————————————————————————————————————

讨论:4.7中与4.8中用队列的话均为广度优先搜索,我觉得是出题的时候的疏忽。。

应该是广度优先搜索(队列)+MaxNode控制 与 深度优先搜索(堆栈)+MaxDepth控制 两种方法之间的比较。

个人认为,广度优先搜索(队列)+MaxNode 的方法更容易控制决策树所需内存不溢出。因为最大节点数目是固定的。队列中储存的是当前深度未处理的节点以及当前深度以处理节点的下一级节点,其数目是可控的,总小于最大节点数。而深度优先搜索在堆栈中储存的是当前节点的兄弟节点、当前节点的父节点、当前节点的父节点的兄弟节点.....若一些分支节点的分支数很多,那么堆栈的深度就会比较深,虽然有MaxDepth控制最大深度,但还是可能出现栈溢出的情况。


4.9试将4.4.2节对缺失值的处理机制推广到基尼指数的计算中去。

 答:

机器学习-周志华-课后习题答案-决策树

 

4.10从网上下载或自己编程实现任意一种多变量决策树算法,并观察其在西瓜数据集3.0上产生的结果。

答:此处要求实现一种多变量决策树算法。实际上4.3与4.4题就是多变量决策树算法。其在西瓜数据集3.0上产生的结果如下


机器学习-周志华-课后习题答案-决策树

与P85的图4.8一致。