决策树和随机森林的python实现

作者:刘梦娇

决策树

决策树是一种有监督的机器学习算法,该方法可以用于解决分类和回归问题。决策树可以简单地理解为达到某一特定结果的一系列决策。包含分类树(classification tree)和回归树(regression tree)。

实现方法

sklearn.tree模块中提供了很多关于决策树的类,tree.DecisionTreeClassifier可实现分类树模型,tree.DecisionTreeRegressor可实现回归树模型。

sklearn.tree.DecisionTreeClassifier(criterion="entropy" 	#不纯度的计算方法。"entropy"表示使用信息熵,ID3算法,此时sklearn实际计算的是基于信息熵的信息增益(Information Gain);"gini"表示使用基尼系数,CART算法
     ,splitter="best" 	#控制决策树中的随机选项。“best”表示在分枝时会优先选择重要的特征进行分枝;“random”表示分枝时会更加随机,常用来防止过拟合
     ,max_depth=10	#限制树的最大深度,深度越大,越容易过拟合。
     ,min_samples_split=5 	#节点必须包含训练样本的个数。
     ,min_samples_leaf=1	#叶子最少包含样本的个数
     ,min_weight_fraction_leaf=0.0	#基于权重的剪枝参数,如果样本是加权的,则使用基于权重的预修剪标准来更容易优化树结构,这确保叶节点至少包含样本权重的总和的一小部分。
     ,max_features=None	#限制分枝的特征个数
     ,random_state=None	#输入任意数字会让模型稳定下来。加上random_state这个参数后,score就不会总是变化。随机性越大,效果一般越好,因此一般不设置,若追求稳定性可设置成固定值
     ,max_leaf_nodes=None 	#最大叶子节点数量,在最佳分枝下,以max_leaf_nodes为限制来生长树。默认没有节点数量的限制。
     ,min_impurity_decrease=0.0	#限制信息增益的大小,信息增益小于设定值分枝不会发生
     ,min_impurity_split=None 	#节点必须含有最小信息增益再划分
     ,class_weight=None	#设置样本的权重,当正反样本差别较大时,又需要对少的样本进行精确估计时使用,搭配min_weight_fraction_leaf来剪枝
     ,presort=False	#是否预先分配数据以加快拟合中最佳分枝的发现。在大型数据集上使用默认设置决策树时,将这个参数设置为True,可能会延长训练过程,降低训练速度。当使用较小数据集或限制树的深度时,设置这个参数为True,可能会加快训练速度。

测试

from sklearn.tree import DecisionTreeClassifier
import pandas as pd
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import precision_score, recall_score, f1_score, confusion_matrix, classification_report
# 导入数据
iris=datasets.load_iris()
X=pd.DataFrame(iris.data,columns=iris.feature_names)
Y=pd.DataFrame(iris.target,columns=['target'])
#分割测试集和训练集
Xtrain,Xtest,Ytrain,Ytest = train_test_split(X,Y,test_size=0.25,random_state=1)

#训练模型
tree_clf=DecisionTreeClassifier()
tree_clf=tree_clf.fit(Xtrain,Ytrain)
Ypred = tree_clf.predict(Xtest)
train_score=tree_clf.score(Xtrain,Ytrain)
test_score=tree_clf.score(Xtest,Ytest)##导入测试集,由接口score得到模型的得分
print(f'train score: {train_score},  test score: {test_score}')

#测试集具体预测情况
confmat = confusion_matrix(y_true=Ytest, y_pred=Ypred)#输出混淆矩阵
print(confmat)# 混淆矩阵并可视化
# 查准率、查全率、F1值
print('precision:%.3f' % precision_score(y_true=Ytest, y_pred=Ypred,average= 'macro'))
print('recall:%.3f' % recall_score(y_true=Ytest, y_pred=Ypred,average= 'macro'))
print('F1:%.3f' % f1_score(y_true=Ytest, y_pred=Ypred,average= 'macro'))

在这里插入图片描述

更好地展示结果

from sklearn import tree
import matplotlib.pyplot as plt
# 对决策树进行美化并保存决策树图片到本地

plt.figure(figsize=(15,15))      #设置画布大小(单位为英寸)
tree.plot_tree(tree_clf             #训练好的决策树评估器
               ,node_ids=True  #显示节点id
               ,filled=True    #给节点填充颜色
               ,rounded=True   #节点方框变成圆角
               ,fontsize=12    #节点中文本的字体大小
              )

plt.savefig("决策树.png")      #保存图片一定要在plt.show()之前
plt.show()

在这里插入图片描述

调参

调整max_depth

from sklearn.model_selection import cross_val_score
scorel = []
for i in range(1,10,1):
    #实例化决策树分类器
    #随机的模式为1
    dtc = DecisionTreeClassifier(max_depth=i,random_state=1)
    #使用交叉验证为我们的模型打分,然后求平均值
    score = cross_val_score(dtc,Xtrain,Ytrain,cv=10,n_jobs=-1).mean()
    #将我们的模型的分数追加到我们的结果列表中
    scorel.append(score)
##将列表中的最大值,也就是最好的打分打印出来
print(max(scorel),([*range(1,10)][scorel.index(max(scorel))]))
plt.figure(figsize=[20,5])
plt.plot(range(1,10,1),scorel)
plt.xlabel('max_depth')
plt.ylabel('accuracy')
plt.show()

在这里插入图片描述

scoring

对模型评价的scoring可设置的参数包括:accuracy、average_precision、f1、f1_micro、f1_macro、f1_weighted、f1_samples、neg_log_loss、roc_auc等

F1-score:是统计学中用来衡量二分类模型精确度的一种指标,用于测量不均衡数据的精度。它同时兼顾了分类模型的精确率和召回率。F1-score可以看作是模型精确率和召回率的一种加权平均,它的最大值是1,最小值是0。

在多分类问题中,如果要计算模型的F1-score,则有两种计算方式,分别为micro-F1和macro-F1,这两种计算方式在二分类中与F1-score的计算方式一样,所以在二分类问题中,计算micro-F1=macro-F1=F1-score,
micro-F1和macro-F1都是多分类F1-score的两种计算方式;
除了micro-F1和macro-F1,还有weighted-F1,是一个将F1-score乘以该类的比例之后相加的结果,也可以看做是macro-F1的变体吧。
micro-F1:多分类问题,若数据极度不平衡会影响结果;
macro-F1:多分类问题,不受数据不平衡影响,容易受到识别性高(高recall、高precision)的类别影响;
由于我们的问题是多分类问题,我们在这里采用f1_micro或者f1_macro。

scorel = []
for i in range(1,10,1):
    #实例化决策树分类器
    #随机的模式为1
    dtc = DecisionTreeClassifier(max_depth=i,random_state=1)
    #使用交叉验证为我们的模型打分,然后求平均值
    score = cross_val_score(dtc,Xtrain,Ytrain,cv=10,scoring='f1_micro',n_jobs=-1).mean()
    #将我们的模型的分数追加到我们的结果列表中
    scorel.append(score)
##将列表中的最大值,也就是最好的打分打印出来
print(max(scorel),([*range(1,10)][scorel.index(max(scorel))]))
plt.figure(figsize=[20,5])
plt.plot(range(1,10,1),scorel)
plt.ylabel('f1_micro')
plt.xlabel('max_depth')
plt.show()

在这里插入图片描述

可以发现,改变scoring的评价方式,对max_depth的选择并没有变化。

利用GridSearchCV确定最佳max_depth

param_grid = {'max_depth':np.arange(1, 10, 1)}
# 一般根据数据的大小来进行一个试探,鸢尾花数据很小,所以可以采用1~10,或者1~20这样的试探
dtc = DecisionTreeClassifier(random_state=1)
GS = GridSearchCV(dtc,param_grid,cv=10,n_jobs=-1)
GS.fit(Xtrain,Ytrain)
GS.best_params_
#{'max_depth': 3}
GS.best_score_
#0.9643939393939395

min_samples_split

param_grid = {'min_samples_split':np.arange(2, 30, 1)}
dtc = DecisionTreeClassifier(random_state=1)
GS = GridSearchCV(dtc,param_grid,cv=10,n_jobs=-1)
GS.fit(Xtrain,Ytrain)
GS.best_params_
#{'min_samples_split': 5}
GS.best_score_
#0.9734848484848484

min_impurity_decrease

param_grid = {'min_impurity_decrease':np.arange(0, 20, 0.2)}
dtc = DecisionTreeClassifier(random_state=1)
GS = GridSearchCV(dtc,param_grid,cv=10,n_jobs=-1)
GS.fit(Xtrain,Ytrain)
GS.best_params_
#{'min_impurity_decrease': 0.0}
GS.best_score_
#0.9553030303030303

max_features

param_grid = {'max_features':np.arange(1, 5, 1)}
dtc = DecisionTreeClassifier(random_state=1)
GS = GridSearchCV(dtc,param_grid,cv=10,n_jobs=-1)
GS.fit(Xtrain,Ytrain)
GS.best_params_
#{'max_features': 4}
GS.best_score_
#0.9553030303030303

多参数同时选优

param_grid = {'max_depth':np.arange(1, 10, 1),'min_samples_split':np.arange(2, 30, 1),'min_impurity_decrease':np.arange(0, 20, 0.2),'max_features':np.arange(1, 5, 1)}
dtc = DecisionTreeClassifier(random_state=1)
GS = GridSearchCV(dtc,param_grid,cv=10,n_jobs=-1)
GS.fit(Xtrain,Ytrain)
GS.best_params_
#{'max_depth': 4,
 #'max_features': 4,
 #'min_impurity_decrease': 0.0,
 #'min_samples_split': 5}
 GS.best_score_
# 0.9734848484848484

采用最优参数

clf = DecisionTreeClassifier(max_depth=4,min_samples_split=5,min_impurity_decrease=0,max_features=4,random_state=1)
tree_clf=clf.fit(Xtrain,Ytrain)## 用决策树模型拟合构造的数据集
Ypred = clf.predict(Xtest)
train_score=clf.score(Xtrain,Ytrain)##导入训练集,由接口score得到模型的得分
test_score=clf.score(Xtest,Ytest)##导入测试集,由接口score得到模型的得分
print(train_score,test_score)

confmat = confusion_matrix(y_true=Ytest, y_pred=Ypred)#输出混淆矩阵
print(confmat)# 混淆矩阵并可视化
# 查准率、查全率、F1值
print('precision:%.3f' % precision_score(y_true=Ytest, y_pred=Ypred,average= 'macro'))
print('recall:%.3f' % recall_score(y_true=Ytest, y_pred=Ypred,average= 'macro'))
print('F1:%.3f' % f1_score(y_true=Ytest, y_pred=Ypred,average= 'macro'))

在这里插入图片描述

特征重要性排序

importances = clf.feature_importances_ 
indices = np.argsort(importances)[::-1]
feat_labels = Xtrain.columns
plt.title('Feature Importance')
plt.bar(range(Xtrain.shape[1]), importances[indices], align='center')
plt.xticks(range(Xtrain.shape[1]), feat_labels[indices], rotation=45)
plt.xlim([-1, Xtrain.shape[1]])
plt.tight_layout()
plt.show()

在这里插入图片描述

随机森林

随机森林算法用多棵(随机生成的)决策树来生成最后的输出结果。sklearn.ensemble模块中的RandomForestClassifier可以实现随机森林。

RandomForestClassifier(n_estimators=100,	随机森林中决策树的个数,默认为 100。若n_estimators太小容易欠拟合,太大不能显著的提升模型。
   criterion='gini',	随机森林中决策树的算法,可选的有两种:gini:基尼系数,也就是CART算法,为默认值。entropy:信息熵,也就是ID3算法。
   max_depth=None, 	决策树的最大深度。决策树最大深度。若等于None,表示决策树在构建最优模型的时候不会限制子树的深度。如果模型样本量多,特征也多的情况下,推荐限制最大深度;若样本量少或者特征少,则不限制最大深度。
   min_samples_split=2, 	节点可分的最小样本数,默认值是2。
   min_samples_leaf=1, 	叶子节点含有的最少样本。若叶子节点样本数小于min_samples_leaf,则对该叶子节点和兄弟叶子节点进行剪枝,只留下该叶子节点的父节点。默认值是1。
   min_weight_fraction_leaf=0.0, 叶节点上所需的总权重的最小加权分数。当没有提供sample_weight时,样本具有相等的权值。
   max_features='auto', 	构建决策树最优模型时考虑的最大特征数。默认是auto,表示最大特征数是N的平方根
   max_leaf_nodes=None, 	最大叶子节点数。int设置节点数,None表示对叶子节点数没有限制。
   min_impurity_decrease=0.0, 节点划分的最小不纯度。假设不纯度用信息增益表示,若某节点划分时的信息增益大于等于min_impurity_decrease,那么该节点还可以再划分;反之,则不能划分。
   min_impurity_split=None, 提前停止的阈值。如果一个节点的不纯度高于阈值,则该节点将拆分,否则,它是一个叶子。
   bootstrap=True, 	是否对样本集进行有放回抽样来构建树,True表示是,默认值True
   oob_score=False, 	是否采用袋外样本来评估模型的好坏,True代表是,默认值False,袋外样本误差是测试数据集误差的无偏估计,所以推荐设置True。
   n_jobs=None, 并行计算数。默认是None
   random_state=None, 控制bootstrap的随机性以及选择样本的随机性。
   verbose=0, 在拟合和预测时控制详细程度。默认是0。
   warm_start=False, 不常用
   class_weight=None, 每个类的权重,可以用字典的形式传入{class_label: weight}。
   ccp_alpha=0.0, 将选择成本复杂度最大且小于ccp_alpha的子树。默认情况下,不执行修剪。
   max_samples=None,如果为None(默认),则抽取X.shape[0]样本。如果为int,则抽取max_samples样本。如果为float,则抽取max_samples*X.shape[0]个样本。因此,max_samples应该在(01)中。是0.22版中的新功能。
   )

测试

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
#sklearn.ensemble库,支持众多集成学习算法和模型
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import cross_val_score
from sklearn.metrics import precision_score, recall_score, f1_score, confusion_matrix, classification_report

from sklearn import datasets
iris=datasets.load_iris()
X=pd.DataFrame(iris.data,columns=iris.feature_names)
Y=pd.DataFrame(iris.target,columns=['target'])
Xtrain,Xtest,Ytrain,Ytest = train_test_split(X,Y,test_size=0.25,random_state=1)

clf=RandomForestClassifier(random_state=1)
#使用.values.ravel()原因>>>>>>>>>>数据转换警告:当需要一维数组时,传递了列向量y。请将Y的形状更改为(n_samples,),例如使用ravel()。
tree_clf=clf.fit(Xtrain,Ytrain.values.ravel())## 用决策树模型拟合构造的数据集
Ypred = clf.predict(Xtest)
train_score=clf.score(Xtrain,Ytrain.values.ravel())##导入训练集,由接口score得到模型的得分
test_score=clf.score(Xtest,Ytest.values.ravel())##导入测试集,由接口score得到模型的得分
print(train_score,test_score)
########测试集具体预测情况
confmat = confusion_matrix(y_true=Ytest.values.ravel(), y_pred=Ypred)#输出混淆矩阵
print(confmat)# 混淆矩阵并可视化
# 查准率、查全率、F1值
print('precision:%.3f' % precision_score(y_true=Ytest.values.ravel(), y_pred=Ypred,average= 'macro'))
print('recall:%.3f' % recall_score(y_true=Ytest.values.ravel(), y_pred=Ypred,average= 'macro'))
print('F1:%.3f' % f1_score(y_true=Ytest.values.ravel(), y_pred=Ypred,average= 'macro'))

在这里插入图片描述

调参

n_estimators

调优随机森林的主要参数是n_estimators参数。一般来说,森林中的树越多,泛化性能越好,但它会减慢拟合和预测的时间。

随机森林调整的第一步:无论如何先来调n_estimators,以10为分隔点

#我们n_estimators对于模型准确率的影响是十分大的,所以需要多次调整
#这一步我们只要目的是在绘制n_estimators的学习曲线。
#这里当然也可以使用GridSearch网格搜索,但是只有学习曲线才能看见趋势
scorel = []
for i in range(0,200,10):
    #实例化随机森林分类器
    #让n_estimator,也就是树的数量不断增长
    #然后动用全部的计算资源去计算
    #随机的模式为1
    rfc = RandomForestClassifier(n_estimators=i+1,
                                 n_jobs=-1,#表示使用CPU所有的线程数进行运算,起到并行加速的作用
                                 random_state=1)
    #使用交叉验证为我们的模型打分,然后求平均值
    score = cross_val_score(rfc,Xtrain,Ytrain.values.ravel(),cv=10,n_jobs=-1).mean()
    #将我们的模型的分数追加到我们的结果列表中
    scorel.append(score)
#将列表中的最大值,也就是最好的打分打印出来
#并且打印其运行时n_estimators所设置的参数,也就是树的数量
print(max(scorel),(scorel.index(max(scorel))*10)+1)
plt.figure(figsize=[20,5])
plt.plot(range(1,201,10),scorel)
plt.xlabel('n_estimators')
plt.ylabel('accuracy')
plt.show()
#我们第一步的学习曲线只是给我们确定一个大致的范围
#现在我们知道我们的n_estimators在大概在10的范围左右将会有一个峰值

在这里插入图片描述
我们第一步的学习曲线只是给我们确定一个大致的范围,根据上面的显示最优点在10附近,进一步细化学习曲线。 即现在我们知道我们的n_estimators在大概10的范围左右将会有一个峰值

scorel = []
#由于我们上面得知的最好的n_estimators大概在10左右,所以我们可以探索5-15之间有没有更好的结果
for i in range(5,15):
    rfc = RandomForestClassifier(n_estimators=i,
                                 n_jobs=-1,
                                 random_state=1)
    score = cross_val_score(rfc,Xtrain,Ytrain.values.ravel(),cv=10,n_jobs=-1).mean()
    scorel.append(score)
#打印出最大的打分和在5到15中,分数最高的时候所对应的索引
print(max(scorel),([*range(5,15)][scorel.index(max(scorel))]))
plt.figure(figsize=[20,5])
plt.xlabel('n_estimators')
plt.ylabel('accuracy')
plt.plot(range(5,15),scorel)
plt.show()

在这里插入图片描述

调整max_depth

param_grid = {'max_depth':np.arange(1, 10, 1)}
# 一般根据数据的大小来进行一个试探,鸢尾花数据很小,所以可以采用1~10,或者1~20这样的试探
rfc = RandomForestClassifier(n_estimators=6,
                             n_jobs=-1,
                             random_state=1
                             )
GS = GridSearchCV(rfc,param_grid,cv=10,n_jobs=-1)
GS.fit(Xtrain,Ytrain.values.ravel())

在这里插入图片描述

调整max_features

param_grid = {'max_features':np.arange(2,5,1)}
rfc = RandomForestClassifier(n_estimators=6,
                             max_depth=5,
                             n_jobs=-1,
                             random_state=1
                             )
GS = GridSearchCV(rfc,param_grid,cv=10,n_jobs=-1)
GS.fit(Xtrain,Ytrain.values.ravel())

在这里插入图片描述

调整min_samples_leaf

param_grid={'min_samples_leaf':np.arange(1,1+10,1)}
#对于min_samples_split和min_samples_leaf,一般是从他们的最小值开始向上增加10或20
#面对高维度高样本量数据,如果不放心,也可以直接+50,对于大型数据,可能需要200~300的范围
#如果调整的时候发现准确率无论如何都上不来,那可以放心大胆调一个很大的数据,大力限制模型的复杂度
rfc = RandomForestClassifier(n_estimators=6,
                             max_depth=5,
                             max_features=2,
                             n_jobs=-1,
                             random_state=1
                             )
GS = GridSearchCV(rfc,param_grid,cv=10,n_jobs=-1,)
GS.fit(Xtrain,Ytrain.values.ravel())

在这里插入图片描述

调整min_samples_split

param_grid={'min_samples_split':np.arange(2, 2+20, 1)}
rfc = RandomForestClassifier(n_estimators=6,
                             max_depth=5,
                             max_features=2,
                             min_samples_leaf=1,
                             n_jobs=-1,
                             random_state=1)
GS = GridSearchCV(rfc,param_grid,cv=10,n_jobs=-1)
GS.fit(Xtrain,Ytrain.values.ravel())

在这里插入图片描述

调整criterion

param_grid = {'criterion':['gini', 'entropy']}
rfc = RandomForestClassifier(n_estimators=6,
                             max_depth=5,
                             max_features=2,
                             min_samples_leaf=1,
                             min_samples_split=2,
                             n_jobs=-1,
                             random_state=1)
GS = GridSearchCV(rfc,param_grid,cv=10,n_jobs=-1)
GS.fit(Xtrain,Ytrain.values.ravel())

在这里插入图片描述

多参数同时选优

param_grid = {'max_depth':np.arange(1, 10, 1),'max_features':np.arange(2,5,1),'min_samples_leaf':np.arange(1,1+10,1),
             'min_samples_split':np.arange(2, 2+20, 1),'criterion':['gini', 'entropy']}
rfc = RandomForestClassifier(n_estimators=6,
                             n_jobs=-1,
                             random_state=1
                             )
GS = GridSearchCV(rfc,param_grid,cv=10,n_jobs=-1)
GS.fit(Xtrain,Ytrain.values.ravel())

在这里插入图片描述

clf = RandomForestClassifier(n_estimators=6,
                             criterion='entropy',
                             max_depth=3,
                             max_features=3,
                             min_samples_leaf=3,
                             min_samples_split=2,
                             n_jobs=-1,
                             random_state=1)
tree_clf=clf.fit(Xtrain,Ytrain.values.ravel())## 用决策树模型拟合构造的数据集
Ypred = clf.predict(Xtest)
train_score=clf.score(Xtrain,Ytrain.values.ravel())##导入训练集,由接口score得到模型的得分
test_score=clf.score(Xtest,Ytest.values.ravel())##导入测试集,由接口score得到模型的得分
print(train_score,test_score)
########集具体预测情况
confmat = confusion_matrix(y_true=Ytest.values.ravel(), y_pred=Ypred)#输出混淆矩阵
print(confmat)# 混淆矩阵并可视化
# 查准率、查全率、F1值
print('precision:%.3f' % precision_score(y_true=Ytest.values.ravel(), y_pred=Ypred,average= 'macro'))
print('recall:%.3f' % recall_score(y_true=Ytest.values.ravel(), y_pred=Ypred,average= 'macro'))
print('F1:%.3f' % f1_score(y_true=Ytest.values.ravel(), y_pred=Ypred,average= 'macro'))

在这里插入图片描述

特征重要性排序

importances = clf.feature_importances_
indices = np.argsort(importances)[::-1] # 取反后是从大到小
feat_labels = Xtrain.columns
plt.title('Feature Importance')
plt.bar(range(Xtrain.shape[1]), importances[indices], align='center')
plt.xticks(range(Xtrain.shape[1]), feat_labels[indices], rotation=45)
plt.xlim([-1, Xtrain.shape[1]])
plt.tight_layout()
plt.show()

在这里插入图片描述

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>