如何在python3中从xgboost模型中提取决策规则(特征拆分)?



我需要从python中拟合的xgboost模型中提取决策规则。我使用 0.6a2 版本的 xgboost 库,我的 python 版本是 3.5.2。

我的最终目标是使用这些拆分来装箱变量(根据拆分(。

我没有遇到这个版本的模型的任何属性,它可以给我分裂。

plot_tree给了我类似的东西。但是,它是树的可视化。

我需要类似 https://stackoverflow.com/a/39772170/4559070 的东西来开发 xgboost 模型

这是可能的,但并不容易。我建议您使用来自scikit-learnGradientBoostingClassifier,它类似于xgboost,但可以本地访问已构建的树。

但是,使用xgboost,可以获取模型的文本表示,然后对其进行解析:

from sklearn.datasets import load_iris
from xgboost import XGBClassifier
# build a very simple model
X, y = load_iris(return_X_y=True)
model = XGBClassifier(max_depth=2, n_estimators=2)
model.fit(X, y);
# dump it to a text file
model.get_booster().dump_model('xgb_model.txt', with_stats=True)
# read the contents of the file
with open('xgb_model.txt', 'r') as f:
txt_model = f.read()
print(txt_model)

它将打印 6 棵树的文本描述(2 个估计器,每个由 3 棵树组成,每个类一个(,开头如下:

booster[0]:
0:[f2<2.45] yes=1,no=2,missing=1,gain=72.2968,cover=66.6667
1:leaf=0.143541,cover=22.2222
2:leaf=-0.0733496,cover=44.4444
booster[1]:
0:[f2<2.45] yes=1,no=2,missing=1,gain=18.0742,cover=66.6667
1:leaf=-0.0717703,cover=22.2222
2:[f3<1.75] yes=3,no=4,missing=3,gain=41.9078,cover=44.4444
3:leaf=0.124,cover=24
4:leaf=-0.0668394,cover=20.4444
...

例如,现在您可以从此描述中提取所有拆分:

import re
# trying to extract all patterns like "[f2<2.45]"
splits = re.findall('[f([0-9]+)<([0-9]+.[0-9]+)]', txt_model)
splits

它会打印元组列表(feature_id、split_value(,例如

[('2', '2.45'),
('2', '2.45'),
('3', '1.75'),
('3', '1.65'),
('2', '4.95'),
('2', '2.45'),
('2', '2.45'),
('3', '1.75'),
('3', '1.65'),
('2', '4.95')]

您可以根据需要进一步处理此列表。

您可以通过函数model._Booster.trees_to_dataframe()找到决策规则作为数据帧。Yes列包含 yes-branch 的ID和 no-branch 的No列。通过这种方式,您可以重建树,因为对于数据帧的每一行,节点ID将边定向到YesNo。你可以像这样用 networkx 来做到这一点:

import networkx as nx
df = model._Booster.trees_to_dataframe()
# Create graph
G = nx.Graph()
# Add all the nodes
G.add_nodes_from(df.ID.tolist())
# Add the edges. This should be simpler in Pandas, but there seems to be a bug with df.apply(tuple, axis=1) at the moment.
yes_pairs = df[['ID', 'Yes']].dropna()
no_pairs = df[['ID', 'No']].dropna()
yes_edges = [tuple([i[0], i[1]]) for i in yes_pairs.values]
no_edges = [tuple([i[0], i[1]]) for i in no_pairs.values]
G.add_edges_from(yes_edges + no_edges)

下面是代码片段,它打印了从 xgboost 模型中的助推器树中提取的所有规则。

下面的代码基于缺失值替换为999999

import networkx as nx
df = model._Booster.trees_to_dataframe()
G = nx.DiGraph()
G.add_nodes_from(df.ID.tolist())
yes_edges = df[['ID', 'Yes', 'Feature', 'Split']].dropna()
yes_edges['label'] = yes_edges.apply(lambda x: "({feature} > {value:.2f} or {feature} = 999999)".format(feature=x['Feature'], value=x['Split']), axis=1)
no_edges = df[['ID', 'No', 'Feature', 'Split']].dropna()
no_edges['label'] = no_edges.apply(lambda x: "({feature} < {value:.2f})".format(feature=x['Feature'], value=x['Split']), axis=1)
for v in yes_edges.values:
G.add_edge(v[0],v[1], feature=v[2], expr=v[4])

for v in no_edges.values:
G.add_edge(v[0],v[1], feature=v[2], expr=v[4])
leaf_node_values = {i[0]:i[1] for i in df[df.Feature=='Leaf'][['ID','Gain']].values}    

roots = []
leaves = []
for node in G.nodes :
if G.in_degree(node) == 0 : # it's a root
roots.append(node)
elif G.out_degree(node) == 0 : # it's a leaf
leaves.append(node)

paths = []
for root in roots :
for leaf in leaves :
for path in nx.all_simple_paths(G, root, leaf) :
paths.append(path)

pred_conditions = []
for path in paths:
parts = []
for i in range(len(path)-1):
parts.append(G[path[i]][path[i+1]]['expr'])
pred_conditions.append("if " + " and ".join(parts) + " then {value:.4f}".format(value=leaf_node_values.get(path[-1])))

上面的代码以如下格式打印每个规则:

if x>y and a>b and c<d then e

你需要知道树的名称,之后,你可以把它插入到你的代码中。

最新更新