Wie erhalte ich die informativsten Funktionen für Scikit-Learn-Klassifikatoren?

70

Die Klassifizierer in Paketen für maschinelles Lernen wie liblinear und nltk bieten eine Methode show_most_informative_features(), die für das Debuggen von Funktionen sehr hilfreich ist:

viagra = None          ok : spam     =      4.5 : 1.0
hello = True           ok : spam     =      4.5 : 1.0
hello = None           spam : ok     =      3.3 : 1.0
viagra = True          spam : ok     =      3.3 : 1.0
casino = True          spam : ok     =      2.0 : 1.0
casino = None          ok : spam     =      1.5 : 1.0

Meine Frage ist, ob etwas Ähnliches für die Klassifikatoren in Scikit-Learn implementiert ist. Ich habe die Dokumentation durchsucht, aber nichts dergleichen gefunden.

Wenn es noch keine solche Funktion gibt, kennt jemand eine Problemumgehung, um zu diesen Werten zu gelangen?

Vielen Dank!

tobigue
quelle
Du meinst den diskriminierendsten Parameter?
Simon Bergot
Ich bin mir nicht sicher, was du mit Parametern meinst. Ich meine die diskriminierendsten Merkmale, wie in einem Wortsackmodell für die Spam-Klassifizierung, welche Wörter für jede Klasse die meisten Beweise liefern. nicht die Parameter, die ich als "Einstellungen" für den Klassifikator verstehe - wie Lernrate usw.
tobigue
9
@eowl: In der Sprache des maschinellen Lernens sind Parameter die Einstellungen, die durch den Lernvorgang basierend auf den Funktionen Ihres Trainingssatzes generiert werden . Lernrate usw. sind Hyperparameter .
Fred Foo

Antworten:

66

Die Klassifizierer selbst zeichnen keine Feature-Namen auf, sondern sehen nur numerische Arrays. Wenn Sie jedoch Ihre Funktionen mit einem extrahiert Vectorizer/ CountVectorizer/ TfidfVectorizer/ DictVectorizer, und Sie sind ein lineares Modell (zB LinearSVCoder Naive Bayes) , dann können Sie den gleichen Trick , dass die Anwendung Dokumentenklassifizierung Beispiel Verwendungen. Beispiel ( ungetestet , kann einen oder zwei Fehler enthalten):

def print_top10(vectorizer, clf, class_labels):
    """Prints features with the highest coefficient values, per class"""
    feature_names = vectorizer.get_feature_names()
    for i, class_label in enumerate(class_labels):
        top10 = np.argsort(clf.coef_[i])[-10:]
        print("%s: %s" % (class_label,
              " ".join(feature_names[j] for j in top10)))

Dies ist für die Klassifizierung mehrerer Klassen vorgesehen. Für den binären Fall denke ich, dass Sie clf.coef_[0]nur verwenden sollten. Möglicherweise müssen Sie die sortieren class_labels.

Fred Foo
quelle
Ja, in meinen Fällen habe ich nur zwei Klassen, aber mit Ihrem Code konnte ich mir das einfallen lassen, was ich wollte. Vielen Dank!
Tobigue
1
Für 2 Klassen sieht es coef_eher so aus als so coef_[0].
Ryan R. Rosario
2
@ RyanRosario: richtig. Im binären Fall coef_wird aus Platzgründen abgeflacht.
Fred Foo
4
Wie werden class_labels ermittelt? Ich möchte die Reihenfolge der Klassenbezeichnungen wissen.
Yandong Liu
2
Sie können geordnete Klassen vom Klassifikator mitclass_labels=clf.classes_
wassname
54

Mit Hilfe des Larsmans-Codes habe ich diesen Code für den Binärfall erstellt:

def show_most_informative_features(vectorizer, clf, n=20):
    feature_names = vectorizer.get_feature_names()
    coefs_with_fns = sorted(zip(clf.coef_[0], feature_names))
    top = zip(coefs_with_fns[:n], coefs_with_fns[:-(n + 1):-1])
    for (coef_1, fn_1), (coef_2, fn_2) in top:
        print "\t%.4f\t%-15s\t\t%.4f\t%-15s" % (coef_1, fn_1, coef_2, fn_2)
tobigue
quelle
Wie ruft man die Funktion von der Hauptmethode auf? Wofür stehen f1 und f2? Ich versuche, die Funktion vom Entscheidungsbaumklassifikator mit scikit-learn aufzurufen.
1
Dieser Code funktioniert nur mit einem linearen Klassifikator, der ein coef_Array hat. Daher glaube ich leider nicht, dass es möglich ist, ihn mit den Entscheidungsbaumklassifikatoren von sklearn zu verwenden. fn_1und fn_2stehen für die Feature-Namen.
Tobigue
16

RandomForestClassifierUnterstützt jetzt das .feature_importances_Attribut, um ein Update hinzuzufügen . Dieses Attribut gibt an, wie viel von der beobachteten Varianz durch diese Funktion erklärt wird. Offensichtlich muss die Summe aller dieser Werte <= 1 sein.

Ich finde dieses Attribut sehr nützlich, wenn ich Feature-Engineering durchführe.

Vielen Dank an das Scikit-Learn-Team und die Mitwirkenden für die Umsetzung!

Bearbeiten: Dies funktioniert sowohl für RandomForest als auch für GradientBoosting. Also RandomForestClassifier, RandomForestRegressor, GradientBoostingClassifierund GradientBoostingRegressoralle unterstützen diese.

ClimbsRocks
quelle
12

Wir haben kürzlich eine Bibliothek veröffentlicht ( https://github.com/TeamHG-Memex/eli5 ), die dies ermöglicht: Sie behandelt verschiedene Klassifizierer aus Scikit-Learn-, Binär- / Mehrklassenfällen und ermöglicht das Hervorheben von Text gemäß Feature-Werten , integriert sich in IPython usw.

Mikhail Korobov
quelle
Wenn jemand ein Start-Snippet benötigt: aus eli5 importiere show_weights show_weights (model, vec = tfidf)
Darius
4

Ich musste tatsächlich die Wichtigkeit von Features in meinem NaiveBayes-Klassifikator herausfinden, und obwohl ich die oben genannten Funktionen verwendet habe, konnte ich die Feature-Wichtigkeit nicht anhand von Klassen ermitteln. Ich habe die Dokumentation von scikit-learn durchgesehen und die oben genannten Funktionen ein wenig optimiert, um festzustellen, dass sie für mein Problem funktionieren. Hoffe es hilft dir auch!

def important_features(vectorizer,classifier,n=20):
    class_labels = classifier.classes_
    feature_names =vectorizer.get_feature_names()

    topn_class1 = sorted(zip(classifier.feature_count_[0], feature_names),reverse=True)[:n]
    topn_class2 = sorted(zip(classifier.feature_count_[1], feature_names),reverse=True)[:n]

    print("Important words in negative reviews")

    for coef, feat in topn_class1:
        print(class_labels[0], coef, feat)

    print("-----------------------------------------")
    print("Important words in positive reviews")

    for coef, feat in topn_class2:
        print(class_labels[1], coef, feat)

Beachten Sie, dass Ihr Klassifizierer (in meinem Fall NaiveBayes) das Attribut feature_count_ haben muss, damit dies funktioniert.

Sai Sandeep
quelle
1

Sie können auch Folgendes tun, um ein Diagramm mit wichtigen Funktionen in der angegebenen Reihenfolge zu erstellen:

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

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


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

Nicht genau das, wonach Sie suchen, aber eine schnelle Möglichkeit, die größten Größenkoeffizienten zu erhalten (vorausgesetzt, die Spalten eines Pandas-Datenrahmens sind Ihre Feature-Namen):

Sie haben das Modell wie folgt trainiert:

lr = LinearRegression()
X_train, X_test, y_train, y_test = train_test_split(df, Y, test_size=0.25)
lr.fit(X_train, y_train)

Erhalten Sie die 10 größten negativen Koeffizientenwerte (oder ändern Sie sie in umgekehrt = True für die größten positiven) wie:

sorted(list(zip(feature_df.columns, lr.coef_)), key=lambda x: x[1], 
reverse=False)[:10]
slevin886
quelle
0

Erst eine Liste erstellen, ich gebe dieser Liste das Namensschild. Danach extrahiere ich alle Features Namen und Spaltennamen, die ich in der Beschriftungsliste hinzufüge. Hier benutze ich naives Bayes-Modell. Im naiven Bayes-Modell gibt feature_log_prob_ die Wahrscheinlichkeit von Features an.

def top20(model,label):

  feature_prob=(abs(model.feature_log_prob_))

  for i in range(len(feature_prob)):

    print ('top 20 features for {} class'.format(i))

    clas = feature_prob[i,:]

    dictonary={}

    for count,ele in enumerate(clas,0): 

      dictonary[count]=ele

    dictonary=dict(sorted(dictonary.items(), key=lambda x: x[1], reverse=True)[:20])

    keys=list(dictonary.keys())

    for i in keys:

      print(label[i])

    print('*'*1000)
Ashish Saha
quelle