决策树-原理与Sklearn库实现(2)机器学习实战
上篇文章对决策树的基本实现过程进行了了解,关键是几种不同的特征划分方式,sklearn的特点就是代码简单,编码比较简洁,而且使用起来很方便,在基本了解决策树的实现过程后,接下来我们用时下比较流行的Sklearn库实现决策树的建模与绘制。
首先看一下sklearn的决策树建模与绘图效果,数据还是上一篇里最基础的判断是否为鱼的数据:
from sklearn import tree
import os
import pydotplus
from IPython.display import Image
#数据集以及数据标签
dataSet = [[1,1],
[1,1],
[1,0],
[0,1],
[0,1]]
labels = ['yes','yes','no','no','no']
os.environ["PATH"] += os.pathsep + 'C:/Program Files (x86)/Graphviz2.37/bin/'#设置path:graphviz在电脑中的存储位置
clf = tree.DecisionTreeClassifier(criterion = 'gini')#训练决策树模型,特征划分选择'GINI'系数
clf = clf.fit(dataSet,labels)#模型适应输入数据以及对应标签
#绘制决策树图形,并导出为pdf
dot_data = tree.export_graphviz(clf, out_file=None)
graph = pydotplus.graph_from_dot_data(dot_data)
graph.write_pdf("102.pdf")
#绘制决策树图形,在ipython界面调用即可得到决策树图形
dot_data = tree.export_graphviz(clf, out_file=None,
feature_names=None,
class_names=None,
filled=True, rounded=True,
special_characters=True,node_ids = True)
graph = pydotplus.graph_from_dot_data(dot_data)
Image(graph.create_png())
先看一下代码的效果,分别是导出的pdf图和ipython中得到的决策树图形。
相比起来,更喜欢第二种,看着比较好看一些~
看完效果,这里详细介绍一下决策树的建模函数DecisionTreeClassifier和绘图函数export_raphviz函数。
DecisionTreeClassifier()
先看一下这个函数的完全形态:
DecisionTreeClassifier
(criterion=’gini’, splitter=’best’, max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None, random_state=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, class_weight=None, presort=False)
第一眼看到这么多参数,内心是崩溃的,心想着设置一下特征分类方法什么的不就ok了,但是现实就是这么残酷,所以就一个参数一个参数往下看。
cirterion(特征选择):
这里default=”gini”,可选参数:‘Gini’-基尼系数 ,‘entropy’ -信息熵;选择特征划分的标准。
splitter(特征划分点选择标准):
用于在每个节点选择分割的策略。支持的策略是选择最佳分割和“随机”来选择最佳随机分割的“最佳”策略。可选参数:‘best’,‘random’,best在特征的所有划分低点中寻找最优划分点,random则是随机的在划分但范围内选择局部最优的划分点,一般来说,样本量不大选择‘best’,样本量比较大时,为了提高效率,可以选择‘random’。
max_depth(决策树最大深度):
设置决策树的最大深度,int或None,可选(default = None),如果没有设置,则扩展节点直到所有叶子都是纯粹的或者直到所有叶子都包含少于min_samples_split样本。
min_samples_split(内部节点划分所需最小样本数):
分割内部节点所需的最少样本数量,int,float,可选(default = 2),其实就是子树继续划分的条件,如果少于设置的数字,则不根据最有特征划分进行划分。
min_samples_leaf(叶子节点最小样本数):
要求在叶节点上的最小样本数量,int,float,可选(default= 1),就是限制叶子节点的最小样本数。
min_weight_fraction_leaf(叶子节点最小样本权重和):
需要在叶节点处的所有输入样本权重总和的最小加权分数。没有提供sample_weight时,样本具有相同的权重。这里默认为0.0,即不考虑权重问题,如果数据有缺失值或者数据之间有较大偏差,可以考虑引入样本权重。
max_features(划分时考虑的最大特征数):
默认是None,意味着考虑数据集的所有特征值,如果是int,则考虑每个分割处的max_features特征;如果为float,则max_features是百分比,int(max_features * n_features)是在每个分割处考虑的;如果是“auto”,那么max_features = sqrt(n_features);如果“sqrt”,则max_features = sqrt(n_features);如果“log2”则max_features = log2(n_features)。n是样本总特征数,n比较小时,默认None即可,但n特别大时,就要根据特征变量的重要性等设置合适的数值。
random_state(随机状态):
int,RandomState实例或None,可选(default = None),如果是int,random_state是随机数发生器使用的种子;如果RandomState实例,random_state是随机数生成器;如果没有,则随机数生成器是np.random使用的RandomState实例。
max_leaf_nodes(最大叶子节点数):
用max_leaf_nodes以best-first方式生长一棵树。最佳节点被定义为杂质的相对减少。如果没有那么无限的叶节点数。这里默认None,防止模型过拟合,如果特征数不多时,可以不考虑。
min_impurity_decrease(最低杂质减少值):
float,可选(default= 0)。如果此分割导致大于或等于该值的杂质减少,则节点将被分割。
min_impurity_split(节点划分最小不纯度):
float,树木生长早期停止的阈值。如果杂质高于阈值,节点将分裂,否则它是叶子。自0.19版以后不推荐使用:min_impurity_split已被弃用,优先于0.19的min_impurity_decrease,将在0.21中被删除。改为使用min_impurity_decrease。
class_weight(分类权重):
dict, list of dicts, “balanced” or None, default=None,与{class_label:weight}形式的类相关的权重。如果没有给出,所有特征都应该有一个权重。对于多输出问题,可以按照与y列相同的顺序提供一个dicts列表。
presort(数据是否排序) :
bool, 可选 (default=False)。是否推迟数据以加速拟合中最佳分裂的发现,对于大型数据集上的决策树的默认设置,将其设置为true可能会减慢训练过程。
以上就是DecisionTreeClassifier()参数的全部含义,想要了解更多的话可以参考点击打开链接,这里还有tree函数的更多用法。
export_raphviz():
老样子,看一下函数的完全形态:
export_graphviz
(decision_tree, out_file=”tree.dot”, max_depth=None, feature_names=None, class_names=None, label=’all’, filled=False, leaves_parallel=False, impurity=True, node_ids=False, proportion=False, rotate=False, rounded=False, special_characters=False, precision=3)
好吧,又是这么多参数。事实证明我学的还只是一些决策树的一点最基本的东西,想要了解更多相关的东西,自己仍需努力啊,先看一下各个参数的含义。
out_file(文件对象):
文件对象或字符串,可选,default = ‘tree.dot’,处理或输出文件的名称,如果为None,则结果以字符串形式返回。
max_depth(最大深度):
int,可选,default - None,如果不设置,树就可以*生长了。
feature_names(分类特征名):
字符串列表,可选,default = None,这里可以添加划分类别的一个列表,例如前例中就可以添加['live without water','have feet or not'],则在输出的决策树图形中会显示出类别名称,也可以设置为None,则不显示。
claas_names(类别名):
字符串列表,bool或None,这里可以添加分类类别的一个列表,例如前例中就可以添加[' fish ','Not fish'],则在输出的决策树图形中会显示出类别名称,也可以设置为None,则不显示。每个目标类的名称按数字升系,只与分类有关,不支持多输出,如果为True,则显示类名的符号显示。
label(标签):
是否为杂质信息显示信息标签等,选项包括‘all’,显示在每个节点上,‘root’只显示在最上面的根节点上,‘None’不显示任何节点。
filled():
bool,可选,default = False,设置为真时,绘制节点以指示分类的多数类别,回归值的极值或多输出节点的纯度。
leavels_parallel():
bool,可选,deault = False ,设置为True时,绘制树地步的所有叶节点,这个选项设置为True时,返回了一个比较扁平的树,看着比较奇怪,大家可以试一下。
inpurity(杂质):
bool,可选,default = True,默认为True时,显示每个节点的杂质。
Node_ids():
bool,可选,default = False,设置为True时,在每个节点显示ID号。其实就是根据树的生长过程,根据先后顺序为每个节点标号,如果需要具体指出哪个节点,可以设置为True,方便找到。
proportion(比例):
bool,可选,default = False ,设置为True时,将值或样本的显示分别改为比例和百分比。
rotate(旋转):
bool,default = False,设置为True时,树会从左向右生长,而不是自上而下,比较有意思,大家可以试一下。
rounded(舍入):
bool,可选,default = False ,设置为True时,绘制图角的节点框。这个其实就是设置每个节点的形状,一种是方形,一种是圆角,个人倾向于False,比较好看。
special_characters():
bool,可选,default = False,设置为False时,忽略PostScript兼容性的特殊字符(= =)。
precisions(精度):
int,可选,default = 3,每个节点的杂质值,阀值和值属性中精度的位数。
return:
dot_data (字符串-决策树的字符型表示,有点像上个例子里返回的列表嵌套)
以上就是export_raphviz()的全部参数含义,这个函数主要是设置生成决策树的形状和决策树显示的信息,可以多试试参数,得到信息不同的决策树,更多的export_raphviz()信息可以参考点击打开链接。
了解了决策树建模函数和绘图函数后,看到书后还有一个隐形眼镜的例子,于是立马用两个函数进行了套用,但是。。。
fr = open('lenses.txt')
lenses = [inst.strip().split('\t') for inst in fr.readlines()]
lensesLabel = ['age','prescript','astigmatic','tearRate']
X = []
y = []
for i in range(24):
X.append(lenses[i][:-1])
y.append(lenses[i][-1])
clf = tree.DecisionTreeClassifier(criterion = 'gini')
clf = clf.fit(X,y)
显示无法将字符转换为浮点,所以应该是训练数据出了问题,先看下训练数据长什么样子。
ValueError: could not convert string to float: 'young'
原来是属性中包含字符,不过这不是问题,是时候派出LabelEncoder()函数来为属性编码了,我的思路是编写一个循环,将 特每一列的特征全提取出来,然后用Encoder去fit,然后再encoder去transform每一列,最后用append函数用循环把处理好的编码连在一起,形成训练集X。这一步编写的不是太简洁,以后会优化一下。
from sklearn import preprocessing
le = preprocessing.LabelEncoder()
a = []
label1 = []
label2 = []
label3 = []
label4 = []
for i in range(24):
a.append(lenses[i][0])
le.fit(a)
for i in range(24):
label1.append(lenses[i][0])
data1 = le.transform(label1)
for i in range(24):
a.append(lenses[i][1])
le.fit(a)
for i in range(24):
label2.append(lenses[i][1])
data2 = le.transform(label2)
for i in range(24):
a.append(lenses[i][2])
le.fit(a)
for i in range(24):
label3.append(lenses[i][2])
data3 = le.transform(label3)
le.fit(data3)
data3 = le.transform(data3)
for i in range(24):
a.append(lenses[i][3])
le.fit(a)
for i in range(24):
label4.append(lenses[i][3])
data4 = le.transform(label4)
le.fit(data4)
data4 = le.transform(data4)
print(data4)
train_data = []
for i in range(24):
data =[]
data.append(list(data1)[i])
data.append(list(data2)[i])
data.append(list(data3)[i])
data.append(list(data4)[i])
train_data.append(data)
print(train_data)
labels = []
for i in range(24):
labels.append(lenses[i][-1])
[2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1]
[1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0]
[0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1]
[1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0]
这是第一步逐行进行编码后得到的四行新的数据,然后取每一列为一个训练集,得到最后的训练集train_data和labels。
有了train_data,label,再来试一次!
from sklearn import tree
import os
import pydotplus
from IPython.display import Image
from sklearn import preprocessing
os.environ["PATH"] += os.pathsep + 'C:/Program Files (x86)/Graphviz2.37/bin/'
clf = tree.DecisionTreeClassifier(criterion = 'gini')
clf = clf.fit(train_data,labels)
import pydotplus
dot_data = tree.export_graphviz(clf, out_file=None)
graph = pydotplus.graph_from_dot_data(dot_data)
graph.write_pdf("102.pdf")
from IPython.display import Image
dot_data = tree.export_graphviz(clf, out_file=None,
feature_names=None,
class_names=None,
filled=True, rounded=True,
special_characters=True,node_ids = True)
graph = pydotplus.graph_from_dot_data(dot_data)
Image(graph.create_png())
[Finished in 3.8s]
这次成功了~在ipython调用Image(graph.create_png()),看一下生成的决策树图:
总结:
决策树的实现过程看似简单,但其实这其中蕴含了很多数学知识,这里只是简单的实现了建模和绘图,而还有预测,准确率验证 ,剪枝等许多问题需要去做去学,而自己只是刚刚学习了一些基本的东西,还有更多更广的内容需要去探索!