141 votes

Comment sont déterminées les importances des caractéristiques dans RandomForestClassifier ?

J'ai une tâche de classification avec une série chronologique comme entrée de données, où chaque attribut (n=23) représente un point spécifique dans le temps. Outre le résultat absolu de la classification, je voudrais savoir quels attributs/dates contribuent au résultat dans quelle mesure. C'est pourquoi j'utilise la fonction feature_importances_ ce qui fonctionne bien pour moi.

Cependant, j'aimerais savoir comment ils sont calculés et quelle mesure/algorithme est utilisé. Malheureusement, je n'ai trouvé aucune documentation à ce sujet.

14 votes

Woah trois core devs sur un fil SO. Cela doit être une sorte de record ^^

174voto

Gilles Louppe Points 2604

Il existe en effet plusieurs façons d'obtenir des "importances" de fonctionnalités. Comme souvent, il n'y a pas de consensus strict sur la signification de ce mot.

Dans scikit-learn, nous implémentons l'importance telle que décrite dans [1] (souvent citée, mais malheureusement rarement lue...). Elle est parfois appelée "importance gini" ou "impureté moyenne décroissante" et est définie comme la diminution totale de l'impureté d'un nœud (pondérée par la probabilité d'atteindre ce nœud (qui est approximée par la proportion d'échantillons atteignant ce nœud)), moyennée sur tous les arbres de l'ensemble.

Dans la littérature ou dans d'autres paquets, vous pouvez également trouver des importances de caractéristiques implémentées comme la "précision de la diminution moyenne". Fondamentalement, l'idée est de mesurer la diminution de la précision sur les données OOB lorsque vous permutez aléatoirement les valeurs de cette caractéristique. Si la diminution est faible, alors la caractéristique n'est pas importante, et vice-versa.

(Notez que les deux algorithmes sont disponibles dans le paquet R randomForest).

[1] : Breiman, Friedman, "Classification and regression trees", 1984.

51 votes

Il serait bon que cette réponse soit mentionnée dans la documentation des attributs/exemples d'importance. J'ai cherché cette réponse pendant un certain temps :)

3 votes

Il semble que le score d'importance soit en valeur relative ? Par exemple, la somme des scores d'importance de toutes les caractéristiques est toujours égale à 1 (voir l'exemple ici scikit-learn.org/stable/auto_examples/ensemble/ )

6 votes

@RNA : Oui, par défaut les importances de variables sont normalisées dans scikit-learn, de telle sorte que leur somme est égale à un. Vous pouvez contourner ce problème en passant en boucle sur les estimateurs de base individuels et en appelant tree_.compute_feature_importances(normalize=False) .

58voto

La manière habituelle de calculer les valeurs d'importance des caractéristiques d'un seul arbre est la suivante :

  1. vous initialisez un tableau feature_importances de tous les zéros de taille n_features .

  2. vous traversez l'arbre : pour chaque nœud interne qui se divise sur la caractéristique i vous calculez la réduction d'erreur de ce nœud multipliée par le nombre d'échantillons qui ont été acheminés vers le nœud et vous ajoutez cette quantité à feature_importances[i] .

La réduction de l'erreur dépend du critère d'impureté que vous utilisez (par exemple, Gini, Entropie, MSE, ...). C'est l'impureté de l'ensemble d'exemples qui est acheminé vers le nœud interne moins la somme des impuretés des deux partitions créées par la division.

Il est important de noter que ces valeurs sont relatives à un jeu de données spécifique (la réduction des erreurs et le nombre d'échantillons sont spécifiques à chaque jeu de données). Ces valeurs ne peuvent donc pas être comparées entre différents jeux de données.

Pour autant que je sache, il existe d'autres façons de calculer les valeurs d'importance des caractéristiques dans les arbres de décision. Une brève description de la méthode ci-dessus peut être trouvée dans "Elements of Statistical Learning" de Trevor Hastie, Robert Tibshirani, et Jerome Friedman.

16voto

ogrisel Points 13211

Il s'agit du rapport entre le nombre d'échantillons acheminés vers un nœud de décision impliquant cette caractéristique dans l'un des arbres de l'ensemble et le nombre total d'échantillons dans l'ensemble d'apprentissage.

Les caractéristiques qui sont impliquées dans les nœuds de niveau supérieur des arbres de décision ont tendance à voir plus d'échantillons et sont donc susceptibles d'avoir plus d'importance.

Modifier Cette description n'est que partiellement correcte : les réponses de Gilles et Peter sont les bonnes.

1 votes

Savez-vous s'il existe un article ou une documentation sur la méthode exacte ? Par exemple, Breiman, 2001. Ce serait formidable si je disposais d'un document approprié que je pourrais citer pour la méthodologie.

1 votes

@ogrisel ce serait bien si vous pouviez clairement marquer votre réponse comme étant l'explication de la "pondération". La pondération seule ne détermine pas l'importance de la caractéristique. La "métrique d'impureté" ("gini-importance" ou RSS) combinée aux poids, dont la moyenne est calculée sur les arbres, détermine l'importance globale de la caractéristique. Malheureusement, la documentation sur scikit-learn se trouve ici : scikit-learn.org/stable/modules/ n'est pas exacte et mentionne à tort la "profondeur" comme mesure de l'impureté.

14voto

Peter Points 363

Comme @GillesLouppe l'a souligné plus haut, scikit-learn implémente actuellement la métrique "diminution moyenne de l'impureté" pour les importances des caractéristiques. Personnellement, je trouve la deuxième métrique un peu plus intéressante, où vous permutez aléatoirement les valeurs de chacune de vos fonctionnalités une par une et voyez à quel point vos performances hors du sac sont pires.

Puisque ce que vous recherchez avec l'importance des caractéristiques, c'est la contribution de chaque caractéristique à la performance prédictive globale de votre modèle, la deuxième métrique vous donne en fait une mesure directe de celle-ci, alors que la "diminution moyenne de l'impureté" n'est qu'une bonne approximation.

Si cela vous intéresse, j'ai écrit un petit paquet qui met en œuvre la métrique de l'importance de la permutation et qui peut être utilisé pour calculer les valeurs d'une instance d'une classe de forêt aléatoire scikit-learn :

https://github.com/pjh2011/rf_perm_feat_import

Edit : Ceci fonctionne pour Python 2.7, pas 3

0 votes

Bonjour @Peter, lorsque j'utilise votre code, j'obtiens cette erreur : NameError : le nom 'xrange' n'est pas défini.

0 votes

Bonjour @Aizzaac. Désolé, je n'ai pas l'habitude d'écrire des paquets, j'aurais donc dû préciser que je l'ai écrit pour Python 2.7. Essayez def xrange(x) : return iter(range(x)) avant de l'exécuter.

4voto

tengfei li Points 31

Code :

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

plot decision_tree :
Entrez la description de l'image ici
On obtient

compute_feature_importance:[0. ,0.01333333,0.06405596,0.92261071]   

Vérifiez le code source :

cpdef compute_feature_importances(self, normalize=True):
    """Computes the importance of each feature (aka variable)."""
    cdef Node* left
    cdef Node* right
    cdef Node* nodes = self.nodes
    cdef Node* node = nodes
    cdef Node* end_node = node + self.node_count

    cdef double normalizer = 0.

    cdef np.ndarray[np.float64_t, ndim=1] importances
    importances = np.zeros((self.n_features,))
    cdef DOUBLE_t* importance_data = <DOUBLE_t*>importances.data

    with nogil:
        while node != end_node:
            if node.left_child != _TREE_LEAF:
                # ... and node.right_child != _TREE_LEAF:
                left = &nodes[node.left_child]
                right = &nodes[node.right_child]

                importance_data[node.feature] += (
                    node.weighted_n_node_samples * node.impurity -
                    left.weighted_n_node_samples * left.impurity -
                    right.weighted_n_node_samples * right.impurity)
            node += 1

    importances /= nodes[0].weighted_n_node_samples

    if normalize:
        normalizer = np.sum(importances)

        if normalizer > 0.0:
            # Avoid dividing by zero (e.g., when root is pure)
            importances /= normalizer

    return importances

Essayez de calculer l'importance de la caractéristique :

print("sepal length (cm)",0)
print("sepal width (cm)",(3*0.444-(0+0)))
print("petal length (cm)",(54* 0.168 - (48*0.041+6*0.444)) +(46*0.043 -(0+3*0.444)) + (3*0.444-(0+0)))
print("petal width (cm)",(150* 0.667 - (0+100*0.5)) +(100*0.5-(54*0.168+46*0.043))+(6*0.444 -(0+3*0.444)) + (48*0.041-(0+0)))

Nous obtenons feature_importance : np.array([0,1.332,6.418,92.30]) .

Après normalisation, on obtient array ([0., 0.01331334, 0.06414793, 0.92253873]) ,c'est la même chose que clf.feature_importances_ .

Attention, toutes les classes sont censées avoir un poids.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X