Standardschwelle für scikit-learn .predict ()

75

Ich arbeite an einem Klassifizierungsproblem mit unausgeglichenen Klassen (5% 1). Ich möchte die Klasse vorhersagen, nicht die Wahrscheinlichkeit.

classifier.predict()Verwendet Scikit bei einem binären Klassifizierungsproblem 0.5standardmäßig? Wenn nicht, wie lautet die Standardmethode? Wenn ja, wie ändere ich es?

In Scikit haben einige Klassifikatoren die class_weight='auto'Option, aber nicht alle. Mit class_weight='auto'würde .predict()der tatsächliche Bevölkerungsanteil als Schwelle verwendet werden?

Was wäre der Weg, dies in einem Klassifikator zu tun MultinomialNB, der nicht unterstützt wird class_weight? Anders als predict_proba()die Klassen selbst zu benutzen und dann zu berechnen.

ADJ
quelle

Antworten:

42

Verwendet Scikit classifier.predict()standardmäßig 0,5?

In probabilistischen Klassifikatoren ja. Es ist aus mathematischer Sicht die einzig sinnvolle Schwelle, wie andere erklärt haben.

Wie würde dies in einem Klassifikator wie MultinomialNB geschehen, der dies nicht unterstützt class_weight?

Sie können class_priordie vorherige Wahrscheinlichkeit P ( y ) pro Klasse y einstellen . Das verschiebt effektiv die Entscheidungsgrenze. Z.B

# minimal dataset
>>> X = [[1, 0], [1, 0], [0, 1]]
>>> y = [0, 0, 1]
# use empirical prior, learned from y
>>> MultinomialNB().fit(X,y).predict([1,1])
array([0])
# use custom prior to make 1 more likely
>>> MultinomialNB(class_prior=[.1, .9]).fit(X,y).predict([1,1])
array([1])
Fred Foo
quelle
Es scheint, dass es keinen Class_prior für RandomForestClassifier gibt. Wie geht man das an?
Famargar
2
Der RandomForestClassifier hat keinen class_prior-Parameter, aber einen class_weight-Parameter, der verwendet werden kann.
lbcommer
4
Tatsächlich ist der Standardwert von 0,5 willkürlich und muss nicht optimal sein, wie z. B. in dieser Antwort auf den Lebenslauf von Frank Harrell, der eine resezierte Behörde ist , bemerkt wird.
Tim
"In probabilistischen Klassifikatoren ja. Aus mathematischer Sicht ist dies die einzig sinnvolle Schwelle, wie andere erklärt haben." - Dies scheint völlig falsch. Was ist, wenn Sie beispielsweise den Rückruf über die Präzision gewichten möchten?
Cyniphile
39

Der Schwellenwert kann mit eingestellt werden clf.predict_proba()

zum Beispiel:

from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(random_state = 2)
clf.fit(X_train,y_train)
# y_pred = clf.predict(X_test)  # default threshold is 0.5
y_pred = (clf.predict_proba(X_test)[:,1] >= 0.3).astype(bool) # set threshold as 0.3
Yuchao Jiang
quelle
6
Zur Verdeutlichung legen Sie den Schwellenwert nicht fest , da dies bedeuten würde, dass Sie das Verhalten von dauerhaft ändern clf.predict(), was Sie nicht tun.
pcko1
Dies ist die richtige Antwort. Ich konnte in der MLP-Quelle nicht sehen, wo sie die Schwelle von 0,5 erreichen ...
Eggie5
Wie würden Sie dies in GridSearchCV einbinden, wo die durchgeführte Vorhersage intern und für Sie nicht zugänglich ist? Angenommen, ein Schwellenwert von 0,3 würde mir eine andere beste Modellauswahl bringen.
Demongolem
2
Ich denke, GridSearchCV wird nur den Standardschwellenwert von 0,5 verwenden. Es ist nicht sinnvoll, diese Schwelle während des Trainings zu ändern, da wir wollen, dass alles fair ist. Erst in der letzten Vorhersagephase stellen wir die Wahrscheinlichkeitsschwelle so ein, dass ein positiveres oder negativeres Ergebnis erzielt wird. Um beispielsweise eine größere Erfassungsrate zu erzielen (auf Kosten eines höheren Fehlalarms), können wir den Schwellenwert manuell senken.
Yuchao Jiang
37

Der Schwellenwert beim Scikit-Lernen beträgt 0,5 für die binäre Klassifizierung und die Klasse mit der größten Wahrscheinlichkeit für die Klassifizierung mehrerer Klassen. Bei vielen Problemen kann durch Einstellen des Schwellenwerts ein viel besseres Ergebnis erzielt werden. Dies muss jedoch mit Sorgfalt und NICHT anhand der Holdout-Testdaten erfolgen, sondern durch Kreuzvalidierung der Trainingsdaten. Wenn Sie den Schwellenwert für Ihre Testdaten anpassen, passen Sie die Testdaten nur an.

Die meisten Methoden zum Anpassen des Schwellenwerts basieren auf den Empfängerbetriebseigenschaften (ROC) und der Youden-J-Statistik , können jedoch auch mit anderen Methoden durchgeführt werden, z. B. mit einer Suche mit einem genetischen Algorithmus.

Hier ist ein Artikel in einem Peer-Review-Journal, in dem dies in der Medizin beschrieben wird:

http://www.ncbi.nlm.nih.gov/pmc/articles/PMC2515362/

Soweit ich weiß, gibt es kein Paket dafür in Python, aber es ist relativ einfach (aber ineffizient), es mit einer Brute-Force-Suche in Python zu finden.

Dies ist ein R-Code, der dies tut.

## load data
DD73OP <- read.table("/my_probabilites.txt", header=T, quote="\"")

library("pROC")
# No smoothing
roc_OP <- roc(DD73OP$tc, DD73OP$prob)
auc_OP <- auc(roc_OP)
auc_OP
Area under the curve: 0.8909
plot(roc_OP)

# Best threshold
# Method: Youden
#Youden's J statistic (Youden, 1950) is employed. The optimal cut-off is the threshold that maximizes the distance to the identity (diagonal) line. Can be shortened to "y".
#The optimality criterion is:
#max(sensitivities + specificities)
coords(roc_OP, "best", ret=c("threshold", "specificity", "sensitivity"), best.method="youden")
#threshold specificity sensitivity 
#0.7276835   0.9092466   0.7559022
denson
quelle
5
Guter Eintrag! Wichtigster Punkt: "Wenn Sie den Schwellenwert für Ihre Testdaten anpassen, passen Sie die Testdaten nur an."
Sven R. Kunze
7

Sie scheinen hier verwirrende Konzepte zu haben. Der Schwellenwert ist kein Konzept für einen "generischen Klassifikator" - die grundlegendsten Ansätze basieren auf einem einstellbaren Schwellenwert, aber die meisten vorhandenen Methoden erstellen komplexe Regeln für die Klassifizierung, die nicht als Schwellenwert angesehen werden können (oder sollten).

Also zuerst - man kann Ihre Frage nach dem Standardschwellenwert für den Klassifikator von scikit nicht beantworten, weil es so etwas nicht gibt.

Bei der Gewichtung der zweiten Klasse geht es nicht um Schwellenwerte, sondern um die Fähigkeit des Klassifikators, mit unausgeglichenen Klassen umzugehen, und es geht um etwas, das von einem bestimmten Klassifikator abhängt. Zum Beispiel - im SVM-Fall ist dies die Art und Weise, wie die Slack-Variablen im Optimierungsproblem gewichtet werden, oder, wenn Sie es vorziehen - die Obergrenzen für die mit bestimmten Klassen verbundenen Lagrange-Multiplikatorwerte. Wenn Sie dies auf "Auto" setzen, wird eine Standardheuristik verwendet, aber auch dies kann nicht einfach in einen Schwellenwert übersetzt werden.

Naive Bayes hingegen schätzt die Klassenwahrscheinlichkeit direkt aus dem Trainingssatz. Es heißt "class prior" und kann im Konstruktor mit der Variablen "class_prior" festgelegt werden.

Aus der Dokumentation :

Vorherige Wahrscheinlichkeiten der Klassen. Wenn angegeben, werden die Prioritäten nicht gemäß den Daten angepasst.

Lejlot
quelle
2
Lassen Sie mich das anders erklären, dann können Sie sagen, dass ich immer noch verwirrt bin :-). Angenommen, ich habe zwei Klassen. Die meisten Klassifikatoren sagen eine Wahrscheinlichkeit voraus. Ich kann die Wahrscheinlichkeit verwenden, um mein Modell zu bewerten, beispielsweise mithilfe eines ROC. Aber wenn ich eine Klasse vorhersagen wollte, müsste ich einen Cutoff wählen, sagen wir 0,5, und sagen: "Jede Beobachtung mit p <0,5 geht in Klasse 0, und diejenigen mit p> 0,5 gehen in Klasse 1. Das ist normalerweise gut." Wahl, wenn Ihre Prioritäten 0,5-0,5 sind. Aber für unausgeglichene Probleme brauche ich einen anderen Cutoff. Meine Frage war wirklich, wie dieser Cutoff in Scikit behandelt wird, wenn .predict () verwendet wird.
ADJ
Die meisten Klassifikatoren sind keine probabilistischen. Die Tatsache, dass sie diese Wahrscheinlichkeit (Schätzung) irgendwie "produzieren" können, bedeutet nicht, dass sie sie tatsächlich "verwenden", um eine Vorhersage zu treffen. Deshalb bezeichne ich dies als wahrscheinliche Verwirrung. Predict ruft die Routine des Originalmodells auf, die zur Vorhersage verwendet wird. Sie kann probabilistisch (NB), geometrisch (SVM), regressionsbasiert (NN) oder regelbasiert (Bäume) sein. Die Frage nach einem Wahrscheinlichkeitswert in predict () scheint also wie folgt: a konzeptionelle Verwirrung.
Lejlot
2
@lejlot, wenn das der Fall ist, würde dann nicht auch das gesamte Konzept der mit Predict_Proba gezeichneten ROC-Kurve irrelevant werden? Werden nicht unterschiedliche Punkte der ROC-Kurve bei unterschiedlichen Schwellenwerten aufgezeichnet, die auf die Ergebnisse von Predict_Proba angewendet werden?
Eugene Bragin
2

Falls jemand diesen Thread besucht und auf eine gebrauchsfertige Funktion hofft (Python 2.7). In diesem Beispiel soll der Cutoff das Verhältnis von Ereignissen zu Nichtereignissen im Originaldatensatz df widerspiegeln , während y_prob das Ergebnis der .predict_proba-Methode sein könnte (unter der Annahme einer geschichteten Zug- / Testaufteilung).

def predict_with_cutoff(colname, y_prob, df):
    n_events = df[colname].values
    event_rate = sum(n_events) / float(df.shape[0]) * 100
    threshold = np.percentile(y_prob[:, 1], 100 - event_rate)
    print "Cutoff/threshold at: " + str(threshold)
    y_pred = [1 if x >= threshold else 0 for x in y_prob[:, 1]]
    return y_pred

Fühlen Sie sich frei zu kritisieren / ändern. Ich hoffe, es hilft in seltenen Fällen, wenn ein Klassenausgleich nicht in Frage kommt und der Datensatz selbst stark unausgeglichen ist.

michalw
quelle