【问题标题】:How extraction decision rules of random forest in python?如何在python中提取随机森林的决策规则?
【发布时间】:2019-10-01 05:49:42
【问题描述】:

我正在从随机森林中提取决策规则,并且我已阅读参考链接:

how extraction decision rules of random forest in python

这段代码输出是:

TREE: 0
0 NODE: if feature[33] < 2.5 then next=1 else next=4
1 NODE: if feature[38] < 0.5 then next=2 else next=3
2 LEAF: return class=2
3 LEAF: return class=9
4 NODE: if feature[50] < 8.5 then next=5 else next=6
5 LEAF: return class=4
6 LEAF: return class=0
...

但这不是一个理想的输出。这不是规则,只是打印树。

理想的输出是:

IF weight>80 AND weight<150 AND height<180 THEN figure=fat

我不知道如何产生理想的输出。期待您的帮助!

【问题讨论】:

标签: python-3.x


【解决方案1】:

基于另一个答案...交叉兼容且仅使用一个变量 X。

from sklearn import metrics, datasets, ensemble
from sklearn.tree import _tree

#Decision Rules to code utility
def dtree_to_code(fout,tree, variables, feature_names, tree_idx):
        """
        Decision tree rules in the form of Code.
        """
        f = fout
        tree_ = tree.tree_
        feature_name = [
            variables[i] if i != _tree.TREE_UNDEFINED else "undefined!"
            for i in tree_.feature
        ]
        if tree_idx<=0:
            f.write('def predict(X):\n\tret = 0\n')

        def recurse(node, depth):
            indent = "\t" * depth
            if tree_.feature[node] != _tree.TREE_UNDEFINED:
                variable = variables[node]
                name = feature_names[node]
                threshold = tree_.threshold[node]
                f.write('%sif %s <= %s: # if %s <= %s\n'%(indent, variable, threshold, name, threshold))
                recurse(tree_.children_left[node], depth + 1)
                f.write ('%selse:  # if %s > %s\n'%(indent, name, threshold))
                recurse(tree_.children_right[node], depth + 1)
            else:
                yhat = np.argmax(tree_.value[node][0])
                if yhat!=0:
                    f.write("%sret += %s\n"%(indent, yhat))
                else:
                    f.write("%spass\n"%(indent))
        recurse(0, 1)
def rf_to_code(f,rf,variables,feature_names):
    """
    Conversion of Random forest Decision rules to code.
    """
    for base_learner_id, base_learner in enumerate(rf.estimators_):
        dtree_to_code(f, tree=base_learner, variables=variables, feature_names=feature_names, tree_idx=base_learner_id)
    f.write('\treturn ret/%s\n'%(base_learner_id+1))

with open('_model.py', 'w') as f:
    f.write('''
from numba import jit,njit
@njit\n''')
    labels = ['w_%s'%word for word in d_q2i.keys()]
    variables = ['X[%s]'%i for i,word in enumerate(d_q2i.keys())]
    rf_to_code(f,estimator,variables,labels)  

输出如下所示。 X 是表示单个实例特征的一维向量。

from numba import jit,njit
@njit
def predict(X):
    ret = 0
    if X[0] <= 0.5: # if w_pizza <= 0.5
        if X[1] <= 0.5: # if w_mexico <= 0.5
            if X[2] <= 0.5: # if w_reusable <= 0.5
                ret += 1
            else:  # if w_reusable > 0.5
                pass
        else:  # if w_mexico > 0.5
            ret += 1
    else:  # if w_pizza > 0.5
        pass
    if X[0] <= 0.5: # if w_pizza <= 0.5
        if X[1] <= 0.5: # if w_mexico <= 0.5
            if X[2] <= 0.5: # if w_reusable <= 0.5
                ret += 1
            else:  # if w_reusable > 0.5
                pass
        else:  # if w_mexico > 0.5
            pass
    else:  # if w_pizza > 0.5
        ret += 1
    if X[0] <= 0.5: # if w_pizza <= 0.5
        if X[1] <= 0.5: # if w_mexico <= 0.5
            if X[2] <= 0.5: # if w_reusable <= 0.5
                ret += 1
            else:  # if w_reusable > 0.5
                ret += 1
        else:  # if w_mexico > 0.5
            ret += 1
    else:  # if w_pizza > 0.5
        pass
    if X[0] <= 0.5: # if w_pizza <= 0.5
        if X[1] <= 0.5: # if w_mexico <= 0.5
            if X[2] <= 0.5: # if w_reusable <= 0.5
                ret += 1
            else:  # if w_reusable > 0.5
                ret += 1
        else:  # if w_mexico > 0.5
            pass
    else:  # if w_pizza > 0.5
        ret += 1
    if X[0] <= 0.5: # if w_pizza <= 0.5
        if X[1] <= 0.5: # if w_mexico <= 0.5
            if X[2] <= 0.5: # if w_reusable <= 0.5
                ret += 1
            else:  # if w_reusable > 0.5
                pass
        else:  # if w_mexico > 0.5
            pass
    else:  # if w_pizza > 0.5
        pass
    if X[0] <= 0.5: # if w_pizza <= 0.5
        if X[1] <= 0.5: # if w_mexico <= 0.5
            if X[2] <= 0.5: # if w_reusable <= 0.5
                ret += 1
            else:  # if w_reusable > 0.5
                pass
        else:  # if w_mexico > 0.5
            ret += 1
    else:  # if w_pizza > 0.5
        ret += 1
    if X[0] <= 0.5: # if w_pizza <= 0.5
        if X[1] <= 0.5: # if w_mexico <= 0.5
            if X[2] <= 0.5: # if w_reusable <= 0.5
                ret += 1
            else:  # if w_reusable > 0.5
                pass
        else:  # if w_mexico > 0.5
            pass
    else:  # if w_pizza > 0.5
        ret += 1
    if X[0] <= 0.5: # if w_pizza <= 0.5
        if X[1] <= 0.5: # if w_mexico <= 0.5
            if X[2] <= 0.5: # if w_reusable <= 0.5
                ret += 1
            else:  # if w_reusable > 0.5
                pass
        else:  # if w_mexico > 0.5
            pass
    else:  # if w_pizza > 0.5
        pass
    if X[0] <= 0.5: # if w_pizza <= 0.5
        if X[1] <= 0.5: # if w_mexico <= 0.5
            if X[2] <= 0.5: # if w_reusable <= 0.5
                ret += 1
            else:  # if w_reusable > 0.5
                pass
        else:  # if w_mexico > 0.5
            pass
    else:  # if w_pizza > 0.5
        pass
    if X[0] <= 0.5: # if w_pizza <= 0.5
        if X[1] <= 0.5: # if w_mexico <= 0.5
            if X[2] <= 0.5: # if w_reusable <= 0.5
                ret += 1
            else:  # if w_reusable > 0.5
                pass
        else:  # if w_mexico > 0.5
            pass
    else:  # if w_pizza > 0.5
        pass
    return ret/10

【讨论】:

    【解决方案2】:

    这是根据您的要求提供的解决方案。 这将为您提供每个基础学习器使用的决策规则(即 sklearn 的 RandomForestClassifier 中的 n_estimator 中使用的值将不会使用 DecisionTree。)

    from sklearn import metrics, datasets, ensemble
    from sklearn.tree import _tree
    
    #Decision Rules to code utility
    def dtree_to_code(tree, feature_names, tree_idx):
            """
            Decision tree rules in the form of Code.
            """
            tree_ = tree.tree_
            feature_name = [
                feature_names[i] if i != _tree.TREE_UNDEFINED else "undefined!"
                for i in tree_.feature
            ]
            print('def tree_{1}({0}):'.format(", ".join(feature_names),tree_idx))
    
            def recurse(node, depth):
                indent = "  " * depth
                if tree_.feature[node] != _tree.TREE_UNDEFINED:
                    name = feature_name[node]
                    threshold = tree_.threshold[node]
                    print ('{0}if {1} <= {2}:'.format(indent, name, threshold))
                    recurse(tree_.children_left[node], depth + 1)
                    print ('{0}else:  # if {1} > {2}'.format(indent, name, threshold))
                    recurse(tree_.children_right[node], depth + 1)
                else:
                    print ('{0}return {1}'.format(indent, tree_.value[node]))
            recurse(0, 1)
    def rf_to_code(rf,feature_names):
        """
        Conversion of Random forest Decision rules to code.
        """
        for base_learner_id, base_learner in enumerate(rf.estimators_):
            dtree_to_code(tree = base_learner,feature_names=feature_names,tree_idx=base_learner_id)
    

    我从这里得到决策规则代码 How to extract the decision rules from scikit-learn decision-tree??

    #clf : RandomForestClassifier(n_estimator=100)
    #df :  Iris Dataframe
    
    rf_to_code(rf=clf,feature_names=df.columns)
    

    如果一切顺利,预期输出:

    def tree_0(sepal length, sepal width, petal length, petal width, species):
      if sepal length <= 5.549999952316284:
        if petal length <= 2.350000023841858:
          return [[40.  0.  0.]]
        else:  # if petal length > 2.350000023841858
          return [[0. 5. 0.]]
      else:  # if sepal length > 5.549999952316284
        if petal length <= 4.75:
          if petal width <= 0.7000000029802322:
            return [[2. 0. 0.]]
          else:  # if petal width > 0.7000000029802322
            return [[ 0. 22.  0.]]
        else:  # if petal length > 4.75
          if sepal width <= 3.049999952316284:
            if petal length <= 5.1499998569488525:
              if sepal length <= 5.950000047683716:
                return [[0. 0. 6.]]
              else:  # if sepal length > 5.950000047683716
                if petal width <= 1.75:
                  return [[0. 3. 0.]]
                else:  # if petal width > 1.75
                  return [[0. 0. 1.]]
            else:  # if petal length > 5.1499998569488525
              return [[ 0.  0. 15.]]
          else:  # if sepal width > 3.049999952316284
            return [[ 0.  0. 11.]]
    def tree_1(sepal length, sepal width, petal length, petal width, species):
      if petal length <= 2.350000023841858:
        return [[39.  0.  0.]]
      else:  # if petal length > 2.350000023841858
        if petal length <= 4.950000047683716:
          if petal length <= 4.799999952316284:
            return [[ 0. 29.  0.]]
          else:  # if petal length > 4.799999952316284
            if sepal width <= 2.9499999284744263:
              if petal width <= 1.75:
                return [[0. 1. 0.]]
              else:  # if petal width > 1.75
                return [[0. 0. 2.]]
            else:  # if sepal width > 2.9499999284744263
              return [[0. 3. 0.]]
        else:  # if petal length > 4.950000047683716
          return [[ 0.  0. 31.]]
    ......
    def tree_99(sepal length, sepal width, petal length, petal width, species):
      if sepal length <= 5.549999952316284:
        if petal width <= 0.75:
          return [[28.  0.  0.]]
        else:  # if petal width > 0.75
          return [[0. 4. 0.]]
      else:  # if sepal length > 5.549999952316284
        if petal width <= 1.699999988079071:
          if petal length <= 4.950000047683716:
            if petal width <= 0.7000000029802322:
              return [[3. 0. 0.]]
            else:  # if petal width > 0.7000000029802322
              return [[ 0. 42.  0.]]
          else:  # if petal length > 4.950000047683716
            if sepal length <= 6.049999952316284:
              if sepal width <= 2.450000047683716:
                return [[0. 0. 2.]]
              else:  # if sepal width > 2.450000047683716
                return [[0. 1. 0.]]
            else:  # if sepal length > 6.049999952316284
              return [[0. 0. 3.]]
        else:  # if petal width > 1.699999988079071
          return [[ 0.  0. 22.]]
    

    由于 n_estimators = 100,您将获得总共 100 个此类函数。

    【讨论】:

    • 有没有办法从提取的规则中重新创建 RFC?
    • 这是 Py2.x 的语法吗?
    猜你喜欢
    • 2018-11-09
    • 2017-03-15
    • 2021-03-24
    • 1970-01-01
    • 2019-11-09
    • 2017-05-03
    • 2019-09-09
    • 2017-12-20
    • 1970-01-01
    相关资源
    最近更新 更多