2014-06-09 6 views
14

Sto tentando di preformare l'eliminazione di funzionalità ricorsiva usando scikit-learn e un classificatore di foresta casuale, con OOB ROC come metodo di calcolo di ogni sottogruppo creato durante il processo ricorsivo.Eliminazione di feature ricorsive su Random Forest utilizzando scikit-learn

Tuttavia, quando si tenta di utilizzare il metodo RFECV, ottengo un errore che dice AttributeError: 'RandomForestClassifier' object has no attribute 'coef_'

foreste casuali non hanno coefficienti di per sé, ma hanno classifiche di punteggio Gini. Quindi, mi sto chiedendo come risolvere questo problema.

Si prega di notare che io voglio usare un metodo che in modo esplicito dirmi che cosa caratterizza dal mio pandas dataframe sono stati selezionati nel raggruppamento ottimale come io sto usando la selezione delle funzioni ricorsive per cercare di ridurre al minimo la quantità di dati che saranno immessi nel classificatore finale.

Ecco qualche esempio di codice:

from sklearn import datasets 
import pandas as pd 
from pandas import Series 
from sklearn.ensemble import RandomForestClassifier 
from sklearn.feature_selection import RFECV 

iris = datasets.load_iris() 
x=pd.DataFrame(iris.data, columns=['var1','var2','var3', 'var4']) 
y=pd.Series(iris.target, name='target') 
rf = RandomForestClassifier(n_estimators=500, min_samples_leaf=5, n_jobs=-1) 
rfecv = RFECV(estimator=rf, step=1, cv=10, scoring='ROC', verbose=2) 
selector=rfecv.fit(x, y) 

Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/feature_selection/rfe.py", line 336, in fit 
    ranking_ = rfe.fit(X_train, y_train).ranking_ 
    File "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/feature_selection/rfe.py", line 148, in fit 
    if estimator.coef_.ndim > 1: 
AttributeError: 'RandomForestClassifier' object has no attribute 'coef_' 
+1

Un approccio alternativo consiste nell'utilizzare l'attributo 'feature_importances_' dopo aver chiamato' predicti' o 'predict_proba', restituisce un array di percentuali nell'ordine in cui sono stati passati. Vedere l'esempio [online] (http://scikit-learn.org/stable/auto_examples/ensemble/plot_gradient_boosting_regression.html) – EdChum

+0

Visto questo; Voglio sapere se c'è qualcosa che mi consente di convalidare 10 volte e identificare il sottoinsieme ottimale di funzionalità, però. – Bryan

+0

Ho dovuto fare qualcosa di simile ma l'ho fatto manualmente ordinando le funzionalità importate e poi tagliando per 1,3 o 5 funzioni alla volta. Non ho usato il tuo approccio che devo dire, quindi non so se si può fare. – EdChum

risposta

3

Ecco cosa ho avviato. È una soluzione piuttosto semplice e fa affidamento su una metrica di accuratezza personalizzata (chiamata weightedAccuracy) poiché sto classificando un set di dati altamente sbilanciati. Ma, dovrebbe essere facilmente reso più estensibile se lo si desidera.

from sklearn import datasets 
import pandas 
from sklearn.ensemble import RandomForestClassifier 
from sklearn import cross_validation 
from sklearn.metrics import confusion_matrix 


def get_enhanced_confusion_matrix(actuals, predictions, labels): 
    """"enhances confusion_matrix by adding sensivity and specificity metrics""" 
    cm = confusion_matrix(actuals, predictions, labels = labels) 
    sensitivity = float(cm[1][1])/float(cm[1][0]+cm[1][1]) 
    specificity = float(cm[0][0])/float(cm[0][0]+cm[0][1]) 
    weightedAccuracy = (sensitivity * 0.9) + (specificity * 0.1) 
    return cm, sensitivity, specificity, weightedAccuracy 

iris = datasets.load_iris() 
x=pandas.DataFrame(iris.data, columns=['var1','var2','var3', 'var4']) 
y=pandas.Series(iris.target, name='target') 

response, _ = pandas.factorize(y) 

xTrain, xTest, yTrain, yTest = cross_validation.train_test_split(x, response, test_size = .25, random_state = 36583) 
print "building the first forest" 
rf = RandomForestClassifier(n_estimators = 500, min_samples_split = 2, n_jobs = -1, verbose = 1) 
rf.fit(xTrain, yTrain) 
importances = pandas.DataFrame({'name':x.columns,'imp':rf.feature_importances_ 
           }).sort(['imp'], ascending = False).reset_index(drop = True) 

cm, sensitivity, specificity, weightedAccuracy = get_enhanced_confusion_matrix(yTest, rf.predict(xTest), [0,1]) 
numFeatures = len(x.columns) 

rfeMatrix = pandas.DataFrame({'numFeatures':[numFeatures], 
           'weightedAccuracy':[weightedAccuracy], 
           'sensitivity':[sensitivity], 
           'specificity':[specificity]}) 

print "running RFE on %d features"%numFeatures 

for i in range(1,numFeatures,1): 
    varsUsed = importances['name'][0:i] 
    print "now using %d of %s features"%(len(varsUsed), numFeatures) 
    xTrain, xTest, yTrain, yTest = cross_validation.train_test_split(x[varsUsed], response, test_size = .25) 
    rf = RandomForestClassifier(n_estimators = 500, min_samples_split = 2, 
           n_jobs = -1, verbose = 1) 
    rf.fit(xTrain, yTrain) 
    cm, sensitivity, specificity, weightedAccuracy = get_enhanced_confusion_matrix(yTest, rf.predict(xTest), [0,1]) 
    print("\n"+str(cm)) 
    print('the sensitivity is %d percent'%(sensitivity * 100)) 
    print('the specificity is %d percent'%(specificity * 100)) 
    print('the weighted accuracy is %d percent'%(weightedAccuracy * 100)) 
    rfeMatrix = rfeMatrix.append(
           pandas.DataFrame({'numFeatures':[len(varsUsed)], 
           'weightedAccuracy':[weightedAccuracy], 
           'sensitivity':[sensitivity], 
           'specificity':[specificity]}), ignore_index = True)  
print("\n"+str(rfeMatrix))  
maxAccuracy = rfeMatrix.weightedAccuracy.max() 
maxAccuracyFeatures = min(rfeMatrix.numFeatures[rfeMatrix.weightedAccuracy == maxAccuracy]) 
featuresUsed = importances['name'][0:maxAccuracyFeatures].tolist() 

print "the final features used are %s"%featuresUsed 
6

Questo è il mio codice, ho riordinato su una punta per rendere più rilevanti per il vostro compito:

features_to_use = fea_cols # this is a list of features 
# empty dataframe 
trim_5_df = DataFrame(columns=features_to_use) 
run=1 
# this will remove the 5 worst features determined by their feature importance computed by the RF classifier 
while len(features_to_use)>6: 
    print('number of features:%d' % (len(features_to_use))) 
    # build the classifier 
    clf = RandomForestClassifier(n_estimators=1000, random_state=0, n_jobs=-1) 
    # train the classifier 
    clf.fit(train[features_to_use], train['OpenStatusMod'].values) 
    print('classifier score: %f\n' % clf.score(train[features_to_use], df['OpenStatusMod'].values)) 
    # predict the class and print the classification report, f1 micro, f1 macro score 
    pred = clf.predict(test[features_to_use]) 
    print(classification_report(test['OpenStatusMod'].values, pred, target_names=status_labels)) 
    print('micro score: ') 
    print(metrics.precision_recall_fscore_support(test['OpenStatusMod'].values, pred, average='micro')) 
    print('macro score:\n') 
    print(metrics.precision_recall_fscore_support(test['OpenStatusMod'].values, pred, average='macro')) 
    # predict the class probabilities 
    probs = clf.predict_proba(test[features_to_use]) 
    # rescale the priors 
    new_probs = kf.cap_and_update_priors(priors, probs, private_priors, 0.001) 
    # calculate logloss with the rescaled probabilities 
    print('log loss: %f\n' % log_loss(test['OpenStatusMod'].values, new_probs)) 
    row={} 
    if hasattr(clf, "feature_importances_"): 
     # sort the features by importance 
     sorted_idx = np.argsort(clf.feature_importances_) 
     # reverse the order so it is descending 
     sorted_idx = sorted_idx[::-1] 
     # add to dataframe 
     row['num_features'] = len(features_to_use) 
     row['features_used'] = ','.join(features_to_use) 
     # trim the worst 5 
     sorted_idx = sorted_idx[: -5] 
     # swap the features list with the trimmed features 
     temp = features_to_use 
     features_to_use=[] 
     for feat in sorted_idx: 
      features_to_use.append(temp[feat]) 
     # add the logloss performance 
     row['logloss']=[log_loss(test['OpenStatusMod'].values, new_probs)] 
    print('') 
    # add the row to the dataframe 
    trim_5_df = trim_5_df.append(DataFrame(row)) 
run +=1 

Quindi quello che sto facendo ecco qui ho una lista di funzionalità che voglio allenare e quindi prevedo, utilizzando le funzionalità importate quindi ritaglia il peggiore 5 e ripeto. Durante ogni esecuzione aggiungo una riga per registrare le prestazioni di previsione in modo da poter eseguire alcune analisi in un secondo momento.

Il codice originale era molto più grande Ho avuto diversi classificatori e set di dati che stavo analizzando ma spero che tu abbia ottenuto l'immagine da sopra. La cosa che ho notato è che per le foreste casuali il numero di funzioni rimosse su ogni corsa ha influito sulle prestazioni, pertanto il taglio delle funzioni 1, 3 e 5 alla volta ha prodotto un set diverso di migliori funzionalità.

ho scoperto che utilizzando un GradientBoostingClassifer era più prevedibile e ripetibile nel senso che il set finale delle migliori caratteristiche d'accordo se ho tagliato 1 funzione alla volta o 3 o 5.

Spero di non sto insegnando tu a succhiare le uova qui, probabilmente ne sai più di me, ma il mio approccio all'ablazione ablativa era usare un classificatore veloce per avere un'idea approssimativa dei migliori set di funzionalità, quindi utilizzare un classificatore con prestazioni migliori, quindi avviare la regolazione dei parametri iper, di nuovo facendo le comaprisons a grana grossa e poi a grana fine una volta che ho avuto un'idea di quali fossero i migliori params.

17

Ecco quello che ho fatto per adattarsi RandomForestClassifier a lavorare con RFECV:

class RandomForestClassifierWithCoef(RandomForestClassifier): 
    def fit(self, *args, **kwargs): 
     super(RandomForestClassifierWithCoef, self).fit(*args, **kwargs) 
     self.coef_ = self.feature_importances_ 

Proprio utilizzando questa classe fa il trucco, se si utilizza la 'precisione' o il punteggio 'F1'. Per 'roc_auc', RFECV lamenta che il formato multiclasse non è supportato. Passando alla classificazione a due classi con il codice sottostante, il punteggio 'roc_auc' funziona. (Utilizzando Python 3.4.1 e scikit-learn 0.15.1)

y=(pd.Series(iris.target, name='target')==2).astype(int) 

Inserendo nel codice:

from sklearn import datasets 
import pandas as pd 
from pandas import Series 
from sklearn.ensemble import RandomForestClassifier 
from sklearn.feature_selection import RFECV 

class RandomForestClassifierWithCoef(RandomForestClassifier): 
    def fit(self, *args, **kwargs): 
     super(RandomForestClassifierWithCoef, self).fit(*args, **kwargs) 
     self.coef_ = self.feature_importances_ 

iris = datasets.load_iris() 
x=pd.DataFrame(iris.data, columns=['var1','var2','var3', 'var4']) 
y=(pd.Series(iris.target, name='target')==2).astype(int) 
rf = RandomForestClassifierWithCoef(n_estimators=500, min_samples_leaf=5, n_jobs=-1) 
rfecv = RFECV(estimator=rf, step=1, cv=2, scoring='roc_auc', verbose=2) 
selector=rfecv.fit(x, y) 
4

ho presentato una richiesta di aggiungere coef_ così RandomForestClassifier possono essere utilizzati con RFECV. Tuttavia, il cambiamento era già stato fatto. Questa modifica sarà nella versione 0.17.

https://github.com/scikit-learn/scikit-learn/issues/4945

Si può tirare l'ultimo dev costruire, se si desidera utilizzare ora.