2015-09-10 21 views
6

Buduję drzewo decyzyjne użyciuJak zbadać drzewo decyzyjne zbudowany przy użyciu scikit nauczyć

clf = tree.DecisionTreeClassifier() 
clf = clf.fit(X_train, Y_train) 

To wszystko działa w porządku. Jak jednak zbadać drzewo decyzyjne?

Na przykład, w jaki sposób mogę znaleźć wpisy z X_train w określonym liście?

+3

Wystąpił podobny problem. Możesz znaleźć moją odpowiedź [tutaj] (http://stackoverflow.com/questions/20224526/how-to-extract-the-decision-rules-crom-scikit-learn-decision-tree/42227468#42227468) (i wspomniana tam instrukcja) jest pomocna. Używa metody, 'decision_path', z wersji 0.18. Zamień 'X_test' na' X_train' w kilku miejscach, jeśli chcesz zobaczyć próbki treningowe. – Kevin

Odpowiedz

3

Poniższy kod powinien sporządzić wykres swoimi dziesięciu najlepszych cech:

import numpy as np 
import matplotlib.pyplot as plt 

importances = clf.feature_importances_ 
std = np.std(clf.feature_importances_,axis=0) 
indices = np.argsort(importances)[::-1] 

# Print the feature ranking 
print("Feature ranking:") 

for f in range(10): 
    print("%d. feature %d (%f)" % (f + 1, indices[f], importances[indices[f]])) 

# Plot the feature importances of the forest 
plt.figure() 
plt.title("Feature importances") 
plt.bar(range(10), importances[indices], 
     color="r", yerr=std[indices], align="center") 
plt.xticks(range(10), indices) 
plt.xlim([-1, 10]) 
plt.show() 

pobranych od here i nieznacznie zmodyfikowany, aby pasowały do ​​DecisionTreeClassifier.

To nie pomaga dokładnie zbadać drzewa, ale mówi o drzewie.

+0

Dziękuję, ale chciałbym zobaczyć na przykład, jakie dane treningowe wpadają w każdy liść. Obecnie muszę narysować drzewo decyzyjne, zapisać reguły, napisać skrypt do filtrowania danych za pomocą tych reguł. To nie może być właściwa droga! – eleanora

+0

Czy Twoje dane są wystarczająco małe, aby wykonywać te obliczenia ręcznie lub w arkuszu kalkulacyjnym? Zakładam, że jest to dla klasy, w którym to przypadku lepiej nie po prostu uruchomić algorytm i skopiować strukturę. To powiedziawszy, wyobrażam sobie, że jest jakiś sposób, aby uzyskać strukturę drzewa z sci-kit. Oto źródło decyzji DecisionTreeClassifier: https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/tree/tree.py –

+0

To nie jest dla klasy! Mam około 1000000 elementów, więc robię to pisząc osobny skrypt Pythona. Jednak nie wiem nawet, jak automatycznie wyodrębnić reguły dla każdego liścia. Czy istnieje sposób? – eleanora

5

Musisz użyć metody przewidywania.

Po szkoleniu drzewa podajesz wartości X, aby przewidzieć ich wynik.

from sklearn.datasets import load_iris 
from sklearn.tree import DecisionTreeClassifier 
clf = DecisionTreeClassifier(random_state=0) 
iris = load_iris() 
tree = clf.fit(iris.data, iris.target) 
tree.predict(iris.data) 

wyjściowa:

>>> tree.predict(iris.data) 
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
     1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) 

Aby uzyskać szczegółowe informacje na temat struktury drzewa, możemy użyć tree_. getstate()

struktura Drzewo przekłada się na sztuce "ASCII" obraz

   0 
     _____________ 
     1   2 
       ______________ 
       3   12 
      _______  _______ 
      4  7  13 16 
      ___ ______  _____ 
      5 6 8 9  14 15 
         _____ 
         10 11 

drzewo struktury jako tablicę.

In [38]: tree.tree_.__getstate__()['nodes'] 
Out[38]: 
array([(1, 2, 3, 0.800000011920929, 0.6666666666666667, 150, 150.0), 
     (-1, -1, -2, -2.0, 0.0, 50, 50.0), 
     (3, 12, 3, 1.75, 0.5, 100, 100.0), 
     (4, 7, 2, 4.949999809265137, 0.16803840877914955, 54, 54.0), 
     (5, 6, 3, 1.6500000953674316, 0.04079861111111116, 48, 48.0), 
     (-1, -1, -2, -2.0, 0.0, 47, 47.0), 
     (-1, -1, -2, -2.0, 0.0, 1, 1.0), 
     (8, 9, 3, 1.5499999523162842, 0.4444444444444444, 6, 6.0), 
     (-1, -1, -2, -2.0, 0.0, 3, 3.0), 
     (10, 11, 2, 5.449999809265137, 0.4444444444444444, 3, 3.0), 
     (-1, -1, -2, -2.0, 0.0, 2, 2.0), 
     (-1, -1, -2, -2.0, 0.0, 1, 1.0), 
     (13, 16, 2, 4.850000381469727, 0.042533081285444196, 46, 46.0), 
     (14, 15, 1, 3.0999999046325684, 0.4444444444444444, 3, 3.0), 
     (-1, -1, -2, -2.0, 0.0, 2, 2.0), 
     (-1, -1, -2, -2.0, 0.0, 1, 1.0), 
     (-1, -1, -2, -2.0, 0.0, 43, 43.0)], 
     dtype=[('left_child', '<i8'), ('right_child', '<i8'), 
      ('feature', '<i8'), ('threshold', '<f8'), 
      ('impurity', '<f8'), ('n_node_samples', '<i8'), 
      ('weighted_n_node_samples', '<f8')]) 

gdzie:

  • Pierwszy węzeł [0] jest węzeł główny.
  • Węzły wewnętrzne mają left_child i right_child odnoszące się do węzłów o wartościach dodatnich i większe od bieżącego węzła.
  • liście mają wartość -1 dla lewego i prawego węzła podrzędnego.
  • węzły 1,5,6, 8,10,11,14,15,16 są liśćmi.
  • struktura węzłów została zbudowana przy użyciu algorytmu Głębokość pierwszego wyszukiwania.
  • pole funkcji informuje, która z cech iris.data została użyta w węźle do określenia ścieżki dla tej próbki.
  • próg informuje nas o wartości użytej do oceny kierunku w oparciu o funkcję.
  • nieczystość osiąga 0 na liściach ... ponieważ wszystkie próbki są w tej samej klasie po dotarciu do liścia.
  • n_node_samples mówi nam, ile próbek dociera do każdego liścia.

Korzystając z tych informacji, możemy trywialnie śledzić każdą próbkę X do liścia, gdzie ostatecznie ląduje, przestrzegając reguł klasyfikacji i progów skryptu. Dodatkowo, n_node_samples pozwoliłoby nam na wykonanie testów jednostkowych, upewniając się, że każdy węzeł otrzymuje poprawną liczbę próbek. Następnie wykorzystujemy dane wyjściowe drzewa.przewidzieć, możemy przypisać każdy liść do skojarzonej klasy.

+0

Dziękuję. To powie mi klasę, ale nie który liść drzewa decyzyjnego znajduje się w każdym elemencie. Gdybym mógł tylko wyodrębnić reguły potrzebne do uzyskania każdego liścia, jakoś mógłbym ponownie uruchomić te reguły przez dane. – eleanora

+0

Kiedy mówisz, że chcesz zobaczyć liście, masz na myśli, że chcesz zobaczyć reguły, które drzewo używało w każdym węźle? jeśli tak jest, to może to pomoże: http: // stackoverflow.com/questions/20224526/how-to-extract-the-decision-rules-from-scikit-learn-decision-tree – PabTorre

+0

Dla danego liścia chciałbym zobaczyć dane treningowe, które drzewo decyzyjne będzie umieszczać przy tym liściu. Innymi słowy, każdy liść jest powiązany z sekwencją reguł (porównań). Chciałbym zobaczyć podzbiór danych, które uzyskasz, jeśli zastosujesz te reguły. – eleanora

5

UWAGA: To nie jest odpowiedź, tylko wskazówka na temat możliwych rozwiązań.

Ostatnio napotkałem podobny problem w moim projekcie. Moim celem jest wyodrębnienie odpowiedniego łańcucha decyzji dla niektórych konkretnych próbek. Myślę, że twój problem jest moim podzbiorem, ponieważ wystarczy zarejestrować ostatni krok w łańcuchu decyzyjnym.

Do tej pory jedynym możliwym rozwiązaniem jest napisać niestandardową metodę w Pythonie, aby śledzić decyzje na bieżąco. Powodem jest to, że metoda predict dostarczona przez scikit-learn nie może zrobić tego po uruchomieniu (o ile wiem). A co gorsza, jest to wrapper dla implementacji C, który jest dość trudny do dostosowania.

Personalizacja jest w porządku dla mojego problemu, ponieważ mam do czynienia z niezbilansowanym zestawem danych, a próbki, na których mi zależy (pozytywne) są rzadkie. Mogę je najpierw odfiltrować przy użyciu sklearn predict, a następnie uzyskać łańcuch decyzyjny za pomocą mojego dostosowania.

Może to jednak nie działać, jeśli masz duży zbiór danych. Ponieważ jeśli analizujesz drzewo i przewidujesz w Pythonie, będzie ono działało wolno w Pythonie i nie będzie (łatwo) skalowane. Być może będziesz musiał zrezygnować z dostosowywania implementacji C.

+0

Częściowa odpowiedź z możliwie największą ilością badań jest nadal akceptowalna. – Xufox

+0

Dzięki. Nie miałem czasu na wdrożenie tego pomysłu. Mam nadzieję, że wkrótce pojawi się ktoś z kodem. – zaxliu

3

Ten kod zrobi dokładnie to, co chcesz. Tutaj n to obserwacje liczb w X_train. Na końcu, tablica o rozmiarze (n, number_of_leaves) leaf_observations przechowuje w każdej kolumnie wartości boolowskie do indeksowania do X_train, aby uzyskać obserwacje w każdym liściu. Każda kolumna leaf_observations odpowiada elementowi w leaves, który ma identyfikatory węzłów dla liści.

# get the nodes which are leaves 
leaves = clf.tree_.children_left == -1 
leaves = np.arange(0,clf.tree_.node_count)[leaves] 

# loop through each leaf and figure out the data in it 
leaf_observations = np.zeros((n,len(leaves)),dtype=bool) 
# build a simpler tree as a nested list: [split feature, split threshold, left node, right node] 
thistree = [clf.tree_.feature.tolist()] 
thistree.append(clf.tree_.threshold.tolist()) 
thistree.append(clf.tree_.children_left.tolist()) 
thistree.append(clf.tree_.children_right.tolist()) 
# get the decision rules for each leaf node & apply them 
for (ind,nod) in enumerate(leaves): 
    # get the decision rules in numeric list form 
    rules = [] 
    RevTraverseTree(thistree, nod, rules) 
    # convert & apply to the data by sequentially &ing the rules 
    thisnode = np.ones(n,dtype=bool) 
    for rule in rules: 
     if rule[1] == 1: 
      thisnode = np.logical_and(thisnode,X_train[:,rule[0]] > rule[2]) 
     else: 
      thisnode = np.logical_and(thisnode,X_train[:,rule[0]] <= rule[2]) 
    # get the observations that obey all the rules - they are the ones in this leaf node 
    leaf_observations[:,ind] = thisnode 

To musi funkcję pomocnika, który zdefiniowany tutaj rekurencyjnie przemierza drzewa począwszy od określonego węzła zbudować reguły decyzyjne.

def RevTraverseTree(tree, node, rules): 
    ''' 
    Traverase an skl decision tree from a node (presumably a leaf node) 
    up to the top, building the decision rules. The rules should be 
    input as an empty list, which will be modified in place. The result 
    is a nested list of tuples: (feature, direction (left=-1), threshold). 
    The "tree" is a nested list of simplified tree attributes: 
    [split feature, split threshold, left node, right node] 
    ''' 
    # now find the node as either a left or right child of something 
    # first try to find it as a left node 
    try: 
     prevnode = tree[2].index(node) 
     leftright = -1 
    except ValueError: 
     # failed, so find it as a right node - if this also causes an exception, something's really f'd up 
     prevnode = tree[3].index(node) 
     leftright = 1 
    # now let's get the rule that caused prevnode to -> node 
    rules.append((tree[0][prevnode],leftright,tree[1][prevnode])) 
    # if we've not yet reached the top, go up the tree one more step 
    if prevnode != 0: 
     RevTraverseTree(tree, prevnode, rules) 
1

Myślę, że łatwym rozwiązaniem byłoby zastosowanie metody stosowanej w drzewku decyzyjnym. Trenować drzewo, zastosować traindata i zbudować tablicę przeglądową ze zwróconych indeksów:

import numpy as np 
from sklearn.tree import DecisionTreeClassifier 
from sklearn.datasets import load_iris 

iris = load_iris() 
clf = DecisionTreeClassifier() 
clf = clf.fit(iris.data, iris.target) 

# apply training data to decision tree 
leaf_indices = clf.apply(iris.data) 
lookup = {} 

# build lookup table 
for i, leaf_index in enumerate(leaf_indices): 
    try: 
     lookup[leaf_index].append(iris.data[i]) 
    except KeyError: 
     lookup[leaf_index] = [] 
     lookup[leaf_index].append(iris.data[i]) 

# test 
unkown_sample = [[4., 3.1, 6.1, 1.2]] 
index = clf.apply(unkown_sample) 
print(lookup[index[0]]) 
0

Czy próbowali dumping swoje DecisionTree w graphviz”.dot plik [1], a następnie załadować go z graph_tool [2]. :

import numpy as np 
from sklearn.tree import DecisionTreeClassifier 
from sklearn.datasets import load_iris 
from graph_tool.all import * 

iris = load_iris() 
clf = DecisionTreeClassifier() 
clf = clf.fit(iris.data, iris.target) 

tree.export_graphviz(clf,out_file='tree.dot') 

#load graph with graph_tool and explore structure as you please 
g = load_graph('tree.dot') 

for v in g.vertices(): 
    for e in v.out_edges(): 
     print(e) 
    for w in v.out_neighbours(): 
     print(w) 

[1] http://scikit-learn.org/stable/modules/generated/sklearn.tree.export_graphviz.html

[2] https://graph-tool.skewed.de/

+0

Czy możesz sprawić, że będzie taka piękna? Jak w http://scikit-learn.org/stable/_images/iris.svg? – eleanora

+0

Po wyprowadzeniu z export_graphiz coś takiego można osiągnąć za pomocą dot -Tpng tree.dot -o tree.png. –

2

zmieniłem trochę co dr Drew wysłana.
Poniższy kod, otrzymuje ramkę danych i drzewo decyzyjne po zamontowaniu, zwraca:

  • rules_list: listę zasad
  • values_path: listę wpisów (wpisy dla każdego klasa przechodzi ścieżkę)

    import numpy as np 
    import pandas as pd 
    from sklearn.tree import DecisionTreeClassifier 
    
    def get_rules(dtc, df): 
        rules_list = [] 
        values_path = [] 
        values = dtc.tree_.value 
    
        def RevTraverseTree(tree, node, rules, pathValues): 
         ''' 
         Traverase an skl decision tree from a node (presumably a leaf node) 
         up to the top, building the decision rules. The rules should be 
         input as an empty list, which will be modified in place. The result 
         is a nested list of tuples: (feature, direction (left=-1), threshold). 
         The "tree" is a nested list of simplified tree attributes: 
         [split feature, split threshold, left node, right node] 
         ''' 
         # now find the node as either a left or right child of something 
         # first try to find it as a left node    
    
         try: 
          prevnode = tree[2].index(node)   
          leftright = '<=' 
          pathValues.append(values[prevnode]) 
         except ValueError: 
          # failed, so find it as a right node - if this also causes an exception, something's really f'd up 
          prevnode = tree[3].index(node) 
          leftright = '>' 
          pathValues.append(values[prevnode]) 
    
         # now let's get the rule that caused prevnode to -> node 
         p1 = df.columns[tree[0][prevnode]]  
         p2 = tree[1][prevnode]  
         rules.append(str(p1) + ' ' + leftright + ' ' + str(p2)) 
    
         # if we've not yet reached the top, go up the tree one more step 
         if prevnode != 0: 
          RevTraverseTree(tree, prevnode, rules, pathValues) 
    
        # get the nodes which are leaves 
        leaves = dtc.tree_.children_left == -1 
        leaves = np.arange(0,dtc.tree_.node_count)[leaves] 
    
        # build a simpler tree as a nested list: [split feature, split threshold, left node, right node] 
        thistree = [dtc.tree_.feature.tolist()] 
        thistree.append(dtc.tree_.threshold.tolist()) 
        thistree.append(dtc.tree_.children_left.tolist()) 
        thistree.append(dtc.tree_.children_right.tolist()) 
    
        # get the decision rules for each leaf node & apply them 
        for (ind,nod) in enumerate(leaves): 
    
         # get the decision rules 
         rules = [] 
         pathValues = [] 
         RevTraverseTree(thistree, nod, rules, pathValues) 
    
         pathValues.insert(0, values[nod])  
         pathValues = list(reversed(pathValues)) 
    
         rules = list(reversed(rules)) 
    
         rules_list.append(rules) 
         values_path.append(pathValues) 
    
        return (r, values_path) 
    

Wynika przykład :

df = pd.read_csv('df.csv') 

X = df[df.columns[:-1]] 
y = df['classification'] 

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) 

dtc = DecisionTreeClassifier(max_depth=2) 
dtc.fit(X_train, y_train) 

Decyzja Drzewo wyposażone przyniosła następujące drzewa: Decision Tree with width 2

W tym momencie, po prostu wywołanie funkcji:

get_rules(df, dtc) 

to co funkcja zwraca:

rules = [ 
    ['first <= 63.5', 'first <= 43.5'], 
    ['first <= 63.5', 'first > 43.5'], 
    ['first > 63.5', 'second <= 19.700000762939453'], 
    ['first > 63.5', 'second > 19.700000762939453'] 
] 

values = [ 
    [array([[ 1568., 1569.]]), array([[ 636., 241.]]), array([[ 284., 57.]])], 
    [array([[ 1568., 1569.]]), array([[ 636., 241.]]), array([[ 352., 184.]])], 
    [array([[ 1568., 1569.]]), array([[ 932., 1328.]]), array([[ 645., 620.]])], 
    [array([[ 1568., 1569.]]), array([[ 932., 1328.]]), array([[ 287., 708.]])] 
] 

Oczywiście w wartościach dla każdej ścieżki istnieją również wartości liści.