Importance relative relative pour Boosting

33

Je cherche une explication du calcul de l'importance relative relative des variables dans les arbres à gradient de densité, qui n'est pas trop générale / simpliste, comme:

Les mesures sont basées sur le nombre de fois qu'une variable est sélectionnée pour le fractionnement, pondérées par l'amélioration au carré du modèle à la suite de chaque fractionnement, et moyennées sur tous les arbres . [ Elith et al. 2008, Guide de travail sur les arbres de régression optimisés ]

Et c'est moins abstrait que:

Ij2^(T)=t=1J1it2^1(vt=j)

Lorsque la somme est sur les nœuds non terminaux de l' arbre de nœud J- terminal T , v t est la variable de scission associée au nœud t et ^ i 2 t est l'amélioration empirique correspondante de l'erreur au carré résultant de la scission définie comme i 2 ( R l , R r ) = w l w rtJTvttit2^, où ¯ y l , ¯ y r sont respectivement les moyennes de réponse fille gauche et droite, etwl,wrsont les sommes correspondantes des poids. i2(Rl,Rr)=wlwrwl+wr(yl¯yr¯)2yl¯,yr¯wl,wr[Friedman 2001, Approximation de la fonction gourmande: une machine à accentuer le gradient]

Enfin, je n’ai pas trouvé les éléments de l’apprentissage statistique (Hastie et al. 2008) très utiles à lire ici, car la section pertinente (10.13.1 page 367) a un goût très similaire à la deuxième référence ci-dessus (qui pourrait être expliquée plus loin). par le fait que Friedman est co-auteur du livre).

PS: Je sais que les mesures d’importance relative variable sont indiquées par summary.gbm dans le package gbm-R. J'ai essayé d'explorer le code source, mais je n'arrive pas à trouver où se trouve le calcul.

Points Brownie: Je me demande comment obtenir ces parcelles en R.

Antoine
la source
Je viens d' ajouter une nouvelle réponse à la question liée sur la façon de tracer l' importance des variables par classe qui peut être utile stackoverflow.com/a/51952918/3277050
see24

Réponses:

55

Je vais utiliser le code Sklearn , car il est généralement beaucoup plus propre que le Rcode.

Voici l'implémentation de la propriété feature_importances de GradientBoostingClassifier (j'ai supprimé certaines lignes de code qui gênent le contenu conceptuel)

def feature_importances_(self):
    total_sum = np.zeros((self.n_features, ), dtype=np.float64)
    for stage in self.estimators_:
        stage_sum = sum(tree.feature_importances_
                        for tree in stage) / len(stage)
        total_sum += stage_sum

    importances = total_sum / len(self.estimators_)
    return importances

C'est assez facile à comprendre. self.estimators_est un tableau contenant les arbres individuels dans le booster, de sorte que la boucle for parcourt les arbres individuels. Il y a un hickup avec le

stage_sum = sum(tree.feature_importances_
                for tree in stage) / len(stage)

cela prend en charge le cas de réponse non binaire. Ici, nous ajustons plusieurs arbres dans chaque étape d'une manière one-vs-all. Son concept le plus simple est de se concentrer sur le cas binaire, où la somme a un sommand, et c'est juste tree.feature_importances_. Donc, dans le cas binaire, nous pouvons réécrire tout cela comme

def feature_importances_(self):
    total_sum = np.zeros((self.n_features, ), dtype=np.float64)
    for tree in self.estimators_:
        total_sum += tree.feature_importances_ 
    importances = total_sum / len(self.estimators_)
    return importances

Donc, en mots, résumez les caractéristiques des arbres individuels, puis divisez-les par le nombre total d’arbres . Il reste à voir comment calculer l’importance des caractéristiques pour un seul arbre.

Le calcul de l'importance d'un arbre est implémenté au niveau de cython , mais il est toujours possible de le suivre. Voici une version nettoyée du code

cpdef compute_feature_importances(self, normalize=True):
    """Computes the importance of each feature (aka variable)."""

    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

    return importances

C'est assez simple. Parcourez les nœuds de l’arbre. Tant que vous n'êtes pas à un nœud feuille, calculez la réduction pondérée de la pureté du nœud à partir de la scission au niveau de ce nœud et attribuez-la à l'entité scindée le

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)

Ensuite, divisez le tout par le poids total des données (dans la plupart des cas, le nombre d'observations).

importances /= nodes[0].weighted_n_node_samples

Il convient de rappeler que l’ impureté est un nom commun que la métrique doit utiliser pour déterminer la division à faire lors de la croissance d’un arbre. Dans cette optique, nous résumons simplement à quel point le fractionnement de chaque fonction nous a permis de réduire l’impureté dans toutes les divisions de l’arbre.

Dans le contexte de l’intensification du gradient, ces arbres sont toujours des arbres de régression (minimiser les erreurs carrées goulûment) adaptées au gradient de la fonction de perte.

Matthew Drury
la source
Merci beaucoup pour cette réponse très détaillée. Laissez-moi un peu de temps pour l'examiner attentivement avant de l'accepter.
Antoine
4
Bien qu'il semble que divers critères d'impureté puissent être utilisés, l'indice de Gini n'était pas le critère utilisé par Friedman. Comme mentionné dans ma question et à la ligne 878 de votre troisième lien, Friedman a utilisé le critère d'impureté d'erreur quadratique moyenne avec le score d'amélioration . Si vous pouviez mettre à jour cette partie de votre réponse, ce serait formidable. Et oui, vous avez raison, il semble que les poids correspondent bien au nombre d'observations.
Antoine
3
ou peut-être serait-il encore plus intéressant de garder les deux parties sur l'indice de Gini et le critère initial de Friedman, en soulignant que la première est utilisée pour la classification et la seconde pour la régression?
Antoine
Antoine, merci pour cette mise à jour. Il est vraiment utile de savoir que l'erreur quadratique moyenne est le critère d'amélioration utilisé pour les arbres de régression. Il n'était pas évident de savoir comment cela serait utilisé pour la classification. Cependant, même dans l’amélioration progressive de la classification, je pense que les arbres de régression sont toujours utilisés, par opposition aux arbres de classification. Au moins en python, l'analyse de régression est effectuée sur l'erreur actuelle à chaque étape de renforcement.
Assez ringard
Vous avez raison sur les arbres de régression.
Matthew Drury