Importanza variabile relativa per il potenziamento


33

Sto cercando una spiegazione di come viene calcolata l'importanza della variabile relativa negli alberi potenziati a gradiente che non è eccessivamente generale / semplicistica come:

Le misure si basano sul numero di volte in cui una variabile viene selezionata per la divisione, ponderata dal miglioramento quadrato del modello come risultato di ciascuna divisione e mediata su tutti gli alberi . [ Elith et al. 2008, Una guida di lavoro per potenziare gli alberi di regressione ]

E questo è meno astratto di:

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

Dove la sommatoria è il non terminale nodi della -Terminal albero nodo , è la variabile frazionamento associato con il nodo , e è il corrispondente miglioramento empirica in errore quadratico come risultato della divisione, definita come , dove sono rispettivamente i mezzi di risposta figlia destra e sinistra, e sono le somme corrispondenti dei pesi. tJTvttit2^i2(Rl,Rr)=wlwrwl+wr(yl¯yr¯)2yl¯,yr¯wl,wr[ Friedman 2001, approssimazione di funzioni avide: una macchina per aumentare il gradiente ]

Infine, non ho trovato gli Elementi di apprendimento statistico (Hastie et al. 2008) molto utili per leggere qui, poiché la sezione pertinente (10.13.1 pagina 367) ha un sapore molto simile al secondo riferimento sopra (che potrebbe essere spiegato dal fatto che Friedman è coautore del libro).

PS: So che le misure relative all'importanza variabile sono fornite da summary.gbm nel pacchetto gbm R. Ho cercato di esplorare il codice sorgente, ma non riesco a trovare dove si svolge il calcolo effettivo.

Punti Brownie: mi chiedo come ottenere queste trame in R.


Ho appena aggiunto una nuova risposta alla domanda legata su come tracciare variabile importanza per classe che può essere utile stackoverflow.com/a/51952918/3277050
see24

Risposte:


55

Userò lo sklearn codice, in quanto è in genere molto più pulito rispetto al Rcodice.

Ecco l'implementazione della proprietà feature_importances di GradientBoostingClassifier (ho rimosso alcune righe di codice che ostacolano le cose concettuali)

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

Questo è abbastanza facile da capire. self.estimators_è un array che contiene i singoli alberi nel booster, quindi il ciclo for sta iterando sui singoli alberi. C'è un singhiozzo con il

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

questo si occupa del caso di risposta non binaria. Qui inseriamo più alberi in ogni fase in un modo uno contro tutti. Concettualmente è più semplice concentrarsi sul caso binario, in cui la somma ha un summand, e questo è giusto tree.feature_importances_. Quindi nel caso binario, possiamo riscrivere tutto come

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

Quindi, in parole, riassumi le caratteristiche dell'importanza dei singoli alberi, quindi dividi per il numero totale di alberi . Resta da vedere come calcolare le importazioni delle funzionalità per un singolo albero.

Il calcolo dell'importanza di un albero è implementato a livello di cython , ma è ancora seguibile. Ecco una versione ripulita del codice

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

Questo è abbastanza semplice Scorrere i nodi dell'albero. Finché non ci si trova in un nodo foglia, calcolare la riduzione ponderata della purezza del nodo dalla divisione in questo nodo e attribuirla alla funzione su cui è stata suddivisa

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)

Quindi, al termine, dividere tutto per il peso totale dei dati (nella maggior parte dei casi, il numero di osservazioni)

importances /= nodes[0].weighted_n_node_samples

Vale la pena ricordare che l' impurità è un nome comune che la metrica deve usare quando si determina quale scissione fare quando si coltiva un albero. Alla luce di ciò, stiamo semplicemente riassumendo la suddivisione su ciascuna caratteristica che ci ha permesso di ridurre l'impurità in tutte le divisioni nella struttura.

Nel contesto del potenziamento del gradiente, questi alberi sono sempre alberi di regressione (minimizzano avidamente l'errore al quadrato) adatti al gradiente della funzione di perdita.


Grazie mille per questa risposta molto dettagliata. Lasciami un po 'di tempo per esaminarlo attentamente prima di accettarlo.
Antoine,

4
Mentre sembra che possano essere usati vari criteri di impurità, l'indice Gini non era il criterio usato da Friedman. Come menzionato nella mia domanda e riga 878 del terzo link, Friedman ha utilizzato il criterio dell'impurità dell'errore al quadrato medio con il punteggio di miglioramento . Se potessi aggiornare questa sezione della tua risposta, sarebbe fantastico. E sì, hai ragione, sembra che i pesi siano davvero il numero di osservazioni.
Antoine,

3
o forse renderebbe la tua risposta ancora migliore mantenere entrambe le parti relative all'indice Gini e al criterio originale di Friedman, sottolineando che il primo è usato per la classificazione e il secondo per la regressione?
Antoine,

Antoine, grazie per questo aggiornamento. È davvero utile sapere che l'errore quadratico medio è rappresentato dai criteri di miglioramento utilizzati per gli alberi di regressione. Non era ovvio come sarebbe stato utilizzato per la classificazione. Tuttavia, anche nel potenziamento del gradiente per la classificazione, penso che gli alberi di regressione vengano ancora utilizzati, al contrario degli alberi di classificazione. Almeno in Python, l'analisi di regressione viene eseguita sull'errore corrente in ogni fase di potenziamento.
Abbastanza nerd il

Ragazzi, avete ragione sugli alberi di regressione.
Matthew Drury,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.