决策树

决策树基本上就是把我们以前的经验总结出来。这里有一个打篮球的训练集。假如我们要出门打篮球,一般会根据“天气”、“温度”、“湿度”、“刮风”这几个条件来判断,最后得到结果:去打篮球?还是不去?

上面这个图就是一棵典型的决策树。我们在做决策树的时候,会经历两个阶段:构造和剪枝。

 

构造

什么是构造呢?构造就是生成一棵完整的决策树。简单来说,构造的过程就是选择什么属性作为节点的过程,那么在构造过程中,会存在三种节点:

1.根节点:就是树的最顶端,最开始的那个节点。在上图中,“天气”就是一个根节点;

2.内部节点:就是树中间的那些节点,比如说“温度”、“湿度”、“刮风”;

3.叶节点:就是树最底部的节点,也就是决策结果。

节点之间存在父子关系。比如根节点会有子节点,子节点会有子子节点,但是到了叶节点就停止了,叶节点不存在子节点。那么在其实构造就是对应着三个重要的问题:

1.选择哪个属性作为根节点;

2.选择哪些属性作为子节点;

3.什么时候停止并得到目标状态,即叶节点。

 

剪枝

决策树构造出来之后是不是就万事大吉了呢?也不尽然,我们可能还需要对决策树进行剪枝。剪枝就是给决策树瘦身,这一步想实现的目标就是,不需要太多的判断,同样可以得到不错的结果。之所以这么做,是为了防止“过拟合”(Overfitting)现象的发生。过拟合,它指的就是模型的训练结果“太好了”,以至于在实际应用的过程中,会存在“死板”的情况,导致分类错误。

那么如何解决刚才构造的三个问题呢?

第三个问题叶节点一般就是对应我们的目标,比如去或者不去

而前两个问题可以合并成一个问题,那就是以最重要特征作为根节点以此类推

那么如何计算特征的重要性呢?这里就需要用到信息熵这个概念:它表示了信息的不确定度。

 

 

 1 def entropy(elements):
 2     """
 3     计算熵
 4     (各个元素出现次数/总数 * (各个元素出现次数/总数)的对数)的总数取负
 5     :param elements:
 6     :return:
 7     """
 8     counter = Counter(elements)
 9     probs = [counter[c] / len(elements) for c in elements]
10     return - sum(p * np.log(p) for p in probs)

 

假设有 2 个集合

集合 1:5 次去打篮球,1 次不去打篮球;

集合 2:3 次去打篮球,3 次不去打篮球。

在集合 1 中,有 6 次决策,其中打篮球是 5 次,不打篮球是 1 次。那么假设:类别 1 为“打篮球”,即次数为 5;类别 2 为“不打篮球”,即次数为 1。那么节点划分为类别 1 的概率是 5/6,为类别 2 的概率是 1/6,带入上述信息熵公式可以计算得出:

同样,集合 2 中,也是一共 6 次决策,其中类别 1 中“打篮球”的次数是 3,类别 2“不打篮球”的次数也是 3,那么信息熵为多少呢?我们可以计算得出:

 

 

 

很明显,如果按某特征区分后,样本越均匀,则信息熵越大。相反如果某特征带来的信息熵越小,那么它越好的,我们确定根节点则需要找到这个信息熵最小的节点就是了。

 

 

 1 def find_the_min_spilter(training_data: pd.DataFrame, target: str) -> str:
 2     """
 3     寻找分割后最小熵的分割特征
 4     :param training_data: 数据
 5     :param target: 目标特征
 6     :return:
 7     """
 8     # 将数据去除目标特征
 9     x_fields = set(training_data.columns.tolist()) - {target}
10 
11     spliter = None
12     min_entropy = float('inf')          # 预先设置成正无穷
13 
14     for f in x_fields:                  # 循环所有特征名
15         values = set(training_data[f])  # 取该特征所有数值做集合
16         for v in values:                # 循环该特征所有数值集合
17             sub_spliter_1 = training_data[training_data[f] == v][target].tolist()       # 计算等于该数值对目标特征分布
18             entropy_1 = entropy(sub_spliter_1)                                          # 计算等于该数值时目标特征分布信息熵
19             sub_spliter_2 = training_data[training_data[f] != v][target].tolist()       # 计算不等于该数值对目标特征分布
20             entropy_2 = entropy(sub_spliter_2)                                          # 计算不等于该数值时目标特征分布信息熵
21             entropy_v = entropy_1 + entropy_2                                           # 计算此数值对目标特征的总信息熵
22 
23             if entropy_v <= min_entropy:        # 如果信息熵小于当前最小信息熵则更新信息熵与特征
24                 min_entropy = entropy_v
25                 spliter = (f, v)
26 
27     print('spliter is: {}'.format(spliter))
28     print('the min entropy is: {}'.format(min_entropy))
29     print('----------------------')
30 
31     return spliter

 

posted @ 2019-12-06 22:37  北方姆Q  阅读(375)  评论(0编辑  收藏  举报