5.1

根据表5.1所给的训练数据集,利用信息增益比(C4.5算法)生成决策树.


image.png
先计算每个特征的信息增益比,信息增益比第五章习题 - 图2
其中D关于特征A的熵第五章习题 - 图3(n为A特征的取值个数)
D关于特征A的条件熵
信息增益 第五章习题 - 图4
分别以第五章习题 - 图5表示年龄、有工作、有自己的房子和信贷情况4个特征。
经验熵第五章习题 - 图6
第五章习题 - 图7
第五章习题 - 图8
第五章习题 - 图9
第五章习题 - 图10

例题5.2已经计算了各个特征的信息增益:
第五章习题 - 图11 第五章习题 - 图12 第五章习题 - 图13 第五章习题 - 图14

得信息增益比:
第五章习题 - 图15 第五章习题 - 图16 第五章习题 - 图17 第五章习题 - 图18

选择信息增益比最大的特征第五章习题 - 图19作为根节点特征,将训练集分为两个子集第五章习题 - 图20第五章习题 - 图21,由于第五章习题 - 图22中只有同一类样本点,所以它是一个叶节点,标记为“是”,对第五章习题 - 图23第五章习题 - 图24中选择新的特征,第五章习题 - 图25中的元素有:

第五章习题 - 图26
重新计算各个特征的信息增益比
经验熵:
第五章习题 - 图27

信息增益:
第五章习题 - 图28
其中第五章习题 - 图29分别表示第五章习题 - 图30第五章习题 - 图31取值为青年,中年,老年的样本子集。

第五章习题 - 图32
其中第五章习题 - 图33分别表示第五章习题 - 图34第五章习题 - 图35取值为否,是的样本子集。

第五章习题 - 图36
其中第五章习题 - 图37分别表示第五章习题 - 图38第五章习题 - 图39取值为一般,好,非常好的样本子集。

信息增益比:
第五章习题 - 图40

第五章习题 - 图41

第五章习题 - 图42
选择信息增益比最大的特征第五章习题 - 图43作为节点的特征,从这一结点引出两个子结点:一个对应“是”(有工作)的子结点,包含3个样本,它们属于同一类,所以这是一个叶结点,类标记为“是”;另一个是对应“否”(无工作)的子结点,包含6个样本,它们也属于同一类,所以这也是一个叶结点,类标记为“否”。
最终的决策树如图:
image.png

代码:

例题5.3用ID3算法生成决策树(信息增益)

  1. import numpy as np
  2. from math import log
  3. def loadData():
  4. datasets = [['青年', '否', '否', '一般', '否'],
  5. ['青年', '否', '否', '好', '否'],
  6. ['青年', '是', '否', '好', '是'],
  7. ['青年', '是', '是', '一般', '是'],
  8. ['青年', '否', '否', '一般', '否'],
  9. ['中年', '否', '否', '一般', '否'],
  10. ['中年', '否', '否', '好', '否'],
  11. ['中年', '是', '是', '好', '是'],
  12. ['中年', '否', '是', '非常好', '是'],
  13. ['中年', '否', '是', '非常好', '是'],
  14. ['老年', '否', '是', '非常好', '是'],
  15. ['老年', '否', '是', '好', '是'],
  16. ['老年', '是', '否', '好', '是'],
  17. ['老年', '是', '否', '非常好', '是'],
  18. ['老年', '否', '否', '一般', '否'],
  19. ]
  20. labels = [u'年龄', u'有工作', u'有自己的房子', u'信贷情况', u'类别']
  21. # 返回数据集和每个维度的名称
  22. return datasets, labels
  23. # 计算熵
  24. def calc_entropy(datasets):
  25. label_count = {}
  26. for dataset in datasets:
  27. label = dataset[-1]
  28. if label not in label_count:
  29. label_count[label] = 0
  30. label_count[label] += 1
  31. entropy = -sum([(p/len(datasets))*log(p/len(datasets),2) for p in label_count.values()])
  32. return entropy
  33. # 计算条件熵
  34. def calc_conditional_entropy(datasets, index = 0):
  35. feature_data = {}
  36. for dataset in datasets:
  37. feature = dataset[index]
  38. if feature not in feature_data:
  39. feature_data[feature] = []
  40. feature_data[feature].append(dataset)
  41. condEntropy = sum([(len(p)/len(datasets))*calc_entropy(p) for p in feature_data.values()])
  42. return condEntropy
  43. # 计算信息增益
  44. def info_gain(entropy, condEntropy):
  45. return entropy - condEntropy
  46. def info_gain_train_childTree(datasets, labels):
  47. entropy = calc_entropy(datasets)
  48. features = []
  49. for index in range(len(datasets[0])-1):
  50. condEntropy = calc_conditional_entropy(datasets, index)
  51. c_info_gain = info_gain(entropy, condEntropy)
  52. features.append((index, c_info_gain))
  53. print("特征({})的信息增益为{:.3f}".format(labels[index], c_info_gain))
  54. best_feature = max(features, key=lambda x: x[-1])
  55. print("特征({})的信息增益最大,选择为当前节点特征".format(labels[best_feature[0]]))
  56. return best_feature
  57. def info_gain_train(datasets, labels):
  58. label_count = {}
  59. for dataset in datasets:
  60. label = dataset[-1]
  61. if label not in label_count:
  62. label_count[label] = 0
  63. label_count[label] += 1
  64. if len(label_count.keys()) == 1:
  65. key = list(label_count.keys())[0]
  66. print("此时类别均为{}".format(key))
  67. return
  68. best_feature = info_gain_train_childTree(datasets, labels)
  69. feature_data = {}
  70. for dataset in datasets:
  71. feature = dataset[best_feature[0]]
  72. if feature not in feature_data:
  73. feature_data[feature] = []
  74. feature_data[feature].append(dataset)
  75. for data in zip(feature_data.keys(), feature_data.values()):
  76. print("当{}为{}".format(labels[best_feature[0]], data[0]))
  77. info_gain_train(data[1], labels)
  78. if __name__ == "__main__":
  79. datasets, labels = loadData()
  80. info_gain_train(datasets, labels)

运行结果:

  1. 特征(年龄)的信息增益为0.083
  2. 特征(有工作)的信息增益为0.324
  3. 特征(有自己的房子)的信息增益为0.420
  4. 特征(信贷情况)的信息增益为0.363
  5. 特征(有自己的房子)的信息增益最大,选择为当前节点特征
  6. 当有自己的房子为否
  7. 特征(年龄)的信息增益为0.252
  8. 特征(有工作)的信息增益为0.918
  9. 特征(有自己的房子)的信息增益为0.000
  10. 特征(信贷情况)的信息增益为0.474
  11. 特征(有工作)的信息增益最大,选择为当前节点特征
  12. 当有工作为否
  13. 此时类别均为否
  14. 当有工作为是
  15. 此时类别均为是
  16. 当有自己的房子为是
  17. 此时类别均为是

C4.5生成算法(信息增益比)

  1. import numpy as np
  2. from math import log
  3. def loadData():
  4. datasets = [['青年', '否', '否', '一般', '否'],
  5. ['青年', '否', '否', '好', '否'],
  6. ['青年', '是', '否', '好', '是'],
  7. ['青年', '是', '是', '一般', '是'],
  8. ['青年', '否', '否', '一般', '否'],
  9. ['中年', '否', '否', '一般', '否'],
  10. ['中年', '否', '否', '好', '否'],
  11. ['中年', '是', '是', '好', '是'],
  12. ['中年', '否', '是', '非常好', '是'],
  13. ['中年', '否', '是', '非常好', '是'],
  14. ['老年', '否', '是', '非常好', '是'],
  15. ['老年', '否', '是', '好', '是'],
  16. ['老年', '是', '否', '好', '是'],
  17. ['老年', '是', '否', '非常好', '是'],
  18. ['老年', '否', '否', '一般', '否'],
  19. ]
  20. labels = [u'年龄', u'有工作', u'有自己的房子', u'信贷情况', u'类别']
  21. # 返回数据集和每个维度的名称
  22. return datasets, labels
  23. def calc_entropy(datasets, index=-1):
  24. label_count = {}
  25. for dataset in datasets:
  26. label = dataset[index]
  27. if label not in label_count:
  28. label_count[label] = 0
  29. label_count[label] += 1
  30. entropy = -sum([(p/len(datasets))*log(p/len(datasets),2) for p in label_count.values()])
  31. return entropy
  32. def calc_conditional_entropy(datasets, index = 0):
  33. feature_data = {}
  34. for dataset in datasets:
  35. feature = dataset[index]
  36. if feature not in feature_data:
  37. feature_data[feature] = []
  38. feature_data[feature].append(dataset)
  39. condEntropy = sum([(len(p)/len(datasets))*calc_entropy(p) for p in feature_data.values()])
  40. return condEntropy
  41. def info_gain(entropy, condEntropy):
  42. return entropy - condEntropy
  43. def info_gain_ratio(c_info_gain, c_entropy):
  44. return 0 if c_info_gain == 0 else c_info_gain / c_entropy
  45. def info_gain_train_childTree(datasets, labels):
  46. entropy = calc_entropy(datasets)
  47. features = []
  48. for index in range(len(datasets[0])-1):
  49. condEntropy = calc_conditional_entropy(datasets, index)
  50. c_info_gain = info_gain(entropy, condEntropy)
  51. c_entropy = calc_entropy(datasets, index)
  52. c_info_gain_ratio = info_gain_ratio(c_info_gain, c_entropy)
  53. features.append((index, c_info_gain_ratio))
  54. print("特征({})的信息增益比为{:.3f}".format(labels[index], c_info_gain_ratio))
  55. best_feature = max(features, key=lambda x: x[-1])
  56. print("特征({})的信息增益比最大,选择为当前节点特征".format(labels[best_feature[0]]))
  57. return best_feature
  58. def info_gain_train(datasets, labels):
  59. label_count = {}
  60. for dataset in datasets:
  61. label = dataset[-1]
  62. if label not in label_count:
  63. label_count[label] = 0
  64. label_count[label] += 1
  65. if len(label_count.keys()) == 1:
  66. key = list(label_count.keys())[0]
  67. print("此时类别均为{}".format(key))
  68. return
  69. best_feature = info_gain_train_childTree(datasets, labels)
  70. feature_data = {}
  71. for dataset in datasets:
  72. feature = dataset[best_feature[0]]
  73. if feature not in feature_data:
  74. feature_data[feature] = []
  75. feature_data[feature].append(dataset)
  76. for data in zip(feature_data.keys(), feature_data.values()):
  77. print("当{}为{}".format(labels[best_feature[0]], data[0]))
  78. info_gain_train(data[1], labels)
  79. if __name__ == "__main__":
  80. datasets, labels = loadData()
  81. info_gain_train(datasets, labels)

运行结果:

  1. 特征(年龄)的信息增益比为0.052
  2. 特征(有工作)的信息增益比为0.352
  3. 特征(有自己的房子)的信息增益比为0.433
  4. 特征(信贷情况)的信息增益比为0.232
  5. 特征(有自己的房子)的信息增益比最大,选择为当前节点特征
  6. 当有自己的房子为否
  7. 特征(年龄)的信息增益比为0.164
  8. 特征(有工作)的信息增益比为1.000
  9. 特征(有自己的房子)的信息增益比为0.000
  10. 特征(信贷情况)的信息增益比为0.340
  11. 特征(有工作)的信息增益比最大,选择为当前节点特征
  12. 当有工作为否
  13. 此时类别均为否
  14. 当有工作为是
  15. 此时类别均为是
  16. 当有自己的房子为是
  17. 此时类别均为是

5.2


已知下表所示的训练数据,试用平方误差损失准则生成一个二叉回归树.

x__i 1 2 3 4 5 6 7 8 9 10
y__i 4.50 4.75 4.91 5.34 5.80 7.05 7.70 8.23 8.70 9.00

回归树的建立算法:
image.png

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. #节点定义
  4. class TreeNode(object):
  5. def __init__(self, tempR, tempc):
  6. self.R = tempR
  7. self.c = tempc
  8. self.left = None
  9. self.right = None
  10. # y的值
  11. y = np.array([4.5, 4.75, 4.91, 5.34, 5.8, 7.05, 7.9, 8.23, 8.7, 9])
  12. #CART算法建立回归树
  13. def CART(start, end):
  14. # 切点s的选择表示R1为x值小于等于s的点,R2为大于s的点
  15. if(end - start >= 1):
  16. result = []
  17. for s in range(start+1, end+1): # s在(start, end]之间取值
  18. y1 = y[start : s] # y1取索引为[start, s]之间的值
  19. y2 = y[s: end+1] # y2取索引为[s+1, end]之间的值
  20. result.append((y1.std()**2)*y1.size + (y2.std()**2)*y2.size)
  21. # std即标准差函数,求标准差的时候默认除以元素的个数,因此平方后乘以元素个数才是要求的平方差
  22. index1 = result.index(min(result)) + start # 取平方差误差最小的索引值
  23. root = TreeNode(y[start:end+1], min(result))
  24. # 索引值为0-9,x值为1-10,即s的值比求的索引值多1
  25. print("节点元素值为",y[start:end+1], " s =",index1+1, " 最小平方误差为",min(result))#输出s值和最小平方误差
  26. root.left = CART(start, index1) # 对列表的左侧生成左子树
  27. root.right = CART(index1+1, end) # 对列表的右侧生成右子树
  28. else:
  29. root = None
  30. return root
  31. if __name__ == "__main__":
  32. root = CART(0, 9)

运行结果:

  1. 节点元素值为 [4.5 4.75 4.91 5.34 5.8 7.05 7.9 8.23 8.7 9. ] s = 5 最小平方误差为 3.3587199999999986
  2. 节点元素值为 [4.5 4.75 4.91 5.34 5.8 ] s = 3 最小平方误差为 0.1912
  3. 节点元素值为 [4.5 4.75 4.91] s = 1 最小平方误差为 0.012800000000000023
  4. 节点元素值为 [4.75 4.91] s = 2 最小平方误差为 0.0
  5. 节点元素值为 [5.34 5.8 ] s = 4 最小平方误差为 0.0
  6. 节点元素值为 [7.05 7.9 8.23 8.7 9. ] s = 7 最小平方误差为 0.6625166666666665
  7. 节点元素值为 [7.05 7.9 ] s = 6 最小平方误差为 0.0
  8. 节点元素值为 [8.23 8.7 9. ] s = 8 最小平方误差为 0.04500000000000021
  9. 节点元素值为 [8.7 9. ] s = 9 最小平方误差为 0.0

计算过程:
以根节点为例,变换切分点s,选择使得平方误差最小的切分点
s=1:
第五章习题 - 图46
此时有第五章习题 - 图47第五章习题 - 图48
然后依次将s从2取值到10,计算平方误差,选其中平方误差最小的s为根节点s,将元素分为左右子树后,再对左右子树进行相同的处理。