scikit .predict() порог по умолчанию
Я работаю над проблемой классификации с несбалансированными классами (5% 1). Я хочу предсказать класс, а не вероятность.
В проблеме бинарной классификации, это Scikit's classifier.predict()
с помощью 0.5
по умолчанию? Если это не так, каков метод по умолчанию? Если это так, как я могу изменить это?
В скиките некоторые классификаторы имеют class_weight='auto'
вариант, но не все делают. С class_weight='auto'
, было бы .predict()
использовать фактическую долю населения в качестве порога?
Как бы это сделать в таком классификаторе, как MultinomialNB
это не поддерживает class_weight
? Кроме использования predict_proba()
а потом сам вычисляю классы.
8 ответов
это Скикит
classifier.predict()
используя 0.5 по умолчанию?
В вероятностных классификаторах да. Это единственный разумный порог с математической точки зрения, как объяснили другие.
Какой способ сделать это в классификаторе, таком как MultinomialNB, который не поддерживает
class_weight
?
Вы можете установить class_prior
, которая является предшествующей вероятностью P (y) на класс y. Это эффективно сдвигает границы решения. Например
# 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])
Порог может быть установлен с помощью clf.predict_proba()
например:
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
Порог в scikit обучения составляет 0,5 для двоичной классификации, и какой класс имеет наибольшую вероятность для мультиклассовой классификации. Во многих задачах гораздо лучший результат может быть получен путем регулировки порога. Тем не менее, это должно быть сделано с осторожностью, а не с данными теста на вынос, а путем перекрестной проверки данных обучения. Если вы делаете какую-либо настройку порога для ваших тестовых данных, вы просто перегоняете тестовые данные.
Большинство методов регулировки порога основаны на рабочих характеристиках приемника (ROC) и J-статистике Юдена, но это также может быть сделано другими методами, такими как поиск с использованием генетического алгоритма.
Вот статья в журнале рецензирования, описывающая это в медицине:
http://www.ncbi.nlm.nih.gov/pmc/articles/PMC2515362/
Насколько я знаю, в Python нет пакета для этого, но относительно просто (но неэффективно) найти его с помощью перебора в Python.
Это некоторый код R, который делает это.
## 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
Вы, кажется, путаете понятия здесь. Порог не является понятием для "общего классификатора" - самые базовые подходы основаны на некотором настраиваемом пороге, но большинство существующих методов создают сложные правила для классификации, которые не могут (или, по крайней мере, не должны) рассматриваться как пороговое значение.
Итак, во-первых, никто не может ответить на ваш вопрос о пороге по умолчанию для классификатора scikit, потому что такого нет.
Взвешивание второго класса - это не пороговое значение, а способность классификатора иметь дело с несбалансированными классами, и оно зависит от конкретного классификатора. Например - в случае SVM это способ взвешивания слабых переменных в задаче оптимизации, или, если вы предпочитаете, - верхние границы для значений множителей Лагранжа, связанных с конкретными классами. Установка этого параметра на "auto" означает использование некоторой эвристики по умолчанию, но еще раз - ее нельзя просто перевести в какое-то пороговое значение.
Наивный Байес, с другой стороны, напрямую оценивает вероятность занятий по тренировочному набору. Он называется "приоритет класса", и вы можете установить его в конструкторе с помощью переменной "class_prior".
Из документации:
Априорные вероятности классов. Если указано, приоры не корректируются в соответствии с данными.
В случае, если кто-то посещает этот поток в надежде на готовую к использованию функцию (python 2.7). В этом примере срез предназначен для отражения отношения событий к не-событиям в исходном наборе данных df, тогда как y_prob может быть результатом метода.predict_proba (при условии расслоения поезда / теста).
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
Не стесняйтесь критиковать / изменять. Надеюсь, что это поможет в редких случаях, когда о балансировке классов не может быть и речи, а сам набор данных сильно разбалансирован.
Классификаторы Scikit-learn обычно выбирают прогнозируемый класс, взяв оценки/вероятности (см. LogisticRegression и DecisionTreeClassifier).
Для задач бинарной классификацииargmax
эквивалентно использованию порога 0,5 для вероятностей. В этом случае изменение порога изменяет вашу уверенность в предсказанных классах.
Вы можете настроить/изменить порог в соответствии с вашими целями (например, максимизировать точность или полноту). Концепция четко объяснена в этом посте. Можно автоматизировать нахождение оптимального порога любого классификатора путем извлечения предсказанных вероятностей и оптимизации интересующей метрики в проверочном наборе. Это делается:
import numpy as np
from sklearn.metrics import fbeta_score
from sklearn.model_selection import train_test_split
from sklearn.base import clone, BaseEstimator, ClassifierMixin
class ThresholdClassifier(BaseEstimator, ClassifierMixin):
def __init__(self, estimator, refit=True, val_size=0.3):
self.estimator = estimator
self.refit = refit
self.val_size = val_size
def fit(self, X, y):
def scoring(th, y, prob):
pred = (prob > th).astype(int)
return 0 if not pred.any() else \
-fbeta_score(y, pred, beta=0.1)
X_train, X_val, y_train, y_val = train_test_split(
X, y, stratify=y, test_size=self.val_size,
shuffle=True, random_state=1234
)
self.estimator_ = clone(self.estimator)
self.estimator_.fit(X_train, y_train)
prob_val = self.estimator_.predict_proba(X_val)[:,1]
thresholds = np.linspace(0,1, 200)[1:-1]
scores = [scoring(th, y_val, prob_val)
for th in thresholds]
self.score_ = np.min(scores)
self.th_ = thresholds[np.argmin(scores)]
if self.refit:
self.estimator_.fit(X, y)
if hasattr(self.estimator_, 'classes_'):
self.classes_ = self.estimator_.classes_
return self
def predict(self, X):
proba = self.estimator_.predict_proba(X)[:,1]
return (proba > self.th_).astype(int)
def predict_proba(self, X):
return self.estimator_.predict_proba(X)
При звонкеfit
:
- проверка (и
y_val
) набор генерируется случайным образом из полученных данных; - в
estimator
устанавливается наX_train
иy_train
; - вероятности (
prob_val
) извлекаются для класса 1 на ; - оптимальное пороговое значение находится на
X_val
путем оптимизации метрики выбора (fbeta_score
в нашем случае).
При звонкеpredict
: вероятности для класса 1 генерируются и преобразуются в бинарные классы по найденному оптимальному пороговому значению.
model = ThresholdClassifier(RandomForestClassifier()).fit(X_train, y_train)
pred_clas = model.predict(X_test)
ThresholdClassifier
может использоваться с любым классификатором sklearn, который производит вероятности. Его можно легко настроить в соответствии с различными потребностями. Это очень полезно в сочетании сGridSearchCV
/RandomSearchCV
связать поиск параметров с настройкой порога классификации.
model = RandomizedSearchCV(
ThresholdClassifier(RandomForestClassifier()),
dict(n_estimators=stats.randint(50,300)),
n_iter=20, random_state=1234,
cv=5, n_jobs=-1,
).fit(X_train, y_train)
0,5 никак не связано с долей населения. Это выход вероятности. Не существует "порога", если один класс имеет вероятность 0,51, то он представляется наиболее вероятным классом. 0,5, если всегда то, что следует использовать *, и ни один пакет не использует другой "порог". Если ваши оценки вероятности являются * точными и действительно репрезентативными *, то вы всегда должны выбирать наиболее вероятный класс. В противном случае можно только снизить вашу точность. Поскольку мы используем различные алгоритмы, которые делают предположения, мы не знаем, является ли вероятность истинной, но вы будете идти против предположений, сделанных вашей моделью.
Вы не понимаете, что делает class_weight. Изменение веса класса увеличивает веса для точек данных в менее представленных классах (/ уменьшается для более представленного класса), так что "вес" каждого класса равен - как если бы они имели одинаковое количество положительных и отрицательных примеров. Это обычная уловка для попытки избежать классификатора, который всегда голосует за самый распространенный класс. Потому что таким образом оба класса одинаково распространены с точки зрения алгоритма обучения.
- ПРИМЕЧАНИЕ: если у вас есть страх ложных срабатываний / ложных отрицаний, вы можете принять класс только в том случае, если его вероятность соответствует определенному минимальному значению. Но это не меняет способ обучения, и эта доза не меняет смысла вероятности.
Просто некоторые из моих мыслей.
- Отличный пост! Самый важный момент: «Если вы делаете какую-либо корректировку порога для ваших тестовых данных, вы просто переоцениваете тестовые данные». Не уверен в этом. Переоснащение касается чистых данных, но изменение порогового значения для удовлетворения требования высокой скорости отзыва связано с бизнесом. Учитывая те же данные, но значение, стоящее за ними, может измениться, вам может потребоваться или не потребуется перемещать порог в данных обучения и тестирования.
- Геометрические (KNN), основанные на регрессии (NN) или основанные на правилах (Деревья) - все они связаны с вероятностью, а вероятность используется в качестве пороговых значений для классификации, неявно или явно.