数据挖掘 决策树

简介

决策树, 举两个栗子:

  • 网络上各种心理测试的题, 根据你选的答案, 跳到另一题, 最后得出你是什么性格的人.
  • 图灵测试, 通过设计各种问题来问跟你聊天的人, 在20 个问题以内, 你来判断跟你聊天的是机器人还是人.

以上, 都是决策树的一种形式, 看图就懂:
在这里插入图片描述

  • 判断年龄,->判断性别->是谁

从根节点, 一步一步判断(决策过程), 走到叶子节点得到一个确定的分类

  • 也可以说是通过一步一步获取更多信息 来不断降低事件的不确定性(信息熵)

信息熵

理解:

物理的: 判断空间的混乱程度, 越混乱, 熵值越大
信息熵: 用来衡量事务的不确定性, 事务越不确定, 熵值越大; 随机变量不确定性的度量.
计算公式:
在这里插入图片描述

  • xi 为最后分类的类别
  • p(xi) 为: 第 i 个类别出现的次数 / 总次数
  • 其中 - 号实际意义不大, 是为了把结果取正, log 的是在p(xi) = 1 时, 信息熵最小为0

决策树的优劣

  • 优点:
    1. 复杂度不高, 结果易理解
    2. 过程直观
    3. 只需要一次训练, 存储在磁盘可之后多次使用
  • 缺点:
    1. 可能产生过度匹配(过拟合)问题
  • 使用数据:标称数据或数值数据类型

主要问题

  • 如何更快的得到最后的分类结果, 即如何使决策树的层数最少?
  • 更进一步阐述问题:
    1. 决策树主要需要解决的问题: 每一个节点的选择, 即: 选择什么问题来使你获得更多的信息, 更有效的降低事物的熵.
    2. 如何去掉不必要的分支, 使决策树更小, 即剪枝

解决方案

  • ID3算法
    信息增益
  • C4.5算法
    信息增益率
  • CART算法
    基尼系数

(在此, 我主要讨论ID3算法, 并且只讨论如何选择节点, 不管剪枝问题, 待更新…)

ID3算法

简介

  • id3算法采用信息增益来选择节点
  • 信息增益: 假如当前需分类事务的熵为 0.9, 在得到一个信息后(向下经过一个节点之后), 再次计算此时的熵为 0.4, 那么, 选择该节点的信息增益为 0.9-0.6 = 0.4
  • 只能用标称型数据, 若是数值型需要给数据划分

训练过程

输入: 数据特征及其所属类别
输出: 一棵决策树
此处我们选择的归纳偏好为: 当只有一个属性无法划分, 而存在多个标签时, 选择最多的标签作为最终结果
算法: 采用递归的形式构造, 即从根节点开始, 不断进入子树递归构造
在这里插入图片描述
代码:

def create_tree(dataSet, labels):
   classList = [exam[-1] for exam in dataSet]
   # 1. 若所有的类别相同, 到达叶子节点, 停止划分,
   if classList.count(classList[0]) == len(classList):
       return classList[0]

   # 2. 遍历完了所有特征时, 返回出现最多的类别
   if len(dataSet) == 1:
       return major_class(classList) // 根据归纳偏好设置叶子节点标签

   # 建立一个新的分支
   split_feature = get_best_split_feature(dataSet)
   split_feature_label = labels[split_feature]
   mytree = {split_feature_label:{}}
   del(labels[split_feature])  # 去除当前子标签族的split_feature的标签
   feat_values = set([exam[split_feature] for exam in dataSet])
   for value in feat_values:
       sublabels = labels[:]
       mytree[split_feature_label][value] = create_tree(split_dataset(dataSet, split_feature, >value), sublabels)
   return mytree

信息熵的计算

由公式可知, 信息熵的计算就是训练数据中所有标签出现的概率求和
dataSet: 训练集
返回 ent 香农熵
calc_shannooEnt:
在这里插入图片描述
代码:

def calc_shannonEnt(dataSet):
   """
   计算香农熵
   :param dataSet: 数据集: 格式: 列表  [ [值,值,值, ...],
                                         [值,值,值, ...],
                                         [值,值,值, ...],
                                         ...   ]
   :return: 每一种可能分类的信息熵
   """
   shannonEnt = 0.0
   data_num = len(dataSet)
   # 给最后一个属性的值的种类当作分类
   label = {}  # key:可能的分类, v: 分类的个数
   for features in dataSet:
       if features[-1] not in label.keys():
           label[features[-1]] = 0
       label[features[-1]] += 1
   # 计算香农熵公式:
   for key in label:
       prob = float(label[key]) / data_num
       shannonEnt -= prob * log2(prob)
   return shannonEnt

获取最好的划分特征

  • 信息增益的计算公式
    在这里插入图片描述
    Dv为以属性v划分D后的数据集
    |D|为数据集的样本数量
  • 算法
  1. 计算当前初始数据集的信息熵 base_ent
  2. 遍历当前特征族
    • 按照每个特征来划分数据集得到sub_dataset
    • 计算每个sub_dataset的信息熵new_ent, 并记录对应的划分的特征
    • 该划分的计算信息增益, base_ent - new_int
    • 获取最大的信息增益
  3. 返回得到最大信息增益划分特征

代码:

def get_best_split_feature(dataSet):
   # 因为最后一个属性是分类结果, 所以要 -1
   num_features = len(dataSet[0]) - 1
   base_ent = calc_shannonEnt(dataSet)
   best_info_gain = 0.0
   split_feature = -1
   for i in range(num_features):
       i_feature = set([exam[i] for exam in dataSet]) # 获取第i个属性的所有取值
       new_entropy = 0.0
       # 计算按第i个属性划分的信息增益
       for value in i_feature:
           sub_dataSet = split_dataset(dataSet, i, value)
           prob = len(sub_dataSet) / float(len(dataSet))
           new_entropy = prob * calc_shannonEnt(sub_dataSet)
       info_gain = base_ent - new_entropy

       if info_gain > best_info_gain:
           best_info_gain = info_gain
           split_feature = i
   return split_feature

根据 feature 与 value 划分数据集

  1. 遍历dataset中的 所有样例
  2. 样例的 feature == value 添加到结果集中, 并去掉该样例的这个 feature

代码:

def split_dataset(dataSet, axis, value):
   """
   按照axis=value来划分数据集为
   :param dataSet:
   :param axis: int 划分数据集的featrues
   :param value:
   :return: 按照value划分后的数据集, 并去掉该axis特征(属性)
   """
   split_res = []
   for features in dataSet:
       # 获取去掉该axis特征的结果
       if features[axis] == value:
           axis_after = features[axis + 1:]
           extend = features[0:axis]
           extend.extend(axis_after)
           split_res.append(extend)
   return split_res

ID3算法的缺点与信息增益率:

:下面说的有些计算过程非常不清楚,我自己都看不懂,把公式带上自己算一遍就明白了!

  • 想象: 若给数据集中每个数据一个id, 大家知道 id 是一个无意义的值, 但是, 我们计算器信息增益时, 根据id划分的子集中, 只有一个样本, 而且样本只属于一个类别, 那么公式中, pi = 1, 即 log2(pi) = 0, 所以, 该信息熵为0;
    id的信息增益为base_ent - 0, 最大!
  • 所以, 根据信息增益得到的结果, 更偏向于属性值多的属性。(属性值越多,pi 的分母越小,pi越负趋向于1, log2(pi)越小, 大家自己算一下就清楚了)

因此 提出可以根据信息增益率来确定用来划分的特征
信息增益率:
在这里插入图片描述

  • Gain_ratio(D,a): 以属性a来划分数据D的信息增益率
  • Gain(D,a): 以属性a来划分数据D的信息增益
  • IV(a): 属性a的熵, 即 a 这个属性本身的纯度, 值为:
    在这里插入图片描述

简单说 信息增益率为:
(以属性a划分的信息增益 )gain(D,a) / ent(a) (属性a分裂后的信息熵 = IV(a) )

我们用信息增益率来计算刚刚的极端情况下 ID 的那个例子:
根据之前的计算, 以ID划分得到的gain(D,x)最大,
而 log2(|Dv| / |D|)的分子 |Dv| 都为1最小, 所以 ent(a)取值最大,所以 信息增益率最小

解决了ID3偏向选择值多的特征作为分裂点的问题!

未完待续

猜你喜欢

转载自blog.csdn.net/weixin_40978095/article/details/83628640
今日推荐