当前位置:   article > 正文

【机器学习】决策树(理论与代码)_决策树代码

决策树代码

文章目录

一、理论部分

二、代码实现

1、代码解释。

2、完整代码 

3、可视化


一、理论部分

下班到家,打开博客,源码实现下决策树。

理论部分这里不再详细讲。计算可以参考周志华西瓜书。

计算信息熵Ent(D)与信息增益Gain(D)。

原理的话就是选取信息增益最大的为根,以此类推。

观察公式可以看到:

信息增益Gain(D)= 根节点信息熵(X) -  权重*分支节点信息熵和(Y)= X - Y

由于根节点确定后 X不变,要选取最大Gain(D),就是选择最小Y。

因为Y计算过程会取负数,所以选择节点时只需取 乘积和的最大值就行了。

二、代码实现

1、代码解释。

老样子还是fit 函数 。x是特征数据集,y是结果集,多出的x_label是各个特征对应的标签

np.c_[x,y] 拼接x,y。下面看下build_tree 构建树的函数

  1. def fit(self,x,y,x_label):
  2. self.label = x_label # x数据集对应的 特征标签
  3. numpy_data = np.c_[x,y] # 把x,y拼接成一个numpy矩阵,方便后面操作
  4. self.tree = self.build_tree(numpy_data) # 构建树

 这部分是构建树,在代码里解释,可以看注释。直接上代码

  1. import numpy as np
  2. class DecisionTree():
  3. def fit(self,x,y,x_label):
  4. self.label = x_label # x数据集对应的 特征标签
  5. numpy_data = np.c_[x,y] # 把x,y拼接成一个numpy矩阵,方便后面操作
  6. self.tree = self.build_tree(numpy_data) # 构建树
  7. def build_tree(self,numpy_data): # numpy_data n行m列的矩阵,最后一列是结果集。返回字典
  8. """
  9. 判断结束:如果最后一列去重后为一个值就结束
  10. 最后分类全部是ng或者ok时递归,不能再分 !!!关键点1!!!
  11. 这里还有一种情况,就是所有的特征值都一样,但结果不同
  12. 我们可以选择取结果多的为return值。(暂不考虑,实现也不难)
  13. """
  14. finish_columns = np.unique(numpy_data)[:,-1]
  15. if len(finish_columns) == 1:
  16. return finish_columns [0] # 返回ng与ok,表面这个分支已经分到叶子了
  17. rows,columns = numpy_data.shape # numpy行数与列数
  18. root_numpy = np.zeros(columns-1) # 构造全为0的numpy数组,保存信息熵数据
  19. # 遍历每个特征。"触感","色泽","敲声",...
  20. for i in range(columns-1):
  21. sum = 0
  22. # 每个特征有多少种,如“纹理” 这个特征 numpy_data为("清晰","稍糊","模糊")
  23. unique_data = np.unique(numpy_data[:,i])
  24. for j in unique_data:
  25. len_whole = len(numpy_data[(numpy_data[:,i] == j)])
  26. len_ng = len(numpy_data[(numpy_data[:i]==j)&(numpy_data[:,-1]=="ng")])
  27. # ok的长度 = 总长度-ng的长度
  28. len_ok = len_whole-len_ng
  29. if len_ng !=0 and len_ok!=0: # 有一个为0就不用参与计算
  30. x = len_ok/len_whole
  31. # 每个特征{x属于(0,1) (x*np.log2(x)+(1-x)log2(1-x))*个数比例}
  32. # 得到单个sum
  33. # 迭代求和
  34. sum = sum + round(len_whole/rows*(x*np.log2(x)+(1-x)*np.log2(1-x)),3)
  35. root_numpy[i] = sum # 保存各个特征的信息熵
  36. root = root_numpy.argsort()[-1] # -1 选取最大值索引。np.argsort():排序后的索引
  37. """
  38. 构建字典存放树的数据
  39. 计算得到 tree_dict为:{"纹理":{}}
  40. """
  41. tree_dict = {self.label[root]:{}}
  42. # print(tree_dict)
  43. """
  44. 拿到根节点为“纹理”后还要继续分
  45. np.unique(numpy_data[:,root])计算根节点后面有几分支,("清晰"、"稍糊"、"模糊")继续分
  46. """
  47. for values in np.unique(numpy_data[:,root]):
  48. # 把对应的数据进行区分,切割出符合条件的数据后面继续分
  49. numpy_root = numpy_data[(numpy_data[:,root] == values)]
  50. # print(values,numpy_root)
  51. """
  52. 继续往下走,递归后面数据 !!!关键点2!!!
  53. 拿到根节点,分支后得到新的数据,新的数据选节点计算方式一样,
  54. 递归直到叶子结束。我们已经对数据进行处理了,后面计算方式是一样的
  55. """
  56. # 字典新的key 继续存
  57. tree_dict[self.label[root]][values] = self.build_tree(numpy_root)
  58. return tree_dict

构造数据集进行计算。这个举例子有点难,我们直接用西瓜书的数据集进行测试,也方便验证。

这里大家可能会有个疑问。

data1,2,3,4,5,6 为什么顺序跟西瓜书不一样呢,因为在计算信息熵的时候,最大值可能有多个值,所以构建的树可能不同,都正确。西瓜书选取的是第一次最大值就分,而np.argsort()取得索引最后一位是最后一次出现的最大值。为了保持一致,顺序调了下。

  1. if __name__ == "__main__":
  2. data2 = ["青绿", "乌黑", "乌黑", "青绿", "浅白", "青绿", "乌黑", "乌黑",
  3. "乌黑", "青绿", "浅白", "浅白", "青绿", "浅白", "乌黑", "浅白", "青绿"]
  4. data6 = ["蜷缩", "蜷缩", "蜷缩", "蜷缩", "蜷缩", "稍蜷", "稍蜷", "稍蜷",
  5. "稍蜷", "硬挺", "硬挺", "蜷缩", "稍蜷", "稍蜷", "稍蜷", "蜷缩", "蜷缩"]
  6. data3 = ["浊响", "沉闷", "浊响", "沉闷", "浊响", "浊响", "浊响", "浊响",
  7. "沉闷", "清脆", "清脆", "浊响", "浊响", "沉闷", "浊响", "浊响", "沉闷"]
  8. data4 = ["清晰", "清晰", "清晰", "清晰", "清晰", "清晰", "稍糊", "清晰",
  9. "稍糊", "清晰", "模糊", "模糊", "稍糊", "稍糊", "清晰", "模糊", "稍糊"]
  10. data5 = ["凹陷", "凹陷", "凹陷", "凹陷", "凹陷", "稍凹", "稍凹", "稍凹",
  11. "稍凹", "平坦", "平坦", "平坦", "凹陷", "凹陷", "稍凹", "平坦", "稍凹"]
  12. data1 = ["硬滑", "硬滑", "硬滑", "硬滑", "硬滑", "软粘", "软粘", "硬滑",
  13. "硬滑", "软粘", "硬滑", "软粘", "硬滑", "硬滑", "软粘", "硬滑", "硬滑"]
  14. result = ["ok", "ok", "ok", "ok", "ok", "ok", "ok", "ok",
  15. "ng", "ng", "ng", "ng", "ng", "ng", "ng", "ng", "ng"]
  16. x_label = ["触感", "色泽", "敲声", "纹理", "脐部", "根蒂"]
  17. x = np.array([data1, data2, data3, data4, data5, data6]).transpose()
  18. y = np.array(result)
  19. # print("标签:",x_label)
  20. # print(x)
  21. decisionTree = DecisionTree()
  22. decisionTree.fit(x, y, x_label)
  23. print(decisionTree.tree)
  24. """
  25. # print("标签:",x_label)
  26. # print(x)
  27. 标签: ['触感', '色泽', '敲声', '纹理', '脐部', '根蒂']
  28. [['硬滑' '青绿' '浊响' '清晰' '凹陷' '蜷缩']
  29. ['硬滑' '乌黑' '沉闷' '清晰' '凹陷' '蜷缩']
  30. ['硬滑' '乌黑' '浊响' '清晰' '凹陷' '蜷缩']
  31. ['硬滑' '青绿' '沉闷' '清晰' '凹陷' '蜷缩']
  32. ['硬滑' '浅白' '浊响' '清晰' '凹陷' '蜷缩']
  33. ['软粘' '青绿' '浊响' '清晰' '稍凹' '稍蜷']
  34. ['软粘' '乌黑' '浊响' '稍糊' '稍凹' '稍蜷']
  35. ['硬滑' '乌黑' '浊响' '清晰' '稍凹' '稍蜷']
  36. ['硬滑' '乌黑' '沉闷' '稍糊' '稍凹' '稍蜷']
  37. ['软粘' '青绿' '清脆' '清晰' '平坦' '硬挺']
  38. ['硬滑' '浅白' '清脆' '模糊' '平坦' '硬挺']
  39. ['软粘' '浅白' '浊响' '模糊' '平坦' '蜷缩']
  40. ['硬滑' '青绿' '浊响' '稍糊' '凹陷' '稍蜷']
  41. ['硬滑' '浅白' '沉闷' '稍糊' '凹陷' '稍蜷']
  42. ['软粘' '乌黑' '浊响' '清晰' '稍凹' '稍蜷']
  43. ['硬滑' '浅白' '浊响' '模糊' '平坦' '蜷缩']
  44. ['硬滑' '青绿' '沉闷' '稍糊' '稍凹' '蜷缩']]
  45. print(decisionTree.tree)
  46. {'纹理': {
  47. '模糊': 'ng',
  48. '清晰': {'根蒂': {'硬挺': 'ng', '稍蜷': {'色泽': {'乌黑': {'触感': {'硬滑': 'ok', '软粘': 'ng'}}, '青绿': 'ok'}}, '蜷缩': 'ok'}},
  49. '稍糊': {'触感': {'硬滑': 'ng', '软粘': 'ok'}}
  50. }
  51. }
  52. """

2、完整代码 

  1. import numpy as np
  2. class DecisionTree():
  3. def fit(self, x, y, x_label):
  4. self.label = x_label # x数据集对应的 特征标签
  5. numpy_data = np.c_[x, y] # 把x,y拼接成一个numpy矩阵,方便后面操作
  6. self.tree = self.build_tree(numpy_data) # 构建树
  7. def build_tree(self, numpy_data): # numpy_data n行m列的矩阵,最后一列是结果集。返回字典
  8. finish_columns = np.unique(numpy_data[:, -1])
  9. if len(finish_columns) == 1:
  10. return finish_columns[0] # 返回ng与ok,表面这个分支已经分到叶子了
  11. rows, columns = numpy_data.shape # numpy行数与列数
  12. root_numpy = np.zeros(columns - 1) # 构造全为0的numpy数组,保存信息熵数据
  13. for i in range(columns - 1):
  14. sum = 0
  15. unique_data = np.unique(numpy_data[:, i])
  16. for j in unique_data:
  17. len_whole = len(numpy_data[(numpy_data[:, i] == j)])
  18. len_ng = len(numpy_data[((numpy_data[:,i] == j)
  19. & (numpy_data[:, -1] == "ng"))])
  20. len_ok = len_whole - len_ng
  21. if len_ng != 0 and len_ok != 0: # 有一个为0就不用参与计算
  22. x = len_ok / len_whole
  23. sum = sum + round(len_whole / rows *
  24. (x * np.log2(x) + (1-x) * np.log2(1-x)), 3)
  25. root_numpy[i] = sum # 保存各个特征的信息熵
  26. root = root_numpy.argsort()[-1] # -1 选取最大值索引。np.argsort():排序后的索引
  27. tree_dict = {self.label[root]: {}}
  28. for values in np.unique(numpy_data[:, root]):
  29. numpy_root = numpy_data[(numpy_data[:, root] == values)]
  30. tree_dict[self.label[root]][values] = self.build_tree(numpy_root)
  31. return tree_dict
  32. if __name__ == "__main__":
  33. data2 = ["青绿", "乌黑", "乌黑", "青绿", "浅白", "青绿", "乌黑", "乌黑",
  34. "乌黑", "青绿", "浅白", "浅白", "青绿", "浅白", "乌黑", "浅白", "青绿"]
  35. data6 = ["蜷缩", "蜷缩", "蜷缩", "蜷缩", "蜷缩", "稍蜷", "稍蜷", "稍蜷",
  36. "稍蜷", "硬挺", "硬挺", "蜷缩", "稍蜷", "稍蜷", "稍蜷", "蜷缩", "蜷缩"]
  37. data3 = ["浊响", "沉闷", "浊响", "沉闷", "浊响", "浊响", "浊响", "浊响",
  38. "沉闷", "清脆", "清脆", "浊响", "浊响", "沉闷", "浊响", "浊响", "沉闷"]
  39. data4 = ["清晰", "清晰", "清晰", "清晰", "清晰", "清晰", "稍糊", "清晰",
  40. "稍糊", "清晰", "模糊", "模糊", "稍糊", "稍糊", "清晰", "模糊", "稍糊"]
  41. data5 = ["凹陷", "凹陷", "凹陷", "凹陷", "凹陷", "稍凹", "稍凹", "稍凹",
  42. "稍凹", "平坦", "平坦", "平坦", "凹陷", "凹陷", "稍凹", "平坦", "稍凹"]
  43. data1 = ["硬滑", "硬滑", "硬滑", "硬滑", "硬滑", "软粘", "软粘", "硬滑",
  44. "硬滑", "软粘", "硬滑", "软粘", "硬滑", "硬滑", "软粘", "硬滑", "硬滑"]
  45. result = ["ok", "ok", "ok", "ok", "ok", "ok", "ok", "ok",
  46. "ng", "ng", "ng", "ng", "ng", "ng", "ng", "ng", "ng"]
  47. x_label = ["触感", "色泽", "敲声", "纹理", "脐部", "根蒂"]
  48. x = np.array([data1, data2, data3, data4, data5, data6]).transpose()
  49. y = np.array(result)
  50. decisionTree = DecisionTree()
  51. decisionTree.fit(x, y, x_label)
  52. print(decisionTree.tree)

3、可视化

可视化树这里有点难度,用《机器学习实战》的代码

  1. import numpy as np
  2. import matplotlib.pylab as plt
  3. import matplotlib
  4. # 能够显示中文
  5. matplotlib.rcParams['font.sans-serif'] = ['SimHei']
  6. matplotlib.rcParams['font.serif'] = ['SimHei']
  7. # 分叉节点,也就是决策节点
  8. decisionNode = dict(boxstyle="sawtooth", fc="0.8")
  9. # 叶子节点
  10. leafNode = dict(boxstyle="round4", fc="0.8")
  11. # 箭头样式
  12. arrow_args = dict(arrowstyle="<-")
  13. def plotNode(nodeTxt, centerPt, parentPt, nodeType):
  14. """
  15. 绘制一个节点
  16. :param nodeTxt: 描述该节点的文本信息
  17. :param centerPt: 文本的坐标
  18. :param parentPt: 点的坐标,这里也是指父节点的坐标
  19. :param nodeType: 节点类型,分为叶子节点和决策节点
  20. :return:
  21. """
  22. createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction',
  23. xytext=centerPt, textcoords='axes fraction',
  24. va="center", ha="center", bbox=nodeType, arrowprops=arrow_args)
  25. def getNumLeafs(myTree):
  26. """
  27. 获取叶节点的数目
  28. :param myTree:
  29. :return:
  30. """
  31. # 统计叶子节点的总数
  32. numLeafs = 0
  33. # 得到当前第一个key,也就是根节点
  34. firstStr = list(myTree.keys())[0]
  35. # 得到第一个key对应的内容
  36. secondDict = myTree[firstStr]
  37. # 递归遍历叶子节点
  38. for key in secondDict.keys():
  39. # 如果key对应的是一个字典,就递归调用
  40. if type(secondDict[key]).__name__ == 'dict':
  41. numLeafs += getNumLeafs(secondDict[key])
  42. # 不是的话,说明此时是一个叶子节点
  43. else:
  44. numLeafs += 1
  45. return numLeafs
  46. def getTreeDepth(myTree):
  47. """
  48. 得到数的深度层数
  49. :param myTree:
  50. :return:
  51. """
  52. # 用来保存最大层数
  53. maxDepth = 0
  54. # 得到根节点
  55. firstStr = list(myTree.keys())[0]
  56. # 得到key对应的内容
  57. secondDic = myTree[firstStr]
  58. # 遍历所有子节点
  59. for key in secondDic.keys():
  60. # 如果该节点是字典,就递归调用
  61. if type(secondDic[key]).__name__ == 'dict':
  62. # 子节点的深度加1
  63. thisDepth = 1 + getTreeDepth(secondDic[key])
  64. # 说明此时是叶子节点
  65. else:
  66. thisDepth = 1
  67. # 替换最大层数
  68. if thisDepth > maxDepth:
  69. maxDepth = thisDepth
  70. return maxDepth
  71. def plotMidText(cntrPt, parentPt, txtString):
  72. """
  73. 计算出父节点和子节点的中间位置,填充信息
  74. :param cntrPt: 子节点坐标
  75. :param parentPt: 父节点坐标
  76. :param txtString: 填充的文本信息
  77. :return:
  78. """
  79. # 计算x轴的中间位置
  80. xMid = (parentPt[0]-cntrPt[0])/2.0 + cntrPt[0]
  81. # 计算y轴的中间位置
  82. yMid = (parentPt[1]-cntrPt[1])/2.0 + cntrPt[1]
  83. # 进行绘制
  84. createPlot.ax1.text(xMid, yMid, txtString)
  85. def plotTree(myTree, parentPt, nodeTxt):
  86. """
  87. 绘制出树的所有节点,递归绘制
  88. :param myTree: 树
  89. :param parentPt: 父节点的坐标
  90. :param nodeTxt: 节点的文本信息
  91. :return:
  92. """
  93. # 计算叶子节点数
  94. numLeafs = getNumLeafs(myTree=myTree)
  95. # 计算树的深度
  96. depth = getTreeDepth(myTree=myTree)
  97. # 得到根节点的信息内容
  98. firstStr = list(myTree.keys())[0]
  99. # 计算出当前根节点在所有子节点的中间坐标,也就是当前x轴的偏移量加上计算出来的根节点的中心位置作为x轴(比如说第一次:初始的x偏移量为:-1/2W,计算出来的根节点中心位置为:(1+W)/2W,相加得到:1/2),当前y轴偏移量作为y轴
  100. cntrPt = (plotTree.xOff + (1.0 + float(numLeafs))/2.0/plotTree.totalW, plotTree.yOff)
  101. # 绘制该节点与父节点的联系
  102. plotMidText(cntrPt, parentPt, nodeTxt)
  103. # 绘制该节点
  104. plotNode(firstStr, cntrPt, parentPt, decisionNode)
  105. # 得到当前根节点对应的子树
  106. secondDict = myTree[firstStr]
  107. # 计算出新的y轴偏移量,向下移动1/D,也就是下一层的绘制y轴
  108. plotTree.yOff = plotTree.yOff - 1.0/plotTree.totalD
  109. # 循环遍历所有的key
  110. for key in secondDict.keys():
  111. # 如果当前的key是字典的话,代表还有子树,则递归遍历
  112. if isinstance(secondDict[key], dict):
  113. plotTree(secondDict[key], cntrPt, str(key))
  114. else:
  115. # 计算新的x轴偏移量,也就是下个叶子绘制的x轴坐标向右移动了1/W
  116. plotTree.xOff = plotTree.xOff + 1.0/plotTree.totalW
  117. # 打开注释可以观察叶子节点的坐标变化
  118. # print((plotTree.xOff, plotTree.yOff), secondDict[key])
  119. # 绘制叶子节点
  120. plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)
  121. # 绘制叶子节点和父节点的中间连线内容
  122. plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))
  123. # 返回递归之前,需要将y轴的偏移量增加,向上移动1/D,也就是返回去绘制上一层的y轴
  124. plotTree.yOff = plotTree.yOff + 1.0/plotTree.totalD
  125. def createPlot(inTree):
  126. """
  127. 需要绘制的决策树
  128. :param inTree: 决策树字典
  129. :return:
  130. """
  131. # 创建一个图像
  132. fig = plt.figure(1, facecolor='white')
  133. fig.clf()
  134. axprops = dict(xticks=[], yticks=[])
  135. createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)
  136. # 计算出决策树的总宽度
  137. plotTree.totalW = float(getNumLeafs(inTree))
  138. # 计算出决策树的总深度
  139. plotTree.totalD = float(getTreeDepth(inTree))
  140. # 初始的x轴偏移量,也就是-1/2W,每次向右移动1/W,也就是第一个叶子节点绘制的x坐标为:1/2W,第二个:3/2W,第三个:5/2W,最后一个:(W-1)/2W
  141. plotTree.xOff = -0.5/plotTree.totalW
  142. # 初始的y轴偏移量,每次向下或者向上移动1/D
  143. plotTree.yOff = 1.0
  144. # 调用函数进行绘制节点图像
  145. plotTree(inTree, (0.5, 1.0), '')
  146. # 绘制
  147. plt.show()
  148. class DecisionTree():
  149. def fit(self, x, y, x_label):
  150. self.label = x_label # x数据集对应的 特征标签
  151. numpy_data = np.c_[x, y] # 把x,y拼接成一个numpy矩阵,方便后面操作
  152. self.tree = self.build_tree(numpy_data) # 构建树
  153. def build_tree(self, numpy_data): # numpy_data n行m列的矩阵,最后一列是结果集。返回字典
  154. finish_columns = np.unique(numpy_data[:, -1])
  155. if len(finish_columns) == 1:
  156. return finish_columns[0] # 返回ng与ok,表面这个分支已经分到叶子了
  157. rows, columns = numpy_data.shape # numpy行数与列数
  158. root_numpy = np.zeros(columns - 1) # 构造全为0的numpy数组,保存信息熵数据
  159. for i in range(columns - 1):
  160. sum = 0
  161. unique_data = np.unique(numpy_data[:, i])
  162. for j in unique_data:
  163. len_whole = len(numpy_data[(numpy_data[:, i] == j)])
  164. len_ng = len(numpy_data[((numpy_data[:,i] == j) & (numpy_data[:, -1] == "ng"))])
  165. len_ok = len_whole - len_ng
  166. if len_ng != 0 and len_ok != 0: # 有一个为0就不用参与计算
  167. x = len_ok / len_whole
  168. sum = sum + round(len_whole / rows * (x * np.log2(x) + (1-x) * np.log2(1-x)), 3)
  169. root_numpy[i] = sum # 保存各个特征的信息熵
  170. root = root_numpy.argsort()[-1] # -1 选取最大值索引。np.argsort():排序后的索引
  171. tree_dict = {self.label[root]: {}}
  172. for values in np.unique(numpy_data[:, root]):
  173. numpy_root = numpy_data[(numpy_data[:, root] == values)]
  174. tree_dict[self.label[root]][values] = self.build_tree(numpy_root)
  175. return tree_dict
  176. if __name__ == "__main__":
  177. data2 = ["青绿", "乌黑", "乌黑", "青绿", "浅白", "青绿", "乌黑", "乌黑",
  178. "乌黑", "青绿", "浅白", "浅白", "青绿", "浅白", "乌黑", "浅白", "青绿"]
  179. data6 = ["蜷缩", "蜷缩", "蜷缩", "蜷缩", "蜷缩", "稍蜷", "稍蜷", "稍蜷",
  180. "稍蜷", "硬挺", "硬挺", "蜷缩", "稍蜷", "稍蜷", "稍蜷", "蜷缩", "蜷缩"]
  181. data3 = ["浊响", "沉闷", "浊响", "沉闷", "浊响", "浊响", "浊响", "浊响",
  182. "沉闷", "清脆", "清脆", "浊响", "浊响", "沉闷", "浊响", "浊响", "沉闷"]
  183. data4 = ["清晰", "清晰", "清晰", "清晰", "清晰", "清晰", "稍糊", "清晰",
  184. "稍糊", "清晰", "模糊", "模糊", "稍糊", "稍糊", "清晰", "模糊", "稍糊"]
  185. data5 = ["凹陷", "凹陷", "凹陷", "凹陷", "凹陷", "稍凹", "稍凹", "稍凹",
  186. "稍凹", "平坦", "平坦", "平坦", "凹陷", "凹陷", "稍凹", "平坦", "稍凹"]
  187. data1 = ["硬滑", "硬滑", "硬滑", "硬滑", "硬滑", "软粘", "软粘", "硬滑",
  188. "硬滑", "软粘", "硬滑", "软粘", "硬滑", "硬滑", "软粘", "硬滑", "硬滑"]
  189. result = ["ok", "ok", "ok", "ok", "ok", "ok", "ok", "ok",
  190. "ng", "ng", "ng", "ng", "ng", "ng", "ng", "ng", "ng"]
  191. x_label = ["触感", "色泽", "敲声", "纹理", "脐部", "根蒂"]
  192. x = np.array([data1, data2, data3, data4, data5, data6]).transpose()
  193. y = np.array(result)
  194. decisionTree = DecisionTree()
  195. decisionTree.fit(x, y, x_label)
  196. print(decisionTree.tree)
  197. createPlot(decisionTree.tree)

可视化得到图如下: 

 对比西瓜书

对比发现又两点不同 

一是 发现纹理-->根蒂-->色泽 这里缺少浅白,这是因为数据集本身就不存在(清晰、稍蜷、浅白)。这里可以进行填充。感兴趣的可以试下。

二是 在纹理为稍糊、触感这里,ng与ok反了,这里是西瓜书打印错误。

最后亿点说明:

为什么构建树的时候只需要计算信息熵就可以了,而且不用移除出之前的特征?

因为我们实际上计算的是

 sum = sum + x * np.log2(x) + (1-x) * np.log2(1-x)

这个值我们可以观察下y = x * np.log2(x) + (1-x) * np.log2(1-x)。这个函数关于x=1/2对称

当x属于(0,1)时连续,求导,计算出(0,1/2)递减,(1/2,1)递增

当x = 1/2最小,y越小说明信息增益越小,文章开头讲过。求极限,利用洛必达法则,上下求导就可以算出。当x趋于0时 y也趋于0且连续所以函数曲线大致为可以画出类似于  "- sin(πx)" 在(0,1)。因为结果ok,ng为同一组的时候时不能在分的。当可以再分时,我们前面选出的特征一定是负的最大的。对公式简单处理,就可以快速构造决策树

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/1014564
推荐阅读
相关标签
  

闽ICP备14008679号