शिकवा-निर्णय निर्णय-वृक्ष से निर्णय नियम कैसे निकाले?


157

क्या मैं एक निर्णय वृक्ष में एक प्रशिक्षित पेड़ से एक पाठ सूची के रूप में अंतर्निहित निर्णय-नियम (या 'निर्णय पथ') निकाल सकता हूं?

कुछ इस तरह:

if A>0.4 then if B<0.2 then if C>0.8 then class='X'

आपकी सहायता के लिए धन्यवाद।



क्या आपको कभी इस समस्या का जवाब मिला? मुझे एसएएस डेटा स्टेप फॉर्मेट में निर्णय ट्री नियमों को निर्यात करना है जो आपके द्वारा सूचीबद्ध लगभग वैसा ही है।
ज़ेलज़नी

1
आप निर्यात स्केलेर-पोर्टर का उपयोग सी, जावा, जावास्क्रिप्ट और अन्य को निर्णय पेड़ों (यादृच्छिक वन और बढ़ाया पेड़) को निर्यात और ट्रांसपाइल करने के लिए कर सकते हैं।
डेरियस

आप इस कड़ी जाँच कर सकते हैं kdnuggets.com/2017/05/...
अग्रवाल योगेश

जवाबों:


139

मेरा मानना ​​है कि यह उत्तर अन्य उत्तरों की तुलना में अधिक सही है:

from sklearn.tree import _tree

def tree_to_code(tree, feature_names):
    tree_ = tree.tree_
    feature_name = [
        feature_names[i] if i != _tree.TREE_UNDEFINED else "undefined!"
        for i in tree_.feature
    ]
    print "def tree({}):".format(", ".join(feature_names))

    def recurse(node, depth):
        indent = "  " * depth
        if tree_.feature[node] != _tree.TREE_UNDEFINED:
            name = feature_name[node]
            threshold = tree_.threshold[node]
            print "{}if {} <= {}:".format(indent, name, threshold)
            recurse(tree_.children_left[node], depth + 1)
            print "{}else:  # if {} > {}".format(indent, name, threshold)
            recurse(tree_.children_right[node], depth + 1)
        else:
            print "{}return {}".format(indent, tree_.value[node])

    recurse(0, 1)

यह एक मान्य पायथन फ़ंक्शन को प्रिंट करता है। यहां एक पेड़ के लिए एक उदाहरण आउटपुट है जो अपने इनपुट को वापस करने की कोशिश कर रहा है, 0 और 10 के बीच की संख्या।

def tree(f0):
  if f0 <= 6.0:
    if f0 <= 1.5:
      return [[ 0.]]
    else:  # if f0 > 1.5
      if f0 <= 4.5:
        if f0 <= 3.5:
          return [[ 3.]]
        else:  # if f0 > 3.5
          return [[ 4.]]
      else:  # if f0 > 4.5
        return [[ 5.]]
  else:  # if f0 > 6.0
    if f0 <= 8.5:
      if f0 <= 7.5:
        return [[ 7.]]
      else:  # if f0 > 7.5
        return [[ 8.]]
    else:  # if f0 > 8.5
      return [[ 9.]]

यहाँ कुछ ठोकरें हैं जो मैं अन्य उत्तरों में देखता हूं:

  1. का उपयोग करना tree_.threshold == -2तय करने के लिए एक नोड एक पत्ता है कि क्या एक अच्छा विचार नहीं है। क्या होगा अगर यह -2 की सीमा के साथ एक वास्तविक निर्णय नोड है? इसके बजाय, आपको देखना चाहिए tree.featureया tree.children_*
  2. रेखा features = [feature_names[i] for i in tree_.feature]मेरे स्केलेर के संस्करण के साथ दुर्घटनाग्रस्त हो जाती है, क्योंकि tree.tree_.feature-2 के कुछ मूल्य (विशेष रूप से पत्ती नोड्स के लिए) हैं।
  3. पुनरावर्ती कार्य में केवल एक ठीक होने पर कई होने की आवश्यकता नहीं है।

1
यह कोड मेरे लिए बहुत अच्छा काम करता है। हालाँकि, मेरे पास 500+ फीचर_नाम हैं, इसलिए आउटपुट कोड को समझना मानव के लिए लगभग असंभव है। वहाँ एक तरीका है कि मैं केवल फीचर_नाम का इनपुट करूं जो मुझे फ़ंक्शन में उत्सुक है?
1937 पर user3768495

1
मैं पिछली टिप्पणी से सहमत हूं। IIUC को वर्ग सूचकांक वापस print "{}return {}".format(indent, tree_.value[node])करने print "{}return {}".format(indent, np.argmax(tree_.value[node][0]))के लिए फ़ंक्शन के लिए बदला जाना चाहिए ।
सूपल्ट

1
@paulkernfeld आह हां, मैं देख रहा हूं कि आप ओवर लूप कर सकते हैं RandomForestClassifier.estimators_, लेकिन मैं अनुमान लगाने वालों के परिणामों को संयोजित करने के लिए काम नहीं कर पा रहा था।
नाथन लॉयड ने

6
मैं अजगर 3 में यह काम नहीं कर सका, _tree बिट्स ऐसा नहीं लगता कि वे कभी काम करेंगे और TREE_UNDEFINED को परिभाषित नहीं किया गया था। इस लिंक ने मेरी मदद की। हालांकि निर्यात किया गया कोड अजगर में सीधे चलने योग्य नहीं है, यह c- जैसा है और अन्य भाषाओं में अनुवाद करने के लिए बहुत आसान है: web.archive.org/web/20171005203850/http://www.kdnuggets.com/ ...
जोशिया

1
@ जोशियाह, प्रिंट स्टेटमेंट में इसे जोड़ने के लिए python3 में काम करें। उदा print "bla"=>print("bla")
निर्भय

48

मैंने स्केलेर द्वारा बनाए गए निर्णय पेड़ों से नियमों को निकालने के लिए अपना स्वयं का फ़ंक्शन बनाया:

import pandas as pd
import numpy as np
from sklearn.tree import DecisionTreeClassifier

# dummy data:
df = pd.DataFrame({'col1':[0,1,2,3],'col2':[3,4,5,6],'dv':[0,1,0,1]})

# create decision tree
dt = DecisionTreeClassifier(max_depth=5, min_samples_leaf=1)
dt.fit(df.ix[:,:2], df.dv)

यह फ़ंक्शन पहले नोड्स से शुरू होता है (बच्चे के सरणियों में -1 द्वारा पहचाना जाता है) और फिर माता-पिता को पुन: खोजता है। मैं इसे नोड का 'वंश' कहता हूं। रास्ते के साथ, मैं उन मूल्यों को हड़पता हूं जो मुझे बनाने की आवश्यकता है अगर / फिर / अन्यथा एसएएस तर्क:

def get_lineage(tree, feature_names):
     left      = tree.tree_.children_left
     right     = tree.tree_.children_right
     threshold = tree.tree_.threshold
     features  = [feature_names[i] for i in tree.tree_.feature]

     # get ids of child nodes
     idx = np.argwhere(left == -1)[:,0]     

     def recurse(left, right, child, lineage=None):          
          if lineage is None:
               lineage = [child]
          if child in left:
               parent = np.where(left == child)[0].item()
               split = 'l'
          else:
               parent = np.where(right == child)[0].item()
               split = 'r'

          lineage.append((parent, split, threshold[parent], features[parent]))

          if parent == 0:
               lineage.reverse()
               return lineage
          else:
               return recurse(left, right, parent, lineage)

     for child in idx:
          for node in recurse(left, right, child):
               print node

नीचे tuples के सेट में सब कुछ है जो मुझे SAS बनाने की ज़रूरत है अगर / फिर / फिर अन्य कथन। मुझे doएसएएस में ब्लॉक का उपयोग करना पसंद नहीं है यही कारण है कि मैं एक नोड के पूरे पथ का वर्णन करने वाले तर्क बनाता हूं। ट्यूपल्स के बाद एकल पूर्णांक एक पथ में टर्मिनल नोड की आईडी है। पूर्ववर्ती ट्यूपल्स उस नोड को बनाने के लिए गठबंधन करते हैं।

In [1]: get_lineage(dt, df.columns)
(0, 'l', 0.5, 'col1')
1
(0, 'r', 0.5, 'col1')
(2, 'l', 4.5, 'col2')
3
(0, 'r', 0.5, 'col1')
(2, 'r', 4.5, 'col2')
(4, 'l', 2.5, 'col1')
5
(0, 'r', 0.5, 'col1')
(2, 'r', 4.5, 'col2')
(4, 'r', 2.5, 'col1')
6

उदाहरण ट्री का ग्राफविज़ आउटपुट


क्या इस प्रकार का पेड़ सही है क्योंकि col1 फिर से आ रहा है एक col1 <= 0.50000 और एक col1 <= 2.5000 है यदि हाँ, तो क्या पुस्तकालय में किसी भी प्रकार की पुनरावृत्ति का प्रयोग किया जाता है
jayant singh

सही शाखा के बीच रिकॉर्ड होगा (0.5, 2.5]। पेड़ों को पुनरावर्ती विभाजन के साथ बनाया गया है। एक चर को कई बार चयनित होने से रोकने के लिए कुछ भी नहीं है।
ज़ेलज़नी 7

ठीक है आप पुनरावर्ती भाग को बता सकते हैं कि क्या होता है क्योंकि मैंने अपने कोड में इसका उपयोग किया है और इसी तरह का परिणाम देखा गया है
जयंत सिंह

38

मैंने कुछ छद्मकोड मुद्रित करने के लिए ज़ेलज़नी 7 द्वारा प्रस्तुत कोड को संशोधित किया :

def get_code(tree, feature_names):
        left      = tree.tree_.children_left
        right     = tree.tree_.children_right
        threshold = tree.tree_.threshold
        features  = [feature_names[i] for i in tree.tree_.feature]
        value = tree.tree_.value

        def recurse(left, right, threshold, features, node):
                if (threshold[node] != -2):
                        print "if ( " + features[node] + " <= " + str(threshold[node]) + " ) {"
                        if left[node] != -1:
                                recurse (left, right, threshold, features,left[node])
                        print "} else {"
                        if right[node] != -1:
                                recurse (left, right, threshold, features,right[node])
                        print "}"
                else:
                        print "return " + str(value[node])

        recurse(left, right, threshold, features, 0)

यदि आप get_code(dt, df.columns)उसी उदाहरण पर कॉल करते हैं जो आपको प्राप्त होगा:

if ( col1 <= 0.5 ) {
return [[ 1.  0.]]
} else {
if ( col2 <= 4.5 ) {
return [[ 0.  1.]]
} else {
if ( col1 <= 2.5 ) {
return [[ 1.  0.]]
} else {
return [[ 0.  1.]]
}
}
}

1
क्या आप बता सकते हैं, रिटर्न स्टेटमेंट में वास्तव में [[1. 0.]] का मतलब उपरोक्त आउटपुट में है। मैं अजगर लड़का नहीं हूं, लेकिन एक ही तरह की चीज पर काम कर रहा हूं। इसलिए यह मेरे लिए अच्छा होगा यदि आप कुछ विवरणों को सिद्ध करते हैं ताकि यह मेरे लिए आसान हो जाए।
सुभ्रदीप बोस

1
@ user3156186 इसका मतलब है कि कक्षा '0' में एक वस्तु है और कक्षा '1' में शून्य वस्तुएं हैं
डेनियल

1
@ दानिएल, क्या आप जानते हैं कि कक्षाएं कैसे आर्डर की जाती हैं? मैं अल्फ़ान्यूमेरिक का अनुमान लगाऊंगा, लेकिन मुझे कहीं भी पुष्टि नहीं मिली है।
इयानस

धन्यवाद! किनारे के मामले के परिदृश्य के लिए जहां दहलीज का मूल्य वास्तव में -2 है, हमें बदलने की आवश्यकता हो सकती (threshold[node] != -2)है ( left[node] != -1)(बच्चे के नोड्स की आईडी प्राप्त करने के लिए नीचे दी गई विधि के समान)
tlingf

@ डैनीले, किसी भी विचार को अपने फ़ंक्शन को "get_code" "वापसी" के लिए एक मूल्य बनाना है और इसे "प्रिंट" नहीं करना है, क्योंकि मुझे इसे दूसरे फ़ंक्शन पर भेजने की आवश्यकता है?
रोयूमिक्स

17

स्किकिट सीख ने export_textएक पेड़ से नियमों को निकालने के लिए संस्करण 0.21 (मई 2019) में एक स्वादिष्ट नई विधि पेश की । यहाँ प्रलेखन । कस्टम फ़ंक्शन बनाने के लिए यह आवश्यक नहीं है।

एक बार जब आप अपना मॉडल फिट कर लेते हैं, तो आपको कोड की दो लाइनें चाहिए। सबसे पहले, आयात export_text:

from sklearn.tree.export import export_text

दूसरा, एक ऑब्जेक्ट बनाएं जिसमें आपके नियम होंगे। नियमों को अधिक पठनीय बनाने के लिए, feature_namesतर्क का उपयोग करें और अपने फीचर नामों की सूची पास करें। उदाहरण के लिए, यदि आपके मॉडल को कॉल किया जाता है modelऔर आपकी विशेषताओं को एक डेटाफ़्रेम में नामित किया जाता है X_train, तो आप नामक एक ऑब्जेक्ट बना सकते हैं tree_rules:

tree_rules = export_text(model, feature_names=list(X_train))

फिर सिर्फ प्रिंट करें या सेव करें tree_rules। आपका आउटपुट इस तरह दिखेगा:

|--- Age <= 0.63
|   |--- EstimatedSalary <= 0.61
|   |   |--- Age <= -0.16
|   |   |   |--- class: 0
|   |   |--- Age >  -0.16
|   |   |   |--- EstimatedSalary <= -0.06
|   |   |   |   |--- class: 0
|   |   |   |--- EstimatedSalary >  -0.06
|   |   |   |   |--- EstimatedSalary <= 0.40
|   |   |   |   |   |--- EstimatedSalary <= 0.03
|   |   |   |   |   |   |--- class: 1

14

0.18.0 रिलीज में DecisionTreeClassifier, एक नया तरीका है । डेवलपर्स एक व्यापक (अच्छी तरह से प्रलेखित) वॉकथ्रू प्रदान करते हैं ।decision_path

वॉकथ्रू में कोड का पहला खंड जो पेड़ की संरचना को प्रिंट करता है, ठीक लगता है। हालांकि, मैंने एक नमूने को पूछताछ करने के लिए दूसरे खंड में कोड को संशोधित किया। मेरे परिवर्तन के साथ निरूपित# <--

संपादित करें# <-- नीचे दिए गए कोड में चिह्नित परिवर्तन तब से चलना लिंक में अपडेट किए गए हैं जब त्रुटियों को पुल अनुरोधों # 8653 और # 10951 में इंगित किया गया था । अब साथ चलना बहुत आसान है।

sample_id = 0
node_index = node_indicator.indices[node_indicator.indptr[sample_id]:
                                    node_indicator.indptr[sample_id + 1]]

print('Rules used to predict sample %s: ' % sample_id)
for node_id in node_index:

    if leave_id[sample_id] == node_id:  # <-- changed != to ==
        #continue # <-- comment out
        print("leaf node {} reached, no decision here".format(leave_id[sample_id])) # <--

    else: # < -- added else to iterate through decision nodes
        if (X_test[sample_id, feature[node_id]] <= threshold[node_id]):
            threshold_sign = "<="
        else:
            threshold_sign = ">"

        print("decision id node %s : (X[%s, %s] (= %s) %s %s)"
              % (node_id,
                 sample_id,
                 feature[node_id],
                 X_test[sample_id, feature[node_id]], # <-- changed i to sample_id
                 threshold_sign,
                 threshold[node_id]))

Rules used to predict sample 0: 
decision id node 0 : (X[0, 3] (= 2.4) > 0.800000011921)
decision id node 2 : (X[0, 2] (= 5.1) > 4.94999980927)
leaf node 4 reached, no decision here

sample_idअन्य नमूनों के लिए निर्णय पथ देखने के लिए बदलें । मैंने डेवलपर्स से इन परिवर्तनों के बारे में नहीं पूछा है, उदाहरण के माध्यम से काम करते समय बस अधिक सहज लग रहा था।


तुम मेरे दोस्त एक किंवदंती हो! किसी भी विचार कैसे उस विशिष्ट नमूने के लिए निर्णय पेड़ की साजिश रचने के लिए? बहुत मदद की सराहना की है

1
धन्यवाद विक्टर, यह शायद एक अलग सवाल के रूप में पूछने के लिए सबसे अच्छा है क्योंकि प्लॉटिंग आवश्यकताएं उपयोगकर्ता की जरूरतों के लिए विशिष्ट हो सकती हैं। अगर आपको आउटपुट जैसा दिखना है, इसका अंदाजा लगाने पर आपको शायद अच्छा रिस्पांस मिलेगा।
केविन

हे केविन, मैंने सवाल पूछा है stackoverflow.com/questions/48888893/…

क्या आप इस पर एक नज़र रखने के लिए दयालु होंगे: stackoverflow.com/questions/52654280/…
अलेक्जेंडर चेरोव

क्या आप कृपया उस हिस्से को न पाकर नोड_इंडेक्स नामक हिस्से की व्याख्या कर सकते हैं। यह क्या करता है?
अनिंद्य शक डे

12
from StringIO import StringIO
out = StringIO()
out = tree.export_graphviz(clf, out_file=out)
print out.getvalue()

आप एक डिग ट्री देख सकते हैं। फिर,clf.tree_.feature और clf.tree_.valueनोड्स विभाजन सुविधा के सरणी और क्रमशः नोड्स मानों के सरणी हैं। आप इस github स्रोत से अधिक विवरणों को संदर्भित कर सकते हैं ।


1
हां, मुझे पता है कि पेड़ को कैसे खींचना है - लेकिन मुझे अधिक पाठ संस्करण - नियमों की आवश्यकता है। कुछ इस तरह से: Orange.biolab.si/docs/latest/reference/rst/…
Dror Hilman

4

सिर्फ इसलिए कि हर कोई इतना मददगार था कि मैं सिर्फ ज़ेलज़नी 7 और डेनियल के सुंदर समाधानों में संशोधन करूँगा। यह अजगर 2.7 के लिए है, इसे और अधिक पठनीय बनाने के लिए टैब के साथ:

def get_code(tree, feature_names, tabdepth=0):
    left      = tree.tree_.children_left
    right     = tree.tree_.children_right
    threshold = tree.tree_.threshold
    features  = [feature_names[i] for i in tree.tree_.feature]
    value = tree.tree_.value

    def recurse(left, right, threshold, features, node, tabdepth=0):
            if (threshold[node] != -2):
                    print '\t' * tabdepth,
                    print "if ( " + features[node] + " <= " + str(threshold[node]) + " ) {"
                    if left[node] != -1:
                            recurse (left, right, threshold, features,left[node], tabdepth+1)
                    print '\t' * tabdepth,
                    print "} else {"
                    if right[node] != -1:
                            recurse (left, right, threshold, features,right[node], tabdepth+1)
                    print '\t' * tabdepth,
                    print "}"
            else:
                    print '\t' * tabdepth,
                    print "return " + str(value[node])

    recurse(left, right, threshold, features, 0)

3

नीचे दिए गए कोड एनाकोंडा पायथन 2.7 के तहत मेरा दृष्टिकोण है और निर्णय नियमों के अनुसार एक पीडीएफ फाइल बनाने के लिए एक पैकेज नाम "पीडोट-एनजी" है। मुझे आशा है कि यह उपयोगी है।

from sklearn import tree

clf = tree.DecisionTreeClassifier(max_leaf_nodes=n)
clf_ = clf.fit(X, data_y)

feature_names = X.columns
class_name = clf_.classes_.astype(int).astype(str)

def output_pdf(clf_, name):
    from sklearn import tree
    from sklearn.externals.six import StringIO
    import pydot_ng as pydot
    dot_data = StringIO()
    tree.export_graphviz(clf_, out_file=dot_data,
                         feature_names=feature_names,
                         class_names=class_name,
                         filled=True, rounded=True,
                         special_characters=True,
                          node_ids=1,)
    graph = pydot.graph_from_dot_data(dot_data.getvalue())
    graph.write_pdf("%s.pdf"%name)

output_pdf(clf_, name='filename%s'%n)

यहां एक ट्री ग्राफी शो है


3

मैं इसके माध्यम से जा रहा हूं, लेकिन मुझे इस प्रारूप में लिखे जाने वाले नियमों की आवश्यकता है

if A>0.4 then if B<0.2 then if C>0.8 then class='X' 

इसलिए मैंने @paulkernfeld (धन्यवाद) के उत्तर को अनुकूलित किया, जिसे आप अपनी आवश्यकता के अनुसार अनुकूलित कर सकते हैं

def tree_to_code(tree, feature_names, Y):
    tree_ = tree.tree_
    feature_name = [
        feature_names[i] if i != _tree.TREE_UNDEFINED else "undefined!"
        for i in tree_.feature
    ]
    pathto=dict()

    global k
    k = 0
    def recurse(node, depth, parent):
        global k
        indent = "  " * depth

        if tree_.feature[node] != _tree.TREE_UNDEFINED:
            name = feature_name[node]
            threshold = tree_.threshold[node]
            s= "{} <= {} ".format( name, threshold, node )
            if node == 0:
                pathto[node]=s
            else:
                pathto[node]=pathto[parent]+' & ' +s

            recurse(tree_.children_left[node], depth + 1, node)
            s="{} > {}".format( name, threshold)
            if node == 0:
                pathto[node]=s
            else:
                pathto[node]=pathto[parent]+' & ' +s
            recurse(tree_.children_right[node], depth + 1, node)
        else:
            k=k+1
            print(k,')',pathto[parent], tree_.value[node])
    recurse(0, 1, 0)

3

यहाँ पूरे पेड़ को एक एकल में अनुवाद करने का एक तरीका है (जरूरी नहीं कि मानव-पठनीय भी) अजगर अभिव्यक्ति का उपयोग कर रहा है:

from skompiler import skompile
skompile(dtree.predict).to('python/code')

3

यह @paulkernfeld के उत्तर पर बनाता है। यदि आपके पास अपनी विशेषताओं के साथ एक डेटाफ़्रेम एक्स है और आपके प्रतिसाद के साथ एक लक्ष्य डेटाफ़्रेम वाई है और आप एक विचार प्राप्त करना चाहते हैं कि कौन सा y मान किस नोड में समाप्त हुआ (और तदनुसार इसे प्लॉट करने के लिए चींटी भी) आप निम्नलिखित कर सकते हैं:

    def tree_to_code(tree, feature_names):
        from sklearn.tree import _tree
        codelines = []
        codelines.append('def get_cat(X_tmp):\n')
        codelines.append('   catout = []\n')
        codelines.append('   for codelines in range(0,X_tmp.shape[0]):\n')
        codelines.append('      Xin = X_tmp.iloc[codelines]\n')
        tree_ = tree.tree_
        feature_name = [
            feature_names[i] if i != _tree.TREE_UNDEFINED else "undefined!"
            for i in tree_.feature
        ]
        #print "def tree({}):".format(", ".join(feature_names))

        def recurse(node, depth):
            indent = "      " * depth
            if tree_.feature[node] != _tree.TREE_UNDEFINED:
                name = feature_name[node]
                threshold = tree_.threshold[node]
                codelines.append ('{}if Xin["{}"] <= {}:\n'.format(indent, name, threshold))
                recurse(tree_.children_left[node], depth + 1)
                codelines.append( '{}else:  # if Xin["{}"] > {}\n'.format(indent, name, threshold))
                recurse(tree_.children_right[node], depth + 1)
            else:
                codelines.append( '{}mycat = {}\n'.format(indent, node))

        recurse(0, 1)
        codelines.append('      catout.append(mycat)\n')
        codelines.append('   return pd.DataFrame(catout,index=X_tmp.index,columns=["category"])\n')
        codelines.append('node_ids = get_cat(X)\n')
        return codelines
    mycode = tree_to_code(clf,X.columns.values)

    # now execute the function and obtain the dataframe with all nodes
    exec(''.join(mycode))
    node_ids = [int(x[0]) for x in node_ids.values]
    node_ids2 = pd.DataFrame(node_ids)

    print('make plot')
    import matplotlib.cm as cm
    colors = cm.rainbow(np.linspace(0, 1, 1+max( list(set(node_ids)))))
    #plt.figure(figsize=cm2inch(24, 21))
    for i in list(set(node_ids)):
        plt.plot(y[node_ids2.values==i],'o',color=colors[i], label=str(i))  
    mytitle = ['y colored by node']
    plt.title(mytitle ,fontsize=14)
    plt.xlabel('my xlabel')
    plt.ylabel(tagname)
    plt.xticks(rotation=70)       
    plt.legend(loc='upper center', bbox_to_anchor=(0.5, 1.00), shadow=True, ncol=9)
    plt.tight_layout()
    plt.show()
    plt.close 

सबसे सुंदर संस्करण नहीं है, लेकिन यह काम करता है ...


1
यह अच्छा तरीका है जब आप कोड लाइनों को सिर्फ प्रिंट करने के बजाय वापस करना चाहते हैं।
हजारा होमयौनी

3

यह वह कोड है जिसकी आपको आवश्यकता है

मैंने ज्यूपिटर नोटबुक अजगर 3 में सही ढंग से इंडेंट करने के लिए शीर्ष पसंद किए गए कोड को संशोधित किया है

import numpy as np
from sklearn.tree import _tree

def tree_to_code(tree, feature_names):
    tree_ = tree.tree_
    feature_name = [feature_names[i] 
                    if i != _tree.TREE_UNDEFINED else "undefined!" 
                    for i in tree_.feature]
    print("def tree({}):".format(", ".join(feature_names)))

    def recurse(node, depth):
        indent = "    " * depth
        if tree_.feature[node] != _tree.TREE_UNDEFINED:
            name = feature_name[node]
            threshold = tree_.threshold[node]
            print("{}if {} <= {}:".format(indent, name, threshold))
            recurse(tree_.children_left[node], depth + 1)
            print("{}else:  # if {} > {}".format(indent, name, threshold))
            recurse(tree_.children_right[node], depth + 1)
        else:
            print("{}return {}".format(indent, np.argmax(tree_.value[node])))

    recurse(0, 1)

2

यहाँ एक कार्य है, अजगर 3 के तहत एक शिकिट-लर्न डिसीजन ट्री के मुद्रण नियम और संरचना को अधिक पठनीय बनाने के लिए सशर्त ब्लॉकों के लिए ऑफसेट के साथ:

def print_decision_tree(tree, feature_names=None, offset_unit='    '):
    '''Plots textual representation of rules of a decision tree
    tree: scikit-learn representation of tree
    feature_names: list of feature names. They are set to f1,f2,f3,... if not specified
    offset_unit: a string of offset of the conditional block'''

    left      = tree.tree_.children_left
    right     = tree.tree_.children_right
    threshold = tree.tree_.threshold
    value = tree.tree_.value
    if feature_names is None:
        features  = ['f%d'%i for i in tree.tree_.feature]
    else:
        features  = [feature_names[i] for i in tree.tree_.feature]        

    def recurse(left, right, threshold, features, node, depth=0):
            offset = offset_unit*depth
            if (threshold[node] != -2):
                    print(offset+"if ( " + features[node] + " <= " + str(threshold[node]) + " ) {")
                    if left[node] != -1:
                            recurse (left, right, threshold, features,left[node],depth+1)
                    print(offset+"} else {")
                    if right[node] != -1:
                            recurse (left, right, threshold, features,right[node],depth+1)
                    print(offset+"}")
            else:
                    print(offset+"return " + str(value[node]))

    recurse(left, right, threshold, features, 0,0)

2

आप इसे और अधिक जानकारीपूर्ण बना सकते हैं ताकि यह पता चले कि यह किस वर्ग का है या इसके उत्पादन मूल्य का उल्लेख करके भी।

def print_decision_tree(tree, feature_names, offset_unit='    '):    
left      = tree.tree_.children_left
right     = tree.tree_.children_right
threshold = tree.tree_.threshold
value = tree.tree_.value
if feature_names is None:
    features  = ['f%d'%i for i in tree.tree_.feature]
else:
    features  = [feature_names[i] for i in tree.tree_.feature]        

def recurse(left, right, threshold, features, node, depth=0):
        offset = offset_unit*depth
        if (threshold[node] != -2):
                print(offset+"if ( " + features[node] + " <= " + str(threshold[node]) + " ) {")
                if left[node] != -1:
                        recurse (left, right, threshold, features,left[node],depth+1)
                print(offset+"} else {")
                if right[node] != -1:
                        recurse (left, right, threshold, features,right[node],depth+1)
                print(offset+"}")
        else:
                #print(offset,value[node]) 

                #To remove values from node
                temp=str(value[node])
                mid=len(temp)//2
                tempx=[]
                tempy=[]
                cnt=0
                for i in temp:
                    if cnt<=mid:
                        tempx.append(i)
                        cnt+=1
                    else:
                        tempy.append(i)
                        cnt+=1
                val_yes=[]
                val_no=[]
                res=[]
                for j in tempx:
                    if j=="[" or j=="]" or j=="." or j==" ":
                        res.append(j)
                    else:
                        val_no.append(j)
                for j in tempy:
                    if j=="[" or j=="]" or j=="." or j==" ":
                        res.append(j)
                    else:
                        val_yes.append(j)
                val_yes = int("".join(map(str, val_yes)))
                val_no = int("".join(map(str, val_no)))

                if val_yes>val_no:
                    print(offset,'\033[1m',"YES")
                    print('\033[0m')
                elif val_no>val_yes:
                    print(offset,'\033[1m',"NO")
                    print('\033[0m')
                else:
                    print(offset,'\033[1m',"Tie")
                    print('\033[0m')

recurse(left, right, threshold, features, 0,0)

यहाँ छवि विवरण दर्ज करें


2

यहां निर्णय नियमों को एक ऐसे रूप में निकालने का मेरा तरीका है जो सीधे sql में उपयोग किया जा सकता है, इसलिए डेटा को नोड द्वारा समूहीकृत किया जा सकता है। (पिछले पोस्टर के दृष्टिकोण के आधार पर।)

इसका परिणाम बाद के CASEक्लॉज़ होंगे जिन्हें एक sql स्टेटमेंट में कॉपी किया जा सकता है, उदा।

SELECT COALESCE(*CASE WHEN <conditions> THEN > <NodeA>*, > *CASE WHEN <conditions> THEN <NodeB>*, > ....)NodeName,* > FROM <table or view>


import numpy as np

import pickle
feature_names=.............
features  = [feature_names[i] for i in range(len(feature_names))]
clf= pickle.loads(trained_model)
impurity=clf.tree_.impurity
importances = clf.feature_importances_
SqlOut=""

#global Conts
global ContsNode
global Path
#Conts=[]#
ContsNode=[]
Path=[]
global Results
Results=[]

def print_decision_tree(tree, feature_names, offset_unit=''    ''):    
    left      = tree.tree_.children_left
    right     = tree.tree_.children_right
    threshold = tree.tree_.threshold
    value = tree.tree_.value

    if feature_names is None:
        features  = [''f%d''%i for i in tree.tree_.feature]
    else:
        features  = [feature_names[i] for i in tree.tree_.feature]        

    def recurse(left, right, threshold, features, node, depth=0,ParentNode=0,IsElse=0):
        global Conts
        global ContsNode
        global Path
        global Results
        global LeftParents
        LeftParents=[]
        global RightParents
        RightParents=[]
        for i in range(len(left)): # This is just to tell you how to create a list.
            LeftParents.append(-1)
            RightParents.append(-1)
            ContsNode.append("")
            Path.append("")


        for i in range(len(left)): # i is node
            if (left[i]==-1 and right[i]==-1):      
                if LeftParents[i]>=0:
                    if Path[LeftParents[i]]>" ":
                        Path[i]=Path[LeftParents[i]]+" AND " +ContsNode[LeftParents[i]]                                 
                    else:
                        Path[i]=ContsNode[LeftParents[i]]                                   
                if RightParents[i]>=0:
                    if Path[RightParents[i]]>" ":
                        Path[i]=Path[RightParents[i]]+" AND not " +ContsNode[RightParents[i]]                                   
                    else:
                        Path[i]=" not " +ContsNode[RightParents[i]]                     
                Results.append(" case when  " +Path[i]+"  then ''" +"{:4d}".format(i)+ " "+"{:2.2f}".format(impurity[i])+" "+Path[i][0:180]+"''")

            else:       
                if LeftParents[i]>=0:
                    if Path[LeftParents[i]]>" ":
                        Path[i]=Path[LeftParents[i]]+" AND " +ContsNode[LeftParents[i]]                                 
                    else:
                        Path[i]=ContsNode[LeftParents[i]]                                   
                if RightParents[i]>=0:
                    if Path[RightParents[i]]>" ":
                        Path[i]=Path[RightParents[i]]+" AND not " +ContsNode[RightParents[i]]                                   
                    else:
                        Path[i]=" not "+ContsNode[RightParents[i]]                      
                if (left[i]!=-1):
                    LeftParents[left[i]]=i
                if (right[i]!=-1):
                    RightParents[right[i]]=i
                ContsNode[i]=   "( "+ features[i] + " <= " + str(threshold[i])   + " ) "

    recurse(left, right, threshold, features, 0,0,0,0)
print_decision_tree(clf,features)
SqlOut=""
for i in range(len(Results)): 
    SqlOut=SqlOut+Results[i]+ " end,"+chr(13)+chr(10)

1

अब आप export_text का उपयोग कर सकते हैं।

from sklearn.tree import export_text

r = export_text(loan_tree, feature_names=(list(X_train.columns)))
print(r)

[Sklearn] [1] से एक पूर्ण उदाहरण

from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.tree import export_text
iris = load_iris()
X = iris['data']
y = iris['target']
decision_tree = DecisionTreeClassifier(random_state=0, max_depth=2)
decision_tree = decision_tree.fit(X, y)
r = export_text(decision_tree, feature_names=iris['feature_names'])
print(r)

0

निर्णय पेड़ से एसक्यूएल लाने के लिए संशोधित ज़ेलज़नी 7 का कोड।

# SQL from decision tree

def get_lineage(tree, feature_names):
     left      = tree.tree_.children_left
     right     = tree.tree_.children_right
     threshold = tree.tree_.threshold
     features  = [feature_names[i] for i in tree.tree_.feature]
     le='<='               
     g ='>'
     # get ids of child nodes
     idx = np.argwhere(left == -1)[:,0]     

     def recurse(left, right, child, lineage=None):          
          if lineage is None:
               lineage = [child]
          if child in left:
               parent = np.where(left == child)[0].item()
               split = 'l'
          else:
               parent = np.where(right == child)[0].item()
               split = 'r'
          lineage.append((parent, split, threshold[parent], features[parent]))
          if parent == 0:
               lineage.reverse()
               return lineage
          else:
               return recurse(left, right, parent, lineage)
     print 'case '
     for j,child in enumerate(idx):
        clause=' when '
        for node in recurse(left, right, child):
            if len(str(node))<3:
                continue
            i=node
            if i[1]=='l':  sign=le 
            else: sign=g
            clause=clause+i[3]+sign+str(i[2])+' and '
        clause=clause[:-4]+' then '+str(j)
        print clause
     print 'else 99 end as clusters'

0

जाहिरा तौर पर एक लंबे समय से पहले ही किसी ने आधिकारिक स्किकट के पेड़ निर्यात कार्यों के लिए निम्नलिखित फ़ंक्शन को जोड़ने का प्रयास करने का फैसला किया (जो मूल रूप से केवल export_graphviz का समर्थन करता है)

def export_dict(tree, feature_names=None, max_depth=None) :
    """Export a decision tree in dict format.

यहाँ उसकी पूरी प्रतिबद्धता है:

https://github.com/scikit-learn/scikit-learn/blob/79bdc8f711d0af225ed6be9fdb708cea9f98a910/sklearn/tree/export.py

निश्चित रूप से निश्चित नहीं है कि इस टिप्पणी का क्या हुआ। लेकिन आप उस फ़ंक्शन का उपयोग करने का प्रयास भी कर सकते हैं।

मुझे लगता है कि यह वारंट scikit के अच्छे लोगों के लिए एक गंभीर दस्तावेजीकरण का अनुरोध- sklearn.tree.Treeएपीआई को ठीक से दस्तावेज करने के लिए सीखता है जो कि अंतर्निहित पेड़ संरचना है जो DecisionTreeClassifierइसकी विशेषता के रूप में उजागर करता है tree_


0

बस इस तरह से sklearn.tree से फ़ंक्शन का उपयोग करें

from sklearn.tree import export_graphviz
    export_graphviz(tree,
                out_file = "tree.dot",
                feature_names = tree.columns) //or just ["petal length", "petal width"]

और फिर फ़ाइल ट्री.डॉट के लिए अपने प्रोजेक्ट फ़ोल्डर में देखें , सभी सामग्री को कॉपी करें और इसे यहां पेस्ट करें http://www.webgraphviz.com/ और अपना ग्राफ बनाएं :)


0

@Paulkerfeld के अद्भुत समाधान के लिए धन्यवाद। उनके समाधान के शीर्ष पर, के लिए उन सभी जो पेड़ों की एक धारावाहिक संस्करण करना चाहते हैं, तो बस का उपयोग tree.threshold, tree.children_left, tree.children_right, tree.featureऔर tree.value। के बाद से पत्ते विभाजन की जरूरत नहीं है और इसलिए कोई नाम और बच्चों में अपने प्लेसहोल्डर सुविधा tree.featureऔर tree.children_***कर रहे हैं _tree.TREE_UNDEFINEDऔर _tree.TREE_LEAF। हर विभाजन को एक अद्वितीय सूचकांक द्वारा निर्दिष्ट किया जाता है depth first search
ध्यान दें कि tree.valueआकार का है[n, 1, 1]


0

यहाँ एक फ़ंक्शन है जो एक आउटपुट ट्री से पायथन कोड उत्पन्न करता है export_text:

import string
from sklearn.tree import export_text

def export_py_code(tree, feature_names, max_depth=100, spacing=4):
    if spacing < 2:
        raise ValueError('spacing must be > 1')

    # Clean up feature names (for correctness)
    nums = string.digits
    alnums = string.ascii_letters + nums
    clean = lambda s: ''.join(c if c in alnums else '_' for c in s)
    features = [clean(x) for x in feature_names]
    features = ['_'+x if x[0] in nums else x for x in features if x]
    if len(set(features)) != len(feature_names):
        raise ValueError('invalid feature names')

    # First: export tree to text
    res = export_text(tree, feature_names=features, 
                        max_depth=max_depth,
                        decimals=6,
                        spacing=spacing-1)

    # Second: generate Python code from the text
    skip, dash = ' '*spacing, '-'*(spacing-1)
    code = 'def decision_tree({}):\n'.format(', '.join(features))
    for line in repr(tree).split('\n'):
        code += skip + "# " + line + '\n'
    for line in res.split('\n'):
        line = line.rstrip().replace('|',' ')
        if '<' in line or '>' in line:
            line, val = line.rsplit(maxsplit=1)
            line = line.replace(' ' + dash, 'if')
            line = '{} {:g}:'.format(line, float(val))
        else:
            line = line.replace(' {} class:'.format(dash), 'return')
        code += skip + line + '\n'

    return code

नमूना उपयोग:

res = export_py_code(tree, feature_names=names, spacing=4)
print (res)

नमूना उत्पादन:

def decision_tree(f1, f2, f3):
    # DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=3,
    #                        max_features=None, max_leaf_nodes=None,
    #                        min_impurity_decrease=0.0, min_impurity_split=None,
    #                        min_samples_leaf=1, min_samples_split=2,
    #                        min_weight_fraction_leaf=0.0, presort=False,
    #                        random_state=42, splitter='best')
    if f1 <= 12.5:
        if f2 <= 17.5:
            if f1 <= 10.5:
                return 2
            if f1 > 10.5:
                return 3
        if f2 > 17.5:
            if f2 <= 22.5:
                return 1
            if f2 > 22.5:
                return 1
    if f1 > 12.5:
        if f1 <= 17.5:
            if f3 <= 23.5:
                return 2
            if f3 > 23.5:
                return 3
        if f1 > 17.5:
            if f1 <= 25:
                return 1
            if f1 > 25:
                return 2

उपरोक्त उदाहरण के साथ उत्पन्न होता है names = ['f'+str(j+1) for j in range(NUM_FEATURES)]

एक उपयोगी विशेषता यह है कि यह कम रिक्ति के साथ छोटे फ़ाइल आकार उत्पन्न कर सकता है। बस सेट spacing=2

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.