威海建设信息网站,互联网软件外包平台,wordpress如何登录,外贸接单软件文章目录1. 决策树模型与学习2. 特征选择2.1 特征选择Python代码3. 决策树的生成3.1 Python代码4. 决策树的剪枝5. CART 算法6. sklearn 例子6.1 书上贷款例子6.2 鸢尾花 及 决策树可视化附. 本文完整代码决策树#xff08;decision tree#xff09;是一种基本的分类与回归方…
文章目录1. 决策树模型与学习2. 特征选择2.1 特征选择Python代码3. 决策树的生成3.1 Python代码4. 决策树的剪枝5. CART 算法6. sklearn 例子6.1 书上贷款例子6.2 鸢尾花 及 决策树可视化附. 本文完整代码决策树decision tree是一种基本的分类与回归方法。
分类问题中基于特征对实例进行分类的过程。优点模型具有可读性分类速度快。学习利用训练数据根据损失函数最小化的原则建立决策树模型。预测对新的数据利用决策树模型进行分类。
决策树学习通常包括3个步骤特征选择、决策树生成、决策树修剪。
Quinlan在1986年提出的ID3算法、1993年提出的C4.5算法 Breiman等人在1984年提出的CART算法
1. 决策树模型与学习
决策树由结点node和有向边directed edge组成。
内部结点internal node和叶结点leaf node。内部结点表示一个特征或属性叶结点表示一个类。用决策树分类从根结点开始对实例的某一特征进行测试根据测试结果将实例分配到其子结点这时每一个子结点对应着该特征的一个取值。递归地对实例进行测试并分配直至达到叶结点。最后将实例分到叶结点的类中。
决策树学习本质从训练数据集中归纳出一组分类规则。
需要一个与训练数据矛盾较小的决策树同时具有很好的泛化能力。决策树学习的损失函数通常是正则化的极大似然函数。决策树学习的策略损失函数为目标函数的最小化。
2. 特征选择
决策树训练时高度太高对训练数据准确率高但泛化能力差需要剪枝。
常用的准则
1样本集合DDD对特征AAA的 信息增益ID3
g(D,A)H(D)−H(D∣A)g(D, A)H(D)-H(D|A)g(D,A)H(D)−H(D∣A)
H(D)−∑k1K∣Ck∣∣D∣log2∣Ck∣∣D∣H(D)-\sum_{k1}^{K} \frac{\left|C_{k}\right|}{|D|} \log _{2} \frac{\left|C_{k}\right|}{|D|}H(D)−k1∑K∣D∣∣Ck∣log2∣D∣∣Ck∣
H(D∣A)∑i1n∣Di∣∣D∣H(Di)H(D | A)\sum_{i1}^{n} \frac{\left|D_{i}\right|}{|D|} H\left(D_{i}\right)H(D∣A)i1∑n∣D∣∣Di∣H(Di)
其中H(D)H(D)H(D)是数据集DDD的熵H(Di)H(D_i)H(Di)是数据集DiD_iDi的熵H(D∣A)H(D|A)H(D∣A)是数据集DDD对特征AAA的条件熵。 DiD_iDi是DDD中特征AAA取第iii个值的样本子集CkC_kCk是DDD中属于第kkk类的样本子集。nnn是特征AAA取值的个数KKK是类的个数。
熵越大随机变量的不确定性就越大信息增益information gain表示得知特征X的信息而使得类Y的信息的不确定性减少的程度。选择信息增益 大的
2样本集合DDD对特征AAA的 信息增益比C4.5
gR(D,A)g(D,A)HA(D)g_{R}(D, A)\frac{g(D, A)}{H_A(D)}gR(D,A)HA(D)g(D,A)
其中g(D,A)g(D,A)g(D,A)是信息增益HA(D)H_A(D)HA(D)是数据集DDD关于特征AAA的熵。
3样本集合DDD的 基尼指数CART
Gini(D)1−∑k1K(∣Ck∣∣D∣)2\operatorname{Gini}(D)1-\sum_{k1}^{K}\left(\frac{\left|C_{k}\right|}{|D|}\right)^{2}Gini(D)1−k1∑K(∣D∣∣Ck∣)2
特征AAA条件下集合DDD的基尼指数
Gini(D,A)∣D1∣∣D∣Gini(D1)∣D2∣∣D∣Gini(D2)\operatorname{Gini}(D, A)\frac{\left|D_{1}\right|}{|D|} \operatorname{Gini}\left(D_{1}\right)\frac{\left|D_{2}\right|}{|D|} \operatorname{Gini}\left(D_{2}\right)Gini(D,A)∣D∣∣D1∣Gini(D1)∣D∣∣D2∣Gini(D2)
基尼指数表示集合D的不确定性基尼指数 Gini(D,A)Gini(D,A)Gini(D,A) 表示经AaAaAa分割后集合DDD的不确定性。基尼指数值越大样本集合的不确定性也就越大这一点与熵相似。选择 基尼指数 小的
2.1 特征选择Python代码
def get_data():datasets [[青年, 否, 否, 一般, 否],[青年, 否, 否, 好, 否],[青年, 是, 否, 好, 是],[青年, 是, 是, 一般, 是],[青年, 否, 否, 一般, 否],[中年, 否, 否, 一般, 否],[中年, 否, 否, 好, 否],[中年, 是, 是, 好, 是],[中年, 否, 是, 非常好, 是],[中年, 否, 是, 非常好, 是],[老年, 否, 是, 非常好, 是],[老年, 否, 是, 好, 是],[老年, 是, 否, 好, 是],[老年, 是, 否, 非常好, 是],[老年, 否, 否, 一般, 否],]labels [u年龄, u有工作, u有自己的房子, u信贷情况, u分类]# 字符串前加 u, 后面字符串以 Unicode 格式 进行编码一般用在中文字符串前面防止乱码return datasets, labels;
# ---------书上贷款例子-----------------
datasets, labels get_data()def cal_entropy(datasets): # 经验熵H(D)data_len len(datasets)label_count {}for i in range(data_len):label datasets[i][-1]if label not in label_count:label_count[label] 0label_count[label] 1entropy -sum([(p / data_len) * log(p / data_len, 2) for p in label_count.values()])return entropydef cond_entropy(datasets, axis0): # 经验条件熵H(D|A)data_len len(datasets)feature_set {}for i in range(data_len):feature datasets[i][axis]if feature not in feature_set:feature_set[feature] []feature_set[feature].append(datasets[i])cond_ent sum([(len(p) / data_len) * cal_entropy(p) for p in feature_set.values()])return cond_entdef info_gain(entropy, cond_ent): # 信息增益return entropy - cond_entdef info_gain_train(datasets): # 基于特征信息增益的特征选择count len(datasets[0]) - 1entropy cal_entropy(datasets)best_feature []for i in range(count):info_gain_i info_gain(entropy, cond_entropy(datasets, axisi))best_feature.append((i, info_gain_i))print(特征{}- info_gain - {:.3f}.format(labels[i], info_gain_i))best_feature_i max(best_feature, keylambda x: x[-1])print(特征{}的信息增益最大选为根节点的特征.format(labels[best_feature_i[0]]))info_gain_train(np.array(datasets))特征年龄- info_gain - 0.083
特征有工作- info_gain - 0.324
特征有自己的房子- info_gain - 0.420
特征信贷情况- info_gain - 0.363
特征有自己的房子的信息增益最大选为根节点的特征3. 决策树的生成
通常使用信息增益最大、信息增益比最大或基尼指数最小作为特征选择的准则。
决策树的生成往往通过计算信息增益或其他指标从根结点开始递归地产生决策树。 这相当于用信息增益或其他准则不断地选取局部最优的特征或将训练集分割为能够基本正确分类的子集。
ID3算法只有树的生成所以该算法生成的树容易产生过拟合C4.5算法与ID3算法相似进行了改进。C4.5在生成的过程中用信息增益比来选择特征。
3.1 Python代码
class Node():def __init__(self, rootTrue, labelNone, feature_nameNone, featureNone):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): # 类似str方法更侧重程序员调试print({}.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, epsilon0.1): # 信息增益阈值 epsilon 时结束决策树展开self.epsilon epsilonself._tree {}staticmethoddef cal_entropy(datasets): # 经验熵H(D)data_len len(datasets)label_count {}for i in range(data_len):label datasets[i][-1]if label not in label_count:label_count[label] 0label_count[label] 1entropy -sum([(p / data_len) * log(p / data_len, 2) for p in label_count.values()])return entropydef cond_entropy(self, datasets, axis0): # 经验条件熵H(D|A)data_len len(datasets)feature_set {}for i in range(data_len):feature datasets[i][axis]if feature not in feature_set:feature_set[feature] []feature_set[feature].append(datasets[i])cond_ent sum([(len(p) / data_len) * self.cal_entropy(p) for p in feature_set.values()])return cond_entstaticmethoddef info_gain(entropy, cond_ent): # 信息增益return entropy - cond_entdef info_gain_train(self, datasets): # 基于特征信息增益的特征选择count len(datasets[0]) - 1entropy self.cal_entropy(datasets)best_feature []for i in range(count):info_gain_i info_gain(entropy, cond_entropy(datasets, axisi))best_feature.append((i, info_gain_i))print(特征{}- info_gain - {:.3f}.format(labels[i], info_gain_i))best_feature_i max(best_feature, keylambda x: x[-1])return best_feature_idef train(self, train_data)::input: 数据集D(DataFrame格式)特征集A阈值eta:return: 决策树DT_, y_train, features train_data.iloc[:, :-1], train_data.iloc[:, -1], train_data.columns[:-1]# 1. 若所有D实例都属于同一分类不用分了直接返回那个类if len(y_train.value_counts()) 1:return Node(rootTrue, labely_train.iloc[0])# 2. 若没有特征A返回D中数量最多的分类if len(features) 0:return Node(rootTrue, labely_train.value_counts().sort_values(ascendingFalse).index[0])# 3. 计算最大信息增益取为特征max_feature, max_info_gain self.info_gain_train(np.array(train_data))max_feature_name features[max_feature]# 4. 如果信息增益小于阈值epsilon置为单节点将实例数最大的类作为节点标记if max_info_gain self.epsilon:return Node(rootTrue, labely_train.value_counts().sort_values(ascendingFalse).index[0])# 5. 构建Ag子集node_tree Node(rootFalse, feature_namemax_feature_name, featuremax_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], axis1)# 6. 递归生成树sub_tree self.train(sub_train_df)node_tree.add_node(f, sub_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)train_data pd.DataFrame(datasets, columnslabels)
dt DTree()
tree dt.fit(train_data)
print(dt.predict([老年, 否, 否, 一般]))
print(dt.predict([青年, 否, 是, 一般]))
print(dt.predict([中年, 是, 否, 好]))
print(dt.predict([老年, 否, 是, 一般]))4. 决策树的剪枝
学习时过多考虑准确性树复杂过拟合泛化能力差需要剪枝。
方法极小化决策树整体损失函数
5. CART 算法
分类与回归树classification and regression treeCART模型
二叉树左分支是右分支否1决策树生成基于训练数据集生成决策树生成的决策树要尽量大 2决策树剪枝用验证数据集对已生成的树进行剪枝并选择最优子树这时用损失函数最小作为剪枝的标准。
6. sklearn 例子
sklearn.tree.DecisionTreeClassifier
class sklearn.tree.DecisionTreeClassifier(criteriongini, splitterbest,max_depthNone, min_samples_split2, min_samples_leaf1, min_weight_fraction_leaf0.0, max_featuresNone, random_stateNone, max_leaf_nodesNone, min_impurity_decrease0.0, min_impurity_splitNone,class_weightNone, presortdeprecated, ccp_alpha0.0)特征选择标准
criterion{“gini”, “entropy”}, default”gini”择优划分、树的最大深度、最小划分几类、叶子节点个数等参数 6.1 书上贷款例子
# ---------书上贷款例子-----------------
datasets, labels get_data()
train_data np.array(pd.DataFrame(datasets, columnslabels))
X_train, y_train train_data[:, :-1], train_data[:, -1:]
encoder preprocessing.OrdinalEncoder() # 将字符转成浮点
encoder.fit(X_train) # 先拟合
X_train encoder.transform(X_train) # 转换成数字
A encoder.transform([[青年, 否, 是, 一般]])
B encoder.transform([[中年, 是, 否, 好]])
C encoder.transform([[老年, 否, 是, 一般]])encoder preprocessing.OrdinalEncoder()
encoder.fit(y_train)
y_train encoder.transform(y_train)
# sklearn 决策树
clf DecisionTreeClassifier()
clf.fit(X_train, y_train)
print(encoder.inverse_transform([clf.predict(A)]))
print(clf.predict_proba(B))
print(clf.predict_proba(C))[[是]]
[[0. 1.]]
[[0. 1.]]6.2 鸢尾花 及 决策树可视化
# ------------鸢尾花---------------
iris load_iris()
df pd.DataFrame(iris.data, columnsiris.feature_names)
df[label] iris.target
df.columns [sepal length, sepal width, petal length, petal width, label]
data np.array(df.iloc[:100, [0, 1, -1]])
X data[:, :2]
y data[:, -1]
X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.3)
clf DecisionTreeClassifier()
print(clf)
clf.fit(X_train, y_train)
print(clf.score(X_test, y_test))# --------------决策树可视化-------------
# 需要安装graphviz,添加path可视化决策树
with open(mytree.dot, w, encodingutf-8) as f:dot_data export_graphviz(clf, out_fileNone, feature_namesdf.columns[:2],filledTrue, roundedTrue, special_charactersTrue, class_namesiris.target_names[0:2])
dot graphviz.Source(dot_data)
dot.view()
# 写入png , pdf
graph pydotplus.graph_from_dot_data(dot_data)
graph.write_png(tree.png)
# cmd: dot -Tpdf tree.dot -o output.pdfdot -Tpng tree.dot -o output.png决策树可视化
附. 本文完整代码
# -*- coding:utf-8 -*-
# Python Version: 3.7
# Time: 2020/3/13 19:36
# Author: Michael Ming
# Website: https://michael.blog.csdn.net/
# File: 5.decisionTree.py
# Reference: https://github.com/fengdu78/lihang-codeimport pandas as pd
import numpy as np
from sklearn import preprocessing
from sklearn.model_selection import train_test_split
from collections import Counter
import math
from math import log
import pprint
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.tree import export_graphviz
import graphviz
import pydotplusdef get_data():datasets [[青年, 否, 否, 一般, 否],[青年, 否, 否, 好, 否],[青年, 是, 否, 好, 是],[青年, 是, 是, 一般, 是],[青年, 否, 否, 一般, 否],[中年, 否, 否, 一般, 否],[中年, 否, 否, 好, 否],[中年, 是, 是, 好, 是],[中年, 否, 是, 非常好, 是],[中年, 否, 是, 非常好, 是],[老年, 否, 是, 非常好, 是],[老年, 否, 是, 好, 是],[老年, 是, 否, 好, 是],[老年, 是, 否, 非常好, 是],[老年, 否, 否, 一般, 否],]labels [u年龄, u有工作, u有自己的房子, u信贷情况, u分类]# 字符串前加 u, 后面字符串以 Unicode 格式 进行编码一般用在中文字符串前面防止乱码return datasets, labels;# ---------书上贷款例子-----------------
datasets, labels get_data()
train_data np.array(pd.DataFrame(datasets, columnslabels))
X_train, y_train train_data[:, :-1], train_data[:, -1:]
encoder preprocessing.OrdinalEncoder() # 将字符转成浮点
encoder.fit(X_train) # 先拟合
X_train encoder.transform(X_train) # 转换成数字
A encoder.transform([[青年, 否, 是, 一般]])
B encoder.transform([[中年, 是, 否, 好]])
C encoder.transform([[老年, 否, 是, 一般]])encoder preprocessing.OrdinalEncoder()
encoder.fit(y_train)
y_train encoder.transform(y_train)
# sklearn 决策树
clf DecisionTreeClassifier()
clf.fit(X_train, y_train)
print(encoder.inverse_transform([clf.predict(A)]))
print(clf.predict_proba(B))
print(clf.predict_proba(C))# --------------决策树可视化-------------
# 需要安装graphviz,添加path可视化决策树
with open(mytree.dot, w, encodingutf-8) as f:dot_data export_graphviz(clf, out_fileNone, feature_namesclf.feature_importances_,filledTrue, roundedTrue, special_charactersTrue)
dot graphviz.Source(dot_data)
# dot.view()
# 写入png , pdf
graph pydotplus.graph_from_dot_data(dot_data)
graph.write_png(tree.png)# cmd: dot -Tpdf tree.dot -o output.pdfdot -Tpng tree.dot -o output.png# -----------自编程抄一遍---------------
# ----特征选择基于信息增益----
def cal_entropy(datasets): # 经验熵H(D)data_len len(datasets)label_count {}for i in range(data_len):label datasets[i][-1]if label not in label_count:label_count[label] 0label_count[label] 1entropy -sum([(p / data_len) * log(p / data_len, 2) for p in label_count.values()])return entropydef cond_entropy(datasets, axis0): # 经验条件熵H(D|A)data_len len(datasets)feature_set {}for i in range(data_len):feature datasets[i][axis]if feature not in feature_set:feature_set[feature] []feature_set[feature].append(datasets[i])cond_ent sum([(len(p) / data_len) * cal_entropy(p) for p in feature_set.values()])return cond_entdef info_gain(entropy, cond_ent): # 信息增益return entropy - cond_entdef info_gain_train(datasets): # 基于特征信息增益的特征选择count len(datasets[0]) - 1entropy cal_entropy(datasets)best_feature []for i in range(count):info_gain_i info_gain(entropy, cond_entropy(datasets, axisi))best_feature.append((i, info_gain_i))print(特征{}- info_gain - {:.3f}.format(labels[i], info_gain_i))best_feature_i max(best_feature, keylambda x: x[-1])print(特征{}的信息增益最大选为根节点的特征.format(labels[best_feature_i[0]]))info_gain_train(np.array(datasets))# -------ID3算法生成决策树---------class Node():def __init__(self, rootTrue, labelNone, feature_nameNone, featureNone):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): # 类似str方法更侧重程序员调试print({}.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, epsilon0.1): # 信息增益阈值 epsilon 时结束决策树展开self.epsilon epsilonself._tree {}staticmethoddef cal_entropy(datasets): # 经验熵H(D)data_len len(datasets)label_count {}for i in range(data_len):label datasets[i][-1]if label not in label_count:label_count[label] 0label_count[label] 1entropy -sum([(p / data_len) * log(p / data_len, 2) for p in label_count.values()])return entropydef cond_entropy(self, datasets, axis0): # 经验条件熵H(D|A)data_len len(datasets)feature_set {}for i in range(data_len):feature datasets[i][axis]if feature not in feature_set:feature_set[feature] []feature_set[feature].append(datasets[i])cond_ent sum([(len(p) / data_len) * self.cal_entropy(p) for p in feature_set.values()])return cond_entstaticmethoddef info_gain(entropy, cond_ent): # 信息增益return entropy - cond_entdef info_gain_train(self, datasets): # 基于特征信息增益的特征选择count len(datasets[0]) - 1entropy self.cal_entropy(datasets)best_feature []for i in range(count):info_gain_i info_gain(entropy, cond_entropy(datasets, axisi))best_feature.append((i, info_gain_i))print(特征{}- info_gain - {:.3f}.format(labels[i], info_gain_i))best_feature_i max(best_feature, keylambda x: x[-1])return best_feature_idef train(self, train_data)::input: 数据集D(DataFrame格式)特征集A阈值eta:return: 决策树DT_, y_train, features train_data.iloc[:, :-1], train_data.iloc[:, -1], train_data.columns[:-1]# 1. 若所有D实例都属于同一分类不用分了直接返回那个类if len(y_train.value_counts()) 1:return Node(rootTrue, labely_train.iloc[0])# 2. 若没有特征A返回D中数量最多的分类if len(features) 0:return Node(rootTrue, labely_train.value_counts().sort_values(ascendingFalse).index[0])# 3. 计算最大信息增益取为特征max_feature, max_info_gain self.info_gain_train(np.array(train_data))max_feature_name features[max_feature]# 4. 如果信息增益小于阈值epsilon置为单节点将实例数最大的类作为节点标记if max_info_gain self.epsilon:return Node(rootTrue, labely_train.value_counts().sort_values(ascendingFalse).index[0])# 5. 构建Ag子集node_tree Node(rootFalse, feature_namemax_feature_name, featuremax_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], axis1)# 6. 递归生成树sub_tree self.train(sub_train_df)node_tree.add_node(f, sub_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)train_data pd.DataFrame(datasets, columnslabels)
dt DTree()
tree dt.fit(train_data)
print(dt.predict([老年, 否, 否, 一般]))
print(dt.predict([青年, 否, 是, 一般]))
print(dt.predict([中年, 是, 否, 好]))
print(dt.predict([老年, 否, 是, 一般]))# ------------鸢尾花---------------
iris load_iris()
df pd.DataFrame(iris.data, columnsiris.feature_names)
df[label] iris.target
df.columns [sepal length, sepal width, petal length, petal width, label]
data np.array(df.iloc[:100, [0, 1, -1]])
X data[:, :2]
y data[:, -1]
X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.3)
clf DecisionTreeClassifier()
print(clf)
clf.fit(X_train, y_train)
print(clf.score(X_test, y_test))
# --------------决策树可视化-------------
# 需要安装graphviz,添加path可视化决策树
with open(mytree.dot, w, encodingutf-8) as f:dot_data export_graphviz(clf, out_fileNone, feature_namesdf.columns[:2],filledTrue, roundedTrue, special_charactersTrue, class_namesiris.target_names[0:2])
dot graphviz.Source(dot_data)
dot.view()
# 写入png , pdf
graph pydotplus.graph_from_dot_data(dot_data)
graph.write_png(tree.png)
# cmd: dot -Tpdf tree.dot -o output.pdfdot -Tpng tree.dot -o output.png