机器学习之决策树

我的个人理解决策树是通过一些离散!!!!feature来确定某些物品的label


学习之前先要学习俩个东西,信息量,熵(香农熵)


信息(informa)和消息(massage)是有去别的

消息中提到的事件A发生的概率决定了这条消息中的信息量 I

I(A) = -log2P(A)

比如 1.明天太阳会从东方升起

        2.今晚彩票中奖号码是xxxxxx

第一条的I为0,第二条巨大


熵(H)的计算是一个完备事件(x1x2...xn)中  P(xi)×I(xi)    和


引入本次算法的所有包

from math import log
import operator
import copy
import pickle

首先是给定数据集计算H

def calc_shannon_ent(data):     # 传入数据
    num_entries = len(data)     # 计算数据大小
    label_count = {}            # 准备存标签
    for feat_vec in data:       # 逐行读取
        current_label = feat_vec[-1]        # 每行的最后个单位为标签
        label_count[current_label] = label_count.get(current_label, 0) + 1  # 标签值+1
        # if current_label not in label_count.keys():
        #     label_count[current_label] = 0
        # label_count[current_label] += 1
    shannon_ent = 0.0           # 熵 初值
    for key in label_count:     
        prob = float(label_count[key])/num_entries  # 计算标签发生概率
        shannon_ent -= prob * log(prob, 2)          # 叠加 H
    return shannon_ent                             # 返回H


可以先建立一个简单的数据集

离开水能否生活

是否有脚蹼

是否是鱼

1

1

Yes

1

1

Yes

1

0

No

0

1

No

0

1

No

def creatdata():
    data = [[1, 1, 'yes'],
            [1, 1, 'yes'],
            [1, 0, 'no'],
            [0, 1, 'no'],
            [0, 1, 'no']]
    labels = ['no surfacing', 'flippers']
    print(labels)
    return data, labels

这样就可以测试一下本数据集的熵了

if __name__ == '__main__':
    data_main, labels_main = creatdata()
    shannon_ent_main = calc_shannon_ent(data_main)
    print(shannon_ent_main)

下面是结果

['no surfacing', 'flippers']
0.9709505944546686

可以验证一下

yes 的概率是.6,no的概率是.4

ll = [0.4, 0.6]
h = 0.0
for p in ll:
    h -= p * log(p, 2)
print(h)
0.9709505944546686


下面就该是决策树了

设想一下实际问题肯定不会只有两个特征,也不会只有两个标签

所有我们想更快地找到被测目标的标签就需要一种特别好的决策树了来划分数据集


就上面假设的数据来看

有两种方法实现决策树

先判定第一个特征还是第二个特征

离开水是否能活

1(能) 概率3/5

0(不能)  概率2/5

结果

结果

1(是鱼) 概率2/3

0(不是鱼) 概率1/3

1(是鱼) 概率0

0(不是鱼) 概率1

是否有脚蹼

1(有) 概率4/5

0(没有)  概率1/5

结果

结果

1(是鱼) 概率1/2

0(不是鱼) 概率1/2

1(是鱼) 概率0

0(不是鱼) 概率1

第一个的 H = 0.55097

第二个的 H = 0.8

所以选择第一个,下面是代码

划分数据集

def splitdata(data_sp, labels_index_sp, value_sp):  # 数据  界定特征在列表中的index  界定值
    return_data = []
    for feat_vec in data_sp:                                # 提取每行数据
        if feat_vec[labels_index_sp] == value_sp: # 如果特征值与指定特征值匹配  ep:有脚蹼/没脚蹼
            reduce_feat_vec = feat_vec[:labels_index_sp]    # 得到表内第一列到特征前一列
            reduce_feat_vec.extend(feat_vec[labels_index_sp + 1:])  
            # 将表内特征后一列到最后  扩展到列表同行
            return_data.append(reduce_feat_vec)              # 将删除特征列的行,添加到返回列表
    return return_data

根据信息增量,选择最佳特征

def choose_bestfeature_to_spilt(data_ch):
    num_feature = len(data_ch[0]) - 1           # 特征个数
    base_entropy = calc_shannon_ent(data_ch)    # 得到数据集的H
    best_info_gain = 0.0                        # 信息增量
    best_feature = -1
    for i in range(num_feature):
        feature_list = [example[i] for example in data_ch]
        unique_vals = set(feature_list)             # 删除重复数据 set就是数学中的集合
        new_entropy = 0.0
        for value in unique_vals:
            sub_data = splitdata(data_ch, i, value)
            prob = len(sub_data)/float(len(data_ch))
            new_entropy += prob * calc_shannon_ent(sub_data)
        info_gain = base_entropy - new_entropy
        if info_gain > best_info_gain:
            best_info_gain = info_gain
            best_feature = i
    return best_feature

主函数中

if __name__ == '__main__':
    data_main, labels_main = creatdata()
    shannon_ent_main = calc_shannon_ent(data_main)
    a = choose_bestfeature_to_spilt(data_main)
    print(a)

结果

['no surfacing', 'flippers']
0

注意这里返回的是最佳划分特征在特征列表中的index

print(labels_main[a])
no surfacing

然后递归生成我们的决策树

def create_tree(data_creat, labels_creat):
    del_labels = copy.deepcopy(labels_creat)        # 深复制
    class_list = [example[-1] for example in data_creat]
    if class_list.count(class_list[0]) == len(class_list):  # 标签列表中第一个的数量等于标签列表的长度
        return class_list[0]                                # 也就是标签只剩下一个,停止划分,返回标签
    if len(data_creat[0]) == 1:                             # 遍历到最后一个标签
        return major_count(class_list)                      # 返回标签列表中出现次数最多的
    best_feat = choose_bestfeature_to_spilt(data_creat)     # 得到index
    best_feat_label = del_labels[best_feat]                 # 得到标签
    my_tree = {best_feat_label: {}}
    del(del_labels[best_feat])                      # 删除最佳标签
    feat_values = [example[best_feat] for example in data_creat]
    unique_vals = set(feat_values)
    for value in unique_vals:                       # 根据最佳标签的取值划分数据
        sub_labels = del_labels[:]
        my_tree[best_feat_label][value] = create_tree(splitdata(data_creat, best_feat, value), sub_labels)
        # 再次划分,划分后的数据
        #  {'label1': {'label11': {}, 'label12': {}},
        #   'label2': {'label21': {}, 'label22': {}}}       类似于这种      
    return my_tree
中间有一个
major_count()

这个函数用来提取标签列表中出现次数最多的

def major_count(list_cnt):
    count_cnt = {}
    for vote in list_cnt:
        count_cnt[vote] = count_cnt.get(vote, 0) + 1
    sorted_count_cnt = sorted(count_cnt.items(), 
                              key=operator.itemgetter(1), reverse=True)
    return sorted_count_cnt[0][0]

这个时候就可以看下我们的树长什么样

if __name__ == '__main__':
    data_main, labels_main = creatdata()
    shannon_ent_main = calc_shannon_ent(data_main)
    my_trees = create_tree(data_main, labels_main)
    print(my_trees)

结果就是

{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

表示先判断 {能不能活,为0 就不是鱼,为1 就判断{有没有脚蹼,为1 就是鱼,为0 就不是鱼}}

现在可以用我们的树来判断到底是不是鱼了

def getlabel(input_trees, featlabels, testfeats): # ,特征标签列表,待测对象
    first_str = list(input_trees.keys())[0]         # 得到树的首个特征(递归调用不一定是0)
    second_dict = input_trees[first_str]           # 保存下级字典
    feat_index = featlabels.index(first_str)     # 得到首个特征的index(递归调用不一定是第一个特征)
    for key in second_dict.keys():                  # 遍历下级字典
        if testfeats[feat_index] == key:           # 匹配第一个特征值
            if type(second_dict[key]).__name__ == 'dict':   # 表示还需要判断下一个特征值
                class_labels_test = getlabel(second_dict[key], featlabels, testfeats)# 继续判断
            else:
                class_labels_test = second_dict[key]    # 保存标签
    return class_labels_test

主函数

if __name__ == '__main__':
    data_main, labels_main = creatdata()
    shannon_ent_main = calc_shannon_ent(data_main)
    my_trees = create_tree(data_main, labels_main)
    result_main = getlabel(my_trees, labels_main, [0, 0])
    print(result_main)
no

用pickle保存我们的树,以便下次使用

保存树

def store_tree(input_trees_st, filename):
    fw = open(filename, 'wb')
    pickle.dump(input_trees_st, fw)
    fw.close()

提取树

def grab_tree(filename):
    fr = open(filename, 'rb')
    return pickle.load(fr)

主函数

if __name__ == '__main__':
    data_main, labels_main = creatdata()
    shannon_ent_main = calc_shannon_ent(data_main)
    my_trees = create_tree(data_main, labels_main)
    result_main = getlabel(my_trees, labels_main, [0, 0])
    # print(result_main)
    store_tree(my_trees, 'getlabelStorage.txt')
    a = grab_tree('getlabelStorage.txt')
    print(a)
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}



使用决策树预测隐形眼镜类型

先写一个文件处理函数

前面已经写过了直接贴代码

def parse_file(filename):
    fr_parse = open(filename)
    len_parse = []
    for ins in fr_parse.readlines():
        lenses = ins.strip()
        lenses = lenses.split('\t')
        len_parse.append(lenses)
    return len_parse
if __name__ == '__main__':
    data_main, labels_main = creatdata()
    shannon_ent_main = calc_shannon_ent(data_main)
    # my_trees = create_tree(data_main, labels_main)
    # result_main = getlabel(my_trees, labels_main, [0, 0])
    # print(result_main)
    # store_tree(my_trees, 'getlabelStorage.txt')
    # a = grab_tree('getlabelStorage.txt')
    # print(a)
    len_main = parse_file('lenses.txt')
    lenses_labels = ['age', 'prescript', 'astigmatic', 'tear_rate']
    lenses_trees = create_tree(len_main, lenses_labels)
    print(lenses_trees)

{'tear_rate': {'reduced': 'no lenses',
               'normal': {'astigmatic': {'yes': {'prescript': {'myope': 'hard',
                                                               'hyper': {'age': {'presbyopic': 'no lenses',
                                                                                 'pre': 'no lenses',
                                                                                 'young': 'hard'}}}},
                                         'no': {'age': {'presbyopic': {'prescript': {'myope': 'no lenses',
                                                                                     'hyper': 'soft'}},
                                                        'pre': 'soft',
                                                        'young': 'soft'}}}}}}

Tearrate

Reduced

Norlma

No lenses

Astigmatic

Yes

no

Prescript

age

Myope

Hyper

Pre

Young

Presbyopic

Hard

Age

soft

soft

Prescript

Presbyopic

Pre

Young

Myope

Hyper

No lenses

No lense

Hard

No lenses

Soft

lenses.txt文件下载

https://pan.baidu.com/s/1a-XUPL_XRrAoO3XoQ4GYkA

欧了

阅读更多
想对作者说点什么? 我来说一句

机器学习实战决策树python实现

2015年06月13日 4KB 下载

没有更多推荐了,返回首页

不良信息举报

机器学习之决策树

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭