决策树、随机森林的决策路径与预测概率

决策树、随机森林的决策路径与预测概率

最近笔者在学习机器学习内容的过程中,发现自己越来越想搞清楚每个算法的运行机制,决策树和随机森林在机器学习中虽然已经是经典到不能再经典的算法了,但是笔者仍然发现自己对这两个算法存在一些模棱两可的理解,这种模棱两可主要在两方面:决策路径的生成和预测概率的计算,本文通过sklearn实现这两个算法,并对两个算法的决策路径和预测概率的计算进行了辨析和理解。

机器学习笔记------随机森林分类的sklearn实现 - 掘金 (juejin.cn)

机器学习笔记------分类决策树的Sklearn实现 - 掘金 (juejin.cn)

首先导入需要用的包,并生成半月型数据集。

python 复制代码
from sklearn.ensemble import RandomForestClassifier  as rfc
import pandas as pd
#import graphviz#用于绘制决策树的包
from sklearn.tree import DecisionTreeClassifier as dtc
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
import numpy as np
from sklearn import  tree
python 复制代码
#构建二维数据集
moon=make_moons(200,noise=0.2,random_state=1)
moon1=pd.DataFrame({"f1":moon[0][:,0],"f2":moon[0][:,1],"label":moon[1]})
moon1['f1']=moon1['f1'].apply(lambda x:round(x,2))
moon1['f2']=moon1['f2'].apply(lambda x:round(x,2))
plt.scatter(moon1['f1'],moon1['f2'],c=moon1['label'])
plt.show()

查看部分数据:

python 复制代码
moon1.head()

| | f1 | f2 | label |
| 0 | -0.31 | 0.90 | 0 |
| 1 | 0.39 | 1.30 | 0 |
| 2 | 0.49 | 0.98 | 0 |
| 3 | -0.13 | 0.28 | 1 |

4 1.73 0.17 1

1.决策树

1.1决策路径

首先训练一个最深深度为3(方便观察)的决策树,并绘制决策树的节点图,节点图上有各个节点的编号

python 复制代码
#moon2=pd.concat([moon1,moon1])
dtc1=dtc(max_features='auto', max_depth=3,
        random_state=0).fit(moon1[['f1','f2']],moon1['label'])
plt.figure(figsize=(8,8))
tree.plot_tree(dtc1,
                    feature_names=['f1','f2'],              
                    filled=True,#填充颜色
                    class_names=["0","1"],
                    rounded=False,#边缘圆润与否
              		 node_ids=True#写出每个节点的序号
                   )
plt.show()

查看决策路径,主要是查看生成的决策树,**在对某个样本进行决策的时候经过哪些节点,并最终到达哪个叶子结点。**以样本[[1.73,0.17]]为例,结合上面的图片,该样本在本决策树最终的决策路径应该是:

graph TD A[#0节点,判断f2=0.17<0.345,转左子节点#1] B[#1节点,判断f1=1.73>-0.45,转右子节点#3] C[#3节点,判断f2=0.17>0.095,转右子节点#5] D[#5节点,进入叶子结点#5] A-->B-->C-->D

这里主要用到的函数是决策树中的decision_path()方法:

参数:

X: 需要进行预测、计算的样本,是二维格式,具体shape=[样本数量,特征数量]

返回值:

indicator: 形状为(n_samples, n_nodes)的稀疏矩阵, 返回一个节点指示矩阵,非零元素的位置表示样本在预测过程中经过了那些节点

python 复制代码
print(dtc1.decision_path([[1.73,0.17]]))
#这里只测试一个样本
scss 复制代码
  (0, 0)	1
  (0, 1)	1
  (0, 3)	1
  (0, 5)	1

以上是样本[[1.73,0.17]]的决策路径,表示样本在预测过程中,经过了0、1、3、5四个节点,其中5为最终的叶子结点。与前面绘制的决策树节点图进行对照,结果一致。

概率

我们使用决策树的predict_proba,决策树预测出来的概率与其他的类似逻辑回归的计算方法不一样,决策树判断某个样本所属某一类别的概率,主要是根据它被分到的叶子结点中的每个类别的数量来计算,即每个类别在叶子结点中的占比。

python 复制代码
dtc1.predict_proba([[1.73,0.17]])
lua 复制代码
array([[0.34782609, 0.65217391]])

测试一下,我们知道最终样本被分到的叶子结点#5,样本的数量为[8,15],则预测为1的概率为以下:

python 复制代码
print(15/(15+8))
0.6521739130434783

与前面用predict_proba计算得到的概率一致。

2随机森林

2.1决策路径

首先训练一个决策树数量为4,最大深度为3的随机森林,随机森林不能直接绘制出节点图,但是可以绘制出构成随机森林的每个决策树的节点图像。

python 复制代码
rfc1=rfc(n_estimators=4,bootstrap=True,min_samples_leaf=10,max_depth=3).fit(moon1[['f1','f2']],moon1['label'])
python 复制代码
est=rfc1.estimators_
plt.figure(figsize=(16,16))
plt.subplot(221)
tree.plot_tree(est[0],
                    feature_names=['f1','f2'],              
                    filled=True,#填充颜色
                        class_names=["0","1"],
                    rounded=False,#边缘圆润与否
               node_ids=True
                   )
plt.subplot(222)

tree.plot_tree(est[1],
                    feature_names=['f1','f2'],              
                    filled=True,#填充颜色
                        class_names=["0","1"],
                    rounded=False,#边缘圆润与否
               node_ids=True
                   )  
plt.subplot(223)

tree.plot_tree(est[2],
                    feature_names=['f1','f2'],              
                    filled=True,#填充颜色
                        class_names=["0","1"],
                    rounded=False,#边缘圆润与否
               node_ids=True
                   )
plt.subplot(224)

tree.plot_tree(est[3],
                    feature_names=['f1','f2'],              
                    filled=True,#填充颜色
                        class_names=["0","1"],
                    rounded=False,#边缘圆润与否
               node_ids=True
                   ) 
plt.show()

我们已经知道,随机森林的预测值是由构成森林的决策树预测并进行取均值/投票得到的,因此其决策路径也是由所有的决策树的决策路径合并组成。

在随机森林中decision_path方法的返回值与决策树中的该函数有所不同

参数:

X: 需要进行预测、计算的样本,是二维格式,具体shape=[样本数量,特征数量]

返回值:

  • indicator: 形状为(n_samples, n_nodes)的稀疏矩阵

    返回一个节点指示矩阵,非零元素的位置表示样本在预测过程中经过了那些节点

  • n_nodes_ptr:形状为(n_estimators + 1,)的数组

    The columns from indicator[n_nodes_ptr[i]:n_nodes_ptr[i+1]] ,gives the indicator value for the i-th estimator.

    indicator[n nodes ptr[i]:n nodes ptr[i+1]]中的列给出了第i个分类器的indicator(决策路径)。

以下是某个样本[[-0.13,0.28]]在训练好的的随机森林rfc1中的决策路径indicator,以及n_nodes_ptr(个人认为它的主要作用就是切分随机森林中不同的决策树的决策路径)。

python 复制代码
print(rfc1.decision_path([[-0.13,0.28]])[0])
print(rfc1.decision_path([[-0.13,0.28]])[1])
scss 复制代码
  (0, 0)	1
  (0, 1)	1
  (0, 3)	1
  (0, 5)	1
  (0, 9)	1
  (0, 10)	1
  (0, 12)	1
  (0, 14)	1
  (0, 18)	1
  (0, 22)	1
  (0, 26)	1
  (0, 27)	1
  (0, 29)	1
  (0, 30)	1
  (0, 32)	1
  (0, 33)	1
[ 0  9 18 29 38]

接下来以[[1.73,0.17]]为例观察随机森林的决策路径,首先为了方便观察和计算,将稀疏矩阵转换为稠密矩阵:

python 复制代码
drfc1=rfc1.decision_path([[1.73,0.17]])[0].toarray()#方便观察转变为稠密矩阵
print(drfc1)
lua 复制代码
[[1 1 0 1 0 1 0 0 0 1 1 0 1 0 1 0 0 0 1 0 0 0 1 1 0 1 0 0 0 1 1 0 1 0 1 0
  0 0]]

并提取n_nodes_ptr,用于切分稠密矩阵drfc1,来获取构成rfc1的每个决策树的决策路径

python 复制代码
mrfc1=rfc1.decision_path([[1.73,0.17]])[1]
print(mrfc1)
css 复制代码
[ 0  9 18 29 38]

同样的为了方便观察,我们将切分好的决策路径,用稀疏矩阵的形式表现出来

python 复制代码
from scipy.sparse import csr_matrix#导入将稠密矩阵转换为稀疏矩阵的包
python 复制代码
list1=[]
#提取样本在每个决策树的决策路径,并转换为稀疏矩阵
for i in range(len(mrfc1)-1):
    list1.append(csr_matrix(drfc1[0][mrfc1[i]:mrfc1[i+1]]))
python 复制代码
for i in range(len(list1)):
    print("样本第{}个决策树的决策路径".format(i+1))
    print(list1[i])
scss 复制代码
样本第1个决策树的决策路径
  (0, 0)	1
  (0, 1)	1
  (0, 3)	1
  (0, 5)	1
样本第2个决策树的决策路径
  (0, 0)	1
  (0, 1)	1
  (0, 3)	1
  (0, 5)	1
样本第3个决策树的决策路径
  (0, 0)	1
  (0, 4)	1
  (0, 5)	1
  (0, 7)	1
样本第4个决策树的决策路径
  (0, 0)	1
  (0, 1)	1
  (0, 3)	1
  (0, 5)	1

同样的,我们也可以直接用构成随机森林的每个决策树来生成该样本的决策路径,如下所示,和上面的结果一致:

python 复制代码
for i in range(4):
    print("样本第{}个决策树的决策路径".format(i+1))
    print(rfc1[i].decision_path([[1.73,0.17]]))
scss 复制代码
样本第1个决策树的决策路径
  (0, 0)	1
  (0, 1)	1
  (0, 3)	1
  (0, 5)	1
样本第2个决策树的决策路径
  (0, 0)	1
  (0, 1)	1
  (0, 3)	1
  (0, 5)	1
样本第3个决策树的决策路径
  (0, 0)	1
  (0, 4)	1
  (0, 5)	1
  (0, 7)	1
样本第4个决策树的决策路径
  (0, 0)	1
  (0, 1)	1
  (0, 3)	1
  (0, 5)	1

2.2概率

随机森林的预测概率即构成每个决策树的预测概率的均值,具体可以观察下面的例子,这里不多加赘述

python 复制代码
rfc1.predict_proba([[1.73,0.17]])
lua 复制代码
array([[0.20480082, 0.79519918]])
python 复制代码
ll=[]
for i in range(4):
    ll.append(rfc1[i].predict_proba([[1.73,0.17]])[0].tolist())
print(np.array(ll))
lua 复制代码
[[0.38983051 0.61016949]
 [0.2972973  0.7027027 ]
 [0.13207547 0.86792453]
 [0.         1.        ]]
python 复制代码
np.array(ll).mean(axis=0)
scss 复制代码
array([0.20480082, 0.79519918])

3.一些其他的发现

在绘制决策树的节点图的时候,我发现,用全部的样本去训练一棵决策树,其#0节点的samples=sum(value)=真实的样本总数, 其中samples表示该节点的样本数量,value表示该节点中每个类别的数量。

但是在训练完成的随机森林中,每个决策树的#0节点value加和虽然与真实的样本总数相等,但是并不等于samples ,笔者认为这与随机森林在训练每个决策树的过程中使用boostrap有放回抽样有关,但是没有找到具体的源代码支撑。

相关推荐
shinelord明3 分钟前
【再谈设计模式】享元模式~对象共享的优化妙手
开发语言·数据结构·算法·设计模式·软件工程
游客5208 分钟前
opencv中的各种滤波器简介
图像处理·人工智能·python·opencv·计算机视觉
დ旧言~10 分钟前
专题八:背包问题
算法·leetcode·动态规划·推荐算法
Eric.Lee202111 分钟前
moviepy将图片序列制作成视频并加载字幕 - python 实现
开发语言·python·音视频·moviepy·字幕视频合成·图像制作为视频
Dontla16 分钟前
vscode怎么设置anaconda python解释器(anaconda解释器、vscode解释器)
ide·vscode·python
_WndProc27 分钟前
C++ 日志输出
开发语言·c++·算法
努力学习编程的伍大侠40 分钟前
基础排序算法
数据结构·c++·算法
qq_529025291 小时前
Torch.gather
python·深度学习·机器学习
数据小爬虫@1 小时前
如何高效利用Python爬虫按关键字搜索苏宁商品
开发语言·爬虫·python
XiaoLeisj1 小时前
【递归,搜索与回溯算法 & 综合练习】深入理解暴搜决策树:递归,搜索与回溯算法综合小专题(二)
数据结构·算法·leetcode·决策树·深度优先·剪枝