决策树模型,XGBoost,LightGBM和CatBoost模型可视化
安装 graphviz
参考文档 http://graphviz.readthedocs.io/en/stable/manual.html#installation
graphviz安装包下载地址 https://www.graphviz.org/download/
将graphviz的安装位置添加到系统环境变量
使用pip install graphviz安装graphviz python包
使用pip install pydotplus安装pydotplus python包
决策树模型可视化
以iris数据为例。训练一个分类决策树,调用export_graphviz函数导出DOT格式的文件。并用pydotplus包绘制图片。

# 在环境变量中加入安装的Graphviz路径
import os
os.environ["PATH"] += os.pathsep + 'E:/Program Files (x86)/Graphviz2.38/bin'from sklearn import tree
from sklearn.datasets import load_irisiris = load_iris()
clf = tree.DecisionTreeClassifier()
clf = clf.fit(iris.data, iris.target)import pydotplus
from IPython.display import Image
dot_data = tree.export_graphviz(clf, out_file=None, feature_names=iris.feature_names,  class_names=iris.target_names,  filled=True, rounded=True, special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data)
Image(graph.create_png())


XGBoost模型可视化
参考文档 https://xgboost.readthedocs.io/en/latest/python/python_api.html
xgboost中,对应的可视化函数是xgboost.to_graphviz。以iris数据为例,训练一个xgb分类模型并可视化

# 在环境变量中加入安装的Graphviz路径
import os
os.environ["PATH"] += os.pathsep + 'E:/Program Files (x86)/Graphviz2.38/bin'import xgboost as xgb
from sklearn.datasets import load_iris
iris = load_iris()xgb_clf = xgb.XGBClassifier()
xgb_clf.fit(iris.data, iris.target)
xgb.to_graphviz(xgb_clf, num_trees=1)


也可以通过Digraph对象可以将保存文件并查看

digraph = xgb.to_graphviz(xgb_clf, num_trees=1)
digraph.format = 'png'
digraph.view('./iris_xgb')

xgboost中提供了另一个api plot_tree,使用matplotlib可视化树模型。效果上没有graphviz清楚。

import matplotlib.pyplot as plt
fig = plt.figure(figsize=(10, 10))
ax = fig.subplots()
xgb.plot_tree(xgb_clf, num_trees=1, ax=ax)
plt.show()

LightGBM模型可视化
参考文档 https://lightgbm.readthedocs.io/en/latest/Python-API.html#plotting
lgb中,对应的可视化函数是lightgbm.create_tree_digraph。以iris数据为例,训练一个lgb分类模型并可视化

# 在环境变量中加入安装的Graphviz路径
import os
os.environ["PATH"] += os.pathsep + 'E:/Program Files (x86)/Graphviz2.38/bin'from sklearn.datasets import load_iris
import lightgbm as lgbiris = load_iris()
lgb_clf = lgb.LGBMClassifier()
lgb_clf.fit(iris.data, iris.target)
lgb.create_tree_digraph(lgb_clf, tree_index=1)


lgb中提供了另一个api plot_tree,使用matplotlib可视化树模型。效果上没有graphviz清楚。

import matplotlib.pyplot as plt
fig2 = plt.figure(figsize=(20, 20))
ax = fig2.subplots()
lgb.plot_tree(lgb_clf, tree_index=1, ax=ax)
plt.show()

CatBoost模型可视化
参考文档 https://tech.yandex.com/catboost/doc/dg/concepts/python-reference_catboostclassifier-docpage/
catboost并没有提供模型可视化的api。唯一可以导出模型结构的api是save_model(fname, format=“cbm”, export_parameters=None)
以iris数据为例,训练一个catboost模型。

from sklearn.datasets import load_iris
from catboost import CatBoostClassifieriris = load_iris()
cat_clf = CatBoostClassifier(iterations=100)
cat_clf.fit(iris.data, iris.target)

以python代码格式保存模型文件

cat_clf.save_model('catboost_model_file.py', format="python", export_parameters=None)

也可以保存以C++代码格式保存模型文件

cat_clf.save_model(‘catboost_model_file.cpp’, format=“cpp”, export_parameters=None)

查看保存到的python代码,部分信息如下



需要自己解析出文件了树的结构,再用 graphviz 绘制图像

导出的Python文件
首先第一个for循环部分

binary_feature_index = 0
binary_features = [0] * model.binary_feature_count
for i in range(model.float_feature_count):for j in range(model.border_counts[i]):binary_features[binary_feature_index] = 1 if (float_features[i] > model.borders[binary_feature_index]) else 0binary_feature_index += 1

输入的参数float_features存储输入的数值型特征值。model.binary_feature_count表示booster中所有树的节点总数。model.border_counts存储每个feature对应的节点数量,model.borders存储所有节点的判断边界。显然,CatBoost并没有按照二叉树结构从左到右,从上到下的存储结构。此段代码的功能,生成所有节点的判断结果。如果特征值大于判断边界,表示为1,否则为0。存储在binary_features中。

第二个for循环部分

# Extract and sum values from trees
result = 0.0
tree_splits_index = 0
current_tree_leaf_values_index = 0
for tree_id in range(model.tree_count):current_tree_depth = model.tree_depth[tree_id]index = 0for depth in range(current_tree_depth):index |= (binary_features[model.tree_splits[tree_splits_index + depth]] << depth)result += model.leaf_values[current_tree_leaf_values_index + index]tree_splits_index += current_tree_depthcurrent_tree_leaf_values_index += (1 << current_tree_depth)
return result
这段点代码功能是生成模型的预测结果result。model.tree_count表示决策树的数量,遍历每棵决策树。model.tree_depth存储每棵决策树的深度,取当前树的深度,存储在current_tree_depth。model.tree_splits存储决策树当前深度的节点在binary_features中的索引,每棵树有current_tree_depth个节点。看似CatBoost模型存储了都是完全二叉树,而且每一层的节点以及该节点的判断边界一致。如一棵6层的决策,可以从binary_features中得到一个长度为6,值为0和1组成的list。model.leaf_values存储所有叶子节点的值,每棵树的叶子节点有(1 << current_tree_depth)个。将之前得到的list,倒序之后,看出一个2进制表示的数index,加上current_tree_leaf_values_index后,即是值在model.leaf_values的索引。将所有树得到的值相加,得到CatBoost模型的结果。还原CatBoost模型树
先从第二个for循环开始,打印每棵树序号,树的深度,当前树节点索引在tree_splits的便宜了,已经每个节点对应在tree_splits中的值。这个值对应的是在第一个for循环中生成的binary_features的索引。
tree_splits_index = 0
current_tree_leaf_values_index = 0
for tree_id in range(tree_count):current_tree_depth = tree_depth[tree_id]tree_splits_list = []for depth in range(current_tree_depth):tree_splits_list.append(tree_splits[tree_splits_index + depth])print tree_id, current_tree_depth, tree_splits_index, tree_splits_listtree_splits_index += current_tree_depthcurrent_tree_leaf_values_index += (1 << current_tree_depth)
0 6 0 [96, 61, 104, 2, 52, 81]
1 6 6 [95, 99, 106, 44, 91, 14]
2 6 12 [96, 31, 81, 102, 16, 34]
3 6 18 [95, 105, 15, 106, 57, 111]
4 6 24 [95, 51, 30, 8, 75, 57]
5 6 30 [94, 96, 103, 104, 25, 33]
6 6 36 [60, 8, 25, 39, 15, 99]
7 6 42 [96, 27, 48, 50, 69, 111]
8 6 48 [61, 80, 71, 3, 45, 2]
9 4 54 [61, 21, 90, 37]

从第一个for循环可以看出,每个feature对应的节点都在一起,且每个feature的数量保存在model.border_counts。即可生成每个feature在binary_features的索引区间。

split_list = [0]
for i in range(len(border_counts)):split_list.append(split_list[-1] + border_counts[i])print border_counts
print zip(split_list[:-1], split_list[1:])
[32, 21, 39, 20]
[(0, 32), (32, 53), (53, 92), (92, 112)]

在拿到一个binary_features的索引后即可知道该索引对应的节点使用的特征序号(float_features的索引)。

def find_feature(tree_splits_index):for i in range(len(split_list) - 1):if split_list[i] <= tree_splits_index < split_list[i+1]:return i

有了节点在binary_features中的索引,该索引也对应特征的判断边界数值索引,也知道了如何根据索引获取特征序号。决策树索引信息都的得到了,现在可以绘制树了。

绘制单棵决策树
首先修改一下代码,便于获取单棵树的节点

class CatBoostTree(object):def __init__(self, CatboostModel):self.model = CatboostModelself.split_list = [0]for i in range(self.model.float_feature_count):self.split_list.append(self.split_list[-1] + self.model.border_counts[i])def find_feature(self, splits_index):# 可优化成二分查找for i in range(self.model.float_feature_count):if self.split_list[i] <= splits_index < self.split_list[i+1]:return idef get_split_index(self, tree_id):tree_splits_index = 0current_tree_leaf_values_index = 0for index in range(tree_id):current_tree_depth = self.model.tree_depth[index]tree_splits_index += current_tree_depthcurrent_tree_leaf_values_index += (1 << current_tree_depth)return tree_splits_index, current_tree_leaf_values_indexdef get_tree_info(self, tree_id):tree_splits_index, current_tree_leaf_values_index = self.get_split_index(tree_id)current_tree_depth = self.model.tree_depth[tree_id]tree_splits_list = []for depth in range(current_tree_depth):tree_splits_list.append(self.model.tree_splits[tree_splits_index + depth])node_feature_list = [self.find_feature(index) for index in tree_splits_list]node_feature_borders = [self.model.borders[index] for index in tree_splits_list]end_tree_leaf_values_index = current_tree_leaf_values_index + (1 << current_tree_depth)tree_leaf_values = self.model.leaf_values[current_tree_leaf_values_index: end_tree_leaf_values_index]return current_tree_depth, node_feature_list, node_feature_borders, tree_leaf_values

下面是绘制一棵决策树的函数,CatBoost导出的python代码文件通过model_file参数传入。

import impimport os
os.environ["PATH"] += os.pathsep + 'E:/Program Files (x86)/Graphviz2.38/bin'from graphviz import Digraphdef draw_tree(model_file, tree_id):fp, pathname, description = imp.find_module(model_file)CatboostModel = imp.load_module('CatboostModel', fp, pathname, description)catboost_tree = CatBoostTree(CatboostModel.CatboostModel)current_tree_depth, node_feature_list, node_feature_borders, tree_leaf_values = catboost_tree.get_tree_info(tree_id)dot = Digraph(name='tree_'+str(tree_id))for depth in range(current_tree_depth):node_name = str(node_feature_list[current_tree_depth - 1 - depth])node_border = str(node_feature_borders[current_tree_depth - 1 - depth])label = 'column_' + node_name + '>' + node_borderif depth == 0:dot.node(str(depth) + '_0', label)else:for j in range(1 << depth):dot.node(str(depth) + '_' + str(j), label)dot.edge(str(depth-1) + '_' + str(j//2), str(depth) + '_' + str(j), label='No' if j%2 == 0 else 'Yes')depth = current_tree_depthfor j in range(1 << depth):dot.node(str(depth) + '_' + str(j), str(tree_leaf_values[j]))dot.edge(str(depth-1) + '_' + str(j//2), str(depth) + '_' + str(j), label='No' if j%2 == 0 else 'Yes')# dot.format = 'png'path = dot.render('./' + str(tree_id), cleanup=True)print path

例如绘制第11棵树(序数从0开始)。draw_tree(‘catboost_model_file’, 11)。


为了验证这个对不对,需要对一个测试特征生成每棵树的路径和结果,抽查一两个测试用例以及其中的一两颗树,观察结果是否相同。

def test_tree(model_file, float_features):fp, pathname, description = imp.find_module(model_file)CatboostModel = imp.load_module('CatboostModel', fp, pathname, description)model = CatboostModel.CatboostModelcatboost_tree = CatBoostTree(CatboostModel.CatboostModel)result = 0for tree_id in range(model.tree_count):current_tree_depth, node_feature_list, node_feature_borders, tree_leaf_values = catboost_tree.get_tree_info(tree_id)route = []for depth in range(current_tree_depth):route.append(1 if float_features[node_feature_list[depth]] > node_feature_borders[depth] else 0)index = 0for depth in range(current_tree_depth):index |= route[depth] << depthtree_value = tree_leaf_values[index]print route, index, tree_valueresult += tree_valuereturn result

如我们生成了第11棵树的图像,根据测试测试特征,手动在图像上查找可以得到一个值A。test_tree函数会打印一系列值,其中第11行对应的结果为值B。值A与值B相同,则测试为问题。
其次还需要测试所有树的结果和导出文件中apply_catboost_model函数得到的结果相同。这个可以写个脚本,拿训练数据集跑一边。

from catboost_model_file import apply_catboost_model
from CatBoostModelInfo import test_treefrom sklearn.datasets import load_irisdef main():iris = load_iris()# print iris.data# print iris.targetfor feature in iris.data:if apply_catboost_model(feature) != test_tree('catboost_model_file', feature):print False print 'End.'if __name__ == '__main__':main()

至此,CatBoost模型的可视化完成了。

可视化问题

1、ModuleNotFoundError: No module named 'graphviz’
原因:未安装graphviz组件

2、graphviz.backend.ExecutableNotFound: failed to execute [‘dot’, ‘-Tpng’, ‘-O’, ‘tmp’], make sure the Graphviz executables are on your systems’ PATH
原因:未在系统中配置graphviz工具的环境变量

答案 :https://blog.csdn.net/az9996/article/details/86552979

lightgbm 决策树 可视化 graphviz相关推荐

  1. python3实现决策树可视化(graphviz)中文乱码

    python3实现决策树可视化(graphviz)中文乱码 python3实现决策树可视化需要用到包graphviz,graphviz默认不支持中文,所以当dot文件中包含中文字符时会出现乱码. 解决 ...

  2. python决策树可视化——graphviz报错

    决策树是一种流行的有监督学习方法.我们在pycharm学习决策树时可能会遇到GraphViz's executables............的问题. 这是因为不仅要安装graphviz的pytho ...

  3. Python决策树可视化Graphviz下载地址

    下载地址:http://www.graphviz.org/pub/graphviz/stable/windows/

  4. 决策树可视化(sklearn、graphviz)——python数据分析与挖掘实战 5-2 决策树预测销售量高低

    若按照书上代码运行会出现如下报错(这是因为代码在截取数据时将属性值转为了矩阵): AttributeError: 'numpy.ndarray' object has no attribute 'co ...

  5. 决策树可视化,被惊艳到了!

    目前无论是机器学习竞赛还是工业界,最流行.应用最广泛的xgboost其实是优化后的GBDT(LightGBM里面的boosting比较经典稳定的也是GBDT哦!),而GBDT的基分类器最常用的就是CA ...

  6. dtreeviz:一款超级棒的决策树可视化和模型可解释性工具

    决策树是梯度提升机和随机森林的基本构建块,在学习这些模型的工作原理和模型可解释性时,可视化决策树是一个非常有帮助.不过,当前的可视化包还很初级,对新手没有多少帮助. 最近逛 Github 时,发现一款 ...

  7. 独家 | 全面!手把手教你决策树可视化(附链接代码)

    作者: Terence Parr, Prince Grover 翻译:王雨桐 校对:詹好 本文长度约为9500字,建议阅读10+分钟 本文分析了决策树可视化中的关键因素,比较了现有的可视化工具.并通过 ...

  8. 属实逼真,决策树可视化!

    同学们好 决策树的可视化,我以为之前介绍的方法已经够惊艳了(决策树可视化,被惊艳到了!),没想到最近又发现了一个更惊艳的,而且更逼真,话不多说,先看效果图↓ 直接绘制随机森林也不在话下 下面就向大家介 ...

  9. 决策树可视化保姆级教程

    决策树可视化指南 决策树是机器学习的一种经典的模型,因其泛化性能好,可解释性强而被广泛应用到实际商业预测中.通常在我们完成决策树模型搭建后,我们会进一步研究分析我们搭建好的模型,这时候模型的可视化就显 ...

最新文章

  1. LeetCode简单题之两句话中的不常见单词
  2. java web dao层_java web 中web层直接调用dao层 可以吗?
  3. 如何禁止谷歌浏览器隐藏url的www前缀
  4. 应用程序范围的键值对
  5. PAT乙级1088 三人行 (20分)
  6. python--自己实现的单链表常用功能
  7. LeetCode之长度最小的子数组
  8. cad项目数据库服务器,cad项目数据库服务器
  9. vue引入iconfont阿里巴巴矢量图标库官网,自定义图标
  10. win10计算机的数字小键盘,Win10开机默认开启数字小键盘的方法
  11. linux nginx 443端口被占用,Nginx拒绝监听端口443(示例代码)
  12. [JavaSE] 二维数组详解【静态初始化和动态初始化区别】—Day13
  13. HUD(蓝牙版)中蓝牙方案
  14. 秦皇岛公积金计算 Python
  15. Android小Demo——绘制小机器人
  16. 还款计划公式计算大全
  17. Windows原版安装程序中diskpart使用
  18. 十一道家常小菜详细攻略[图文并茂]
  19. 人工智能正在如何改变世界:BBC 总结 AI 的 A 到 Z
  20. 小鸡手柄android,简便易懂的连接方式_小鸡 Gamesir-G3_DIY攒机手柄-中关村在线

热门文章

  1. IDEA中根据数据库自动生成实体类,并自定义所生成的实体类中的注解 @Table @Id @...
  2. Go 知识点(11) — goroutine 泄露、设置子协程退出条件
  3. SpringBoot集成AOP管理日志
  4. Python关于%matplotlib inline
  5. Conversion error:Jekyll::Converters::Scss encountered an error while converting css/main.scss
  6. 绝招消除电脑自动弹出垃圾广告和网页
  7. MinkowskiEngine demo ModelNet40分类
  8. 地面标识检测与识别算法
  9. 2021年大数据Hive(九):Hive的数据压缩
  10. SyntaxError: Non-ASCII character ‘\xe5‘ in file(xxlrt_1.py) on line 7, but no encoding declared;