本文是李航老师的《统计学习方法》[1]一书的代码复现。

作者:黄海广[2]

备注:代码都可以在github[3]中下载。

我将陆续将代码发布在公众号“机器学习初学者”,敬请关注。

代码目录

  • 第 1 章 统计学习方法概论

  • 第 2 章 感知机

  • 第 3 章 k 近邻法

  • 第 4 章 朴素贝叶斯

  • 第 5 章 决策树

  • 第 6 章 逻辑斯谛回归

  • 第 7 章 支持向量机

  • 第 8 章 提升方法

  • 第 9 章 EM 算法及其推广

  • 第 10 章 隐马尔可夫模型

  • 第 11 章 条件随机场

  • 第 12 章 监督学习方法总结

代码参考:wzyonggege[4],WenDesi[5],火烫火烫的[6]

第 5 章 决策树

1.分类决策树模型是表示基于特征对实例进行分类的树形结构。决策树可以转换成一个if-then规则的集合,也可以看作是定义在特征空间划分上的类的条件概率分布。

2.决策树学习旨在构建一个与训练数据拟合很好,并且复杂度小的决策树。因为从可能的决策树中直接选取最优决策树是 NP 完全问题。现实中采用启发式方法学习次优的决策树。

决策树学习算法包括 3 部分:特征选择、树的生成和树的剪枝。常用的算法有 ID3、 C4.5 和 CART。

3.特征选择的目的在于选取对训练数据能够分类的特征。特征选择的关键是其准则。常用的准则如下:

(1)样本集合对特征的信息增益(ID3)

其中,是数据集的熵,是数据集的熵,是数据集对特征的条件熵。 是中特征取第个值的样本子集,是中属于第类的样本子集。是特征取 值的个数,是类的个数。

(2)样本集合对特征的信息增益比(C4.5)

其中,是信息增益,是数据集的熵。

(3)样本集合的基尼指数(CART)

特征条件下集合的基尼指数:

4.决策树的生成。通常使用信息增益最大、信息增益比最大或基尼指数最小作为特征选择的准则。决策树的生成往往通过计算信息增益或其他指标,从根结点开始,递归地产生决策树。这相当于用信息增益或其他准则不断地选取局部最优的特征,或将训练集分割为能够基本正确分类的子集。

5.决策树的剪枝。由于生成的决策树存在过拟合问题,需要对它进行剪枝,以简化学到的决策树。决策树的剪枝,往往从已生成的树上剪掉一些叶结点或叶结点以上的子树,并将其父结点或根结点作为新的叶结点,从而简化生成的决策树。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inlinefrom sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from collections import Counter
import math
from math import log
import pprint

书上题目 5.1

# 书上题目5.1
def create_data():datasets = [['青年', '否', '否', '一般', '否'],['青年', '否', '否', '好', '否'],['青年', '是', '否', '好', '是'],['青年', '是', '是', '一般', '是'],['青年', '否', '否', '一般', '否'],['中年', '否', '否', '一般', '否'],['中年', '否', '否', '好', '否'],['中年', '是', '是', '好', '是'],['中年', '否', '是', '非常好', '是'],['中年', '否', '是', '非常好', '是'],['老年', '否', '是', '非常好', '是'],['老年', '否', '是', '好', '是'],['老年', '是', '否', '好', '是'],['老年', '是', '否', '非常好', '是'],['老年', '否', '否', '一般', '否'],]labels = [u'年龄', u'有工作', u'有自己的房子', u'信贷情况', u'类别']# 返回数据集和每个维度的名称return datasets, labels
datasets, labels = create_data()
train_data = pd.DataFrame(datasets, columns=labels)
train_data
年龄 有工作 有自己的房子 信贷情况 类别
0 青年 一般
1 青年
2 青年
3 青年 一般
4 青年 一般
5 中年 一般
6 中年
7 中年
8 中年 非常好
9 中年 非常好
10 老年 非常好
11 老年
12 老年
13 老年 非常好
14 老年 一般
# 熵
def calc_ent(datasets):data_length = len(datasets)label_count = {}for i in range(data_length):label = datasets[i][-1]if label not in label_count:label_count[label] = 0label_count[label] += 1ent = -sum([(p / data_length) * log(p / data_length, 2)for p in label_count.values()])return ent
# def entropy(y):
#     """
#     Entropy of a label sequence
#     """
#     hist = np.bincount(y)
#     ps = hist / np.sum(hist)
#     return -np.sum([p * np.log2(p) for p in ps if p > 0])# 经验条件熵
def cond_ent(datasets, axis=0):data_length = len(datasets)feature_sets = {}for i in range(data_length):feature = datasets[i][axis]if feature not in feature_sets:feature_sets[feature] = []feature_sets[feature].append(datasets[i])cond_ent = sum([(len(p) / data_length) * calc_ent(p) for p in feature_sets.values()])return cond_ent# 信息增益
def info_gain(ent, cond_ent):return ent - cond_entdef info_gain_train(datasets):count = len(datasets[0]) - 1ent = calc_ent(datasets)
#     ent = entropy(datasets)best_feature = []for c in range(count):c_info_gain = info_gain(ent, cond_ent(datasets, axis=c))best_feature.append((c, c_info_gain))print('特征({}) - info_gain - {:.3f}'.format(labels[c], c_info_gain))# 比较大小best_ = max(best_feature, key=lambda x: x[-1])return '特征({})的信息增益最大,选择为根节点特征'.format(labels[best_[0]])
info_gain_train(np.array(datasets))
特征(年龄) - info_gain - 0.083
特征(有工作) - info_gain - 0.324
特征(有自己的房子) - info_gain - 0.420
特征(信贷情况) - info_gain - 0.363'特征(有自己的房子)的信息增益最大,选择为根节点特征'

利用 ID3 算法生成决策树,例 5.3

# 定义节点类 二叉树
class Node:def __init__(self, root=True, label=None, feature_name=None, feature=None):self.root = rootself.label = labelself.feature_name = feature_nameself.feature = featureself.tree = {}self.result = {'label:': self.label,'feature': self.feature,'tree': self.tree}def __repr__(self):return '{}'.format(self.result)def add_node(self, val, node):self.tree[val] = nodedef predict(self, features):if self.root is True:return self.labelreturn self.tree[features[self.feature]].predict(features)class DTree:def __init__(self, epsilon=0.1):self.epsilon = epsilonself._tree = {}# 熵@staticmethoddef calc_ent(datasets):data_length = len(datasets)label_count = {}for i in range(data_length):label = datasets[i][-1]if label not in label_count:label_count[label] = 0label_count[label] += 1ent = -sum([(p / data_length) * log(p / data_length, 2)for p in label_count.values()])return ent# 经验条件熵def cond_ent(self, datasets, axis=0):data_length = len(datasets)feature_sets = {}for i in range(data_length):feature = datasets[i][axis]if feature not in feature_sets:feature_sets[feature] = []feature_sets[feature].append(datasets[i])cond_ent = sum([(len(p) / data_length) * self.calc_ent(p)for p in feature_sets.values()])return cond_ent# 信息增益@staticmethoddef info_gain(ent, cond_ent):return ent - cond_entdef info_gain_train(self, datasets):count = len(datasets[0]) - 1ent = self.calc_ent(datasets)best_feature = []for c in range(count):c_info_gain = self.info_gain(ent, self.cond_ent(datasets, axis=c))best_feature.append((c, c_info_gain))# 比较大小best_ = max(best_feature, key=lambda x: x[-1])return best_def train(self, train_data):"""input:数据集D(DataFrame格式),特征集A,阈值etaoutput:决策树T"""_, y_train, features = train_data.iloc[:, :-1], train_data.iloc[:,-1], train_data.columns[:-1]# 1,若D中实例属于同一类Ck,则T为单节点树,并将类Ck作为结点的类标记,返回Tif len(y_train.value_counts()) == 1:return Node(root=True, label=y_train.iloc[0])# 2, 若A为空,则T为单节点树,将D中实例树最大的类Ck作为该节点的类标记,返回Tif len(features) == 0:return Node(root=True,label=y_train.value_counts().sort_values(ascending=False).index[0])# 3,计算最大信息增益 同5.1,Ag为信息增益最大的特征max_feature, max_info_gain = self.info_gain_train(np.array(train_data))max_feature_name = features[max_feature]# 4,Ag的信息增益小于阈值eta,则置T为单节点树,并将D中是实例数最大的类Ck作为该节点的类标记,返回Tif max_info_gain < self.epsilon:return Node(root=True,label=y_train.value_counts().sort_values(ascending=False).index[0])# 5,构建Ag子集node_tree = Node(root=False, feature_name=max_feature_name, feature=max_feature)feature_list = train_data[max_feature_name].value_counts().indexfor f in feature_list:sub_train_df = train_data.loc[train_data[max_feature_name] ==f].drop([max_feature_name], axis=1)# 6, 递归生成树sub_tree = self.train(sub_train_df)node_tree.add_node(f, sub_tree)# pprint.pprint(node_tree.tree)return node_treedef fit(self, train_data):self._tree = self.train(train_data)return self._treedef predict(self, X_test):return self._tree.predict(X_test)
datasets, labels = create_data()
data_df = pd.DataFrame(datasets, columns=labels)
dt = DTree()
tree = dt.fit(data_df)
tree
{'label:': None, 'feature': 2, 'tree': {'否': {'label:': None, 'feature': 1, 'tree': {'否': {'label:': '否', 'feature': None, 'tree': {}}, '是': {'label:': '是', 'feature': None, 'tree': {}}}}, '是': {'label:': '是', 'feature': None, 'tree': {}}}}
dt.predict(['老年', '否', '否', '一般'])
'否'

scikit-learn 实例

# data
def create_data():iris = load_iris()df = pd.DataFrame(iris.data, columns=iris.feature_names)df['label'] = iris.targetdf.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']data = np.array(df.iloc[:100, [0, 1, -1]])# print(data)return data[:, :2], data[:, -1]X, y = create_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
from sklearn.tree import DecisionTreeClassifier
from sklearn.tree import export_graphviz
import graphviz
clf = DecisionTreeClassifier()
clf.fit(X_train, y_train,)
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=None,max_features=None, max_leaf_nodes=None,min_impurity_decrease=0.0, min_impurity_split=None,min_samples_leaf=1, min_samples_split=2,min_weight_fraction_leaf=0.0, presort=False, random_state=None,splitter='best')
clf.score(X_test, y_test)
0.9666666666666667
tree_pic = export_graphviz(clf, out_file="mytree.pdf")
with open('mytree.pdf') as f:dot_graph = f.read()
graphviz.Source(dot_graph)

参考资料

[1] 《统计学习方法》: https://baike.baidu.com/item/统计学习方法/10430179
[2] 黄海广: https://github.com/fengdu78
[3] github: https://github.com/fengdu78/lihang-code
[4] wzyonggege: https://github.com/wzyonggege/statistical-learning-method
[5] WenDesi: https://github.com/WenDesi/lihang_book_algorithm
[6] 火烫火烫的: https://blog.csdn.net/tudaodiaozhale

关于本站

“机器学习初学者”公众号由是黄海广博士创建,黄博个人知乎粉丝23000+,github排名全球前100名(33000+)。本公众号致力于人工智能方向的科普性文章,为初学者提供学习路线和基础资料。原创作品有:吴恩达机器学习个人笔记、吴恩达深度学习笔记等。

往期精彩回顾

  • 那些年做的学术公益-你不是一个人在战斗

  • 适合初学者入门人工智能的路线及资料下载

  • 吴恩达机器学习课程笔记及资源(github标星12000+,提供百度云镜像)

  • 吴恩达深度学习笔记及视频等资源(github标星8500+,提供百度云镜像)

  • 《统计学习方法》的python代码实现(github标星7200+)

  • 机器学习的数学精华(在线阅读版)

备注:加入本站微信群或者qq群,请回复“加群

复现经典:《统计学习方法》第 5 章 决策树相关推荐

  1. 统计学习方法——第1章(个人笔记)

    统计学习方法--第1章 统计学习及监督学习概论 <统计学习方法>(第二版)李航,学习笔记 1.1 统计学习 1.特点 (1)以计算机及网络为平台,是建立在计算机及网络上的: (2)以数据为 ...

  2. 李航《统计学习方法》第二章课后答案链接

    李航<统计学习方法>第二章课后答案链接 李航 统计学习方法 第二章 课后 习题 答案 http://blog.csdn.net/cracker180/article/details/787 ...

  3. 李航《统计学习方法》第一章课后答案链接

    李航<统计学习方法>第一章课后答案链接 李航 统计学习方法 第一章 课后 习题 答案 http://blog.csdn.net/familyshizhouna/article/detail ...

  4. 统计学习方法笔记第二章-感知机

    统计学习方法笔记第二章-感知机 2.1 感知机模型 2.2感知机学习策略 2.2.1数据集的线性可分型 2.2.2感知机学习策略 2.3感知机学习算法 2.3.1感知机算法的原始形式 2.3.2算法的 ...

  5. 统计学习方法 - 第1章 - 概论

    全书章节 第1章 统计学习方法概论 第2章 感知机 第3章 k近邻法 第4章 朴素贝叶斯法 第5章 决策树 第6章 逻辑斯谛回归与最大熵模型 第7章 支持向量机 第8章 提升方法 第9章 EM算法及其 ...

  6. 统计学习方法 | 第7章 支持向量机

    第7章 支持向量机 <统计学习方法>Python代码实现 [转载自Github开源项目]https://github.com/fengdu78/lihang-code 1.支持向量机最简单 ...

  7. 一篇详解带你再次重现《统计学习方法》——第二章、感知机模型

    个性签名:整个建筑最重要的是地基,地基不稳,地动山摇. 而学技术更要扎稳基础,关注我,带你稳扎每一板块邻域的基础. 博客主页:七归的博客 专栏:<统计学习方法>第二版--个人笔记 创作不易 ...

  8. 《统计学习方法》第一章总结

    统计学习是关于计算机基于数据构建概率统计模型并运用模型对数据进行预测与分析的一门学科. 统计学习的对象是数据.目的是对数据进行预测和分析. 统计学习关于数据的基本假设是同类数据具有一定的统计规律性,这 ...

  9. 统计学习方法-李航-第一章:统计学习方法概论-笔记1

    文章目录 0 机器学习分类 0.1 监督学习 0.2 无监督学习 0.3 半监督学习 0.4 强化学习 1 统计学习方法概论 1.1 监督学习的步骤 1.2 统计学习三要素 1.3 模型评估 1.4 ...

  10. 机器学习——统计学习方法——第1章 统计学习及监督学习概论

    监督学习是从标注数据中学习模型的机器学习问题,是统计学习的重要组成部分. 1.1 统计学习 统计学习的特点 统计学习是关于计算机基于数据构建概率统计模型并运用模型对数据进行预测与分析的一门学科. 特点 ...

最新文章

  1. 获取Java接口的所有实现类
  2. 【webGL入门2】点线面的绘制
  3. 通过Java,Spring Boot应用程序将Gmail用作SMTP服务器
  4. 第七季2:MP4v2库的移植与播放实战
  5. C语言版数据结构及算法_快速排序
  6. 开源软件、开源硬件、……开源餐馆来了
  7. ebpf深入理解和应用介绍
  8. MySQL 执行原生sql
  9. win2003安装邮箱服务器,windows2003邮件服务器
  10. 对缓存的思考——提高命中率
  11. 给pmbus添加mfr_id, mfr_version, mfr_model节点显示
  12. 第 1 课:KNX 与 Arduino:硬件连接
  13. 计算机程序编辑器中文版,QuickEdit电脑版
  14. Excel 去重找其他几列的最大值
  15. BZOJ5294 BJOI2018二进制(线段树)
  16. Python学习日记04
  17. 今日早报 每日精选12条新闻简报 每天一分钟 知晓天下事 9月24日
  18. DataGridView 单击选中一整行,只能单选,不能选择多行,只能选择一行
  19. 网站建设安全隐患有哪些,网站如何确保安全性?
  20. 低代码与BPM有什么区别?

热门文章

  1. 贝叶斯理论在机器学习中的应用
  2. linux、sql 常用的一些特殊符号
  3. EOS page问题
  4. Jquery 概念性内容编辑器
  5. 使用iis发布wcf服务
  6. 微型计算机硬件认识和拆装,贵州省习水县第一中学张天梅
  7. python处理字符_常用python字符串处理
  8. Python-OpenCV学习 -- 台式机外接USB摄像头的视频读取
  9. 医疗器械软件网络安全法规和标准概述(附所有标准)
  10. SQLite学习手册(命令行工具)