Недавно мне предстояло написать реализацию LogisiticRegression для одного проекта в Школе 21, так что было необходимо разложить всё по полочкам и разобраться в бинарной классификации в целом. Хочу поделиться также этой информацией здесь, потому что не нашла статьи, которая была бы понятна и обширна лично в моём случае. Автор хочет отметить, что только начинает свой путь в машинном обучении. Если в статье найдутся неточности, то с радостью будет их заметить в комментариях
Сегодня я бы хотела рассмотреть следующие аспекты:
Сигмойдная функция
MLE и NLL
Распределение Бернулли
Логистическая регрессия: как работает с точки зрения математики
Метрики бинарной классификации: ROC Curve, AUC, коэффициент Джини
Начнем с самой классификации, а именно с постановки задачи. Задача классификации обозначает то, что у нас есть объектов, каждый из которых имеет по
признаков, при этом каждый объект относится к одному из классов
. В таком случае говорят, что нужно найти отображение
из множества признаков
в множество размеченных данных
, причем
дискретно, и показывает определенный класс 1, 2, 3, ..., k.
Если , то речь идет о бинарной классификации (классы 0 и 1 - негативный и позитивный), иначе о многомерной (классы 1, 2, 3, ...). Безусловно, определить, что объект является кошкой или нет легче, чем выбирать еще между 10 вариантами. Поэтому решение многоклассовой задачи сложнее и объемнее, чем бинарной. Однако её можно решить сведением к бинарной. Так что приступим.
В случае классификации нам интересна вероятность принадлежности к какому классу для объекта i максимальна:
На примере бинарного случая, когда объект относится к классу 0 с вероятностью 0.6, а к классу 1 - всего с 0.4, мы уже знаем предсказание для i-го объекта: j* = 0. На самом деле в случае бинарной классификации, когда у нас всего 2 класса, зная
, мы уже знаем
. Принято полагать класс 1 за фиктивный, то есть мы будем определять вероятность для класса 1, а вероятность принадлежности к классу 0 подсчитывать после, а не наоборот. То есть задача бинарной классификации сводится к такому виду
То есть мы хотим найти максимальную вероятность того, что при соответствующих ему набору признаков и параметров модели.
Линейная регрессия позволяет предсказать числовое значение для i-го объекта по его набору признаков
Итак, допустим, решение линейной регрессии найдено. Однако каждое предсказание для i-го объекта
представляет собой действительное число, например, 1.1, -0.8 и т.д. Как такое можно отнести к задаче классификации, где ответом должно быть не отрицательное целое число, обозначающее номер класса
? Мы уже знаем, что в задаче классификации ищут не просто число, как в линейной регрессии, а вероятность, которая, к слову, ограничена и принимает значение от 0 до 1 включительно. Значит, нужна такая функция, которая бы преобразовала
подобным образом. Такая функция действительно существует и называется логистической (сигмойдной) функцией
:
Сигмойдная функция имеет интересное свойство - она принимает значения на интервале от 0 до 1 (здесь считается договоренность за асимптотический y=0 при ), а значит, можно получить асимптотически эквивалентный формат вероятности - предсказаниями для классификации будут являться значения
.
Итак, мы уже поняли, что под предсказанием бинарной классификации лежит нахождение вероятности принадлежности объекта к классу 1 (по нему можно также определить принадлежность к классу 0) за счет преобразования сигмойдной функции значений от линейной регрессии . Хорошо, но пока что мы не сделали самого главного, то без чего обучение с учителем не возможно в принципе - мы не определили функцию ошибки. Именно она поможет нам найти оптимальные веса
для
, так чтобы ... что? Это условие и ставит функция ошибки. В задаче классификации принято брать за функцию ошибки MLE (Maximum Likelihood Estimation) - функцию максимального правдоподобия, а точнее её логарифм (зачем логарифм? Ответ будет далее):
По сути MLE показывает совокупную вероятность получить принадлежность к определённому классу
, при параметрах W и если дан ему соответствующий набор признаков
. Конечно, мы хотим иметь такие W, чтобы получить самые вероятные классы
, а значит, в наших интересах максимизировать MLE. В этом ключе целевая функция не является функцией ошибки, а наоборот, MLE показывает насколько мы оказались правы в своих предсказаниях, в отличии от среднеквадратичной ошибки в случае линейной регрессии. То есть с помощью максимизации MLE можно уже решить задачу, однако, часто в вопросе классификации можно увидеть не MLE, а NLL. NLL (Negative Log-Likelihood) - это функция MLE со знаком минус, а следовательно можно свести задачу максимизации MLE к эквивалентной задаче минимизации NLL:
Такую запись можно часто увидеть за счет того, что в методах оптимизации именно минимизация являлась традиционным подходом.
Отлично ... А собственно как найти ? Важно отметить, что в решении задачи бинарной классификации, принято считать все
объектов независимыми и одинаково распределёнными. А именно:
Пусть независимы, тогда
- то есть MLE является произведением вероятностей, а производную от вероятностей явно сложнее считать по сравнению с производной функции асимптотически ей равной, функции логарифма (особенно натурального).
Пусть считаются одинаково распределенными, следовательно,
подчиняются одному закону распределения и используют общие параметры.
Что касаемо пункта 2, какому распределению объекты из выборки подчиняются? В бинарной классификации только два исхода: класс 0 или класс 1. Это отлично вписывается в распределение Бернулли. Напомню, что в распределении Бернулли случайная величина может принимать только два значения: 0 или 1. Обозначим вероятность того, что случайная величина принимает значение 1 за
, тогда, зная
, можно узнать вероятность исхода, когда
равна 0, а именно
. В таком случае закон распределения случайной величины будет выглядеть следующим образом:
, где
. Действительно
Если x=1, то
Если x=0, то
Что-то напоминает? Именно! В нашем случае - это
, а
. Распределение Бернулли имеет единственный параметр
, как было предположено ранее, все объекты из выборки имеют одинаковые параметры распределения.
Теперь перейдем к самой логистической регрессии, а именно соберем всё равнее написанное. За задачу бинарной классификации принимают задачу максимизации логарифма MLE или эквивалентной ей задачу минимизации логарифма NLL относительно весов W:
Используя закон распределения Бернулли и некоторые свойства логарифма, а именно
можно преобразовать выражение функции MLE:
где - истинная метка класса для объекта
,
, то есть
Хотим ли мы решать задачу с MLE или NLL, неважно, так как необходимо так или иначе вычислять градиент функции , потому что подобные задачи обычно решаются итерационными методами с использованием градиента. Используя производную от натурального логарифма, сигмойды и правила производной от сложной функции, приведу вычисление
, с NLL аналогично, только знаки меняются:
Можно сократить множители в дробях, вынести за скобки и раскрыть их. После проделанных операций получим:
Хочется также отметить, что во время реализации логистической регрессии стоит использовать стохастический градиетный спуск, так как он требует меньше итераций по сравнению с обычным. Что касается моего опыта, линейную регрессию я реализовала обычным градиентным спуском, и всё работало достаточно быстро, чего нельзя было сказать об аналогичной попытке запуска логистической реализации.
Хочется также отметить, как мерить предсказания классификации. Существуют разные оценки по типу Recall, Accuracy, Precision, но они достаточно понятно вычисляются по формулам. Что касается, кривой ROC, могут возникнуть недопонимания, особенно если предстоит её реализовать.
Итак, ROC Curve (Receiver Operating Characteristics Curve), она же ROC кривая показывает насколько классификатор чувствителен к доле предсказаний, которые были неверно предсказаны как позитивный класс (FPR).
FPR (False Positive Rate) - доля истинно негативных классов (то есть 0), которые неправильно были предсказаны как 1.
TPR (True Positive Rate) - доля истинно позитивных классов, которые были предсказаны верно.
ROC кривая - это двухмерный график, по абсциссе (ось x) которого лежат значения FPR, а по ординате (оси y) - значения TPR. Важно отметить, что точки такого графика (FPR, TPR) отсортированы по порогам вероятности, и в идеале количество таких порогов должно равняться количеству уникальных вероятностей . Однако в моей реализации я сделала аппроксимацию ROC кривой, то есть вычисляла точки только для ограниченного множества порогов в размере 20 штук (пороги 0, 0.05, 0.1, 0.15, ..., 0.9, 0.95, 1). То есть модель вычисляла набор предсказаний-вероятностей для каждого объекта
. Затем модель с определенным порогом
считала, что при
i-ый объект принадлежал классу 1, иначе классу 0. По полученным предсказаниям считалась точка
. Стоит отметить, что ROC ограничен от 0 до 1 не включая крайние точки (я включила пороги равные 0 и 1, для более точной аппроксимации AUC).
Хорошо, а как понять по ROC кривой, хорошо модель предсказывает или нет? Визуально, чем ближе ROC к прямой y=x, тем хуже - количество ложно-позитивных предсказаний совпадает с истинно-позитивными, то есть шанс угадать 50 на 50 (рандомизированная модель нам не нужна).
В свою очередь, AUC - это просто площадь под ROC кривой, чем лучше модель, тем больше площадь под графиком. Идеальная модель будет иметь AUC = 1, модель с рандомными результатами - AUC=0.5. Вычислить площадь под графиком (опять же аппроксимированно) можно разными численными методами, например, методом трапеций (про него можно свободно прочитать в интернете или в учебнике по численным методам). В итоге моя реализация по вычислению аппроксимированного ROC AUC выглядит следующим образом (get_predict_of_proba() определяет класс 0 или 1 в соответствии с определённым порогом):
def get_roc_auc(y_true: pd.Series, y_pred_proba: np.array, plot=False) -> float: """ Calculates approximate ROC AUC y_true: actual target y_pred: predictied probabilities of the positive class reterns: ROC AUC сurve of the model and a graph """ FPR_dots = [] TPR_dots = [] auc = 0 all_thresholds = [i*0.05 for i in range(0, 21)] for ts in all_thresholds: y_pred = pd.Series(get_pred_of_proba(y_pred_proba, threshold=ts)) TP, FP, TN, FN = TP_FP_TN_FN(y_true, y_pred) if (FP + TN) == 0: FPR = 0.0 else: FPR = FP / (FP + TN) if (TP + FN) == 0: TPR = 0.0 if TP == 0 else 1.0 else: TPR = TP / (TP + FN) FPR_dots.append(FPR) TPR_dots.append(TPR) sorted_points = sorted(zip(FPR_dots, TPR_dots), key=lambda x: x[0]) FPR_dots_sorted = [p[0] for p in sorted_points] TPR_dots_sorted = [p[1] for p in sorted_points] # метод трапеций S = (a+b)*h / 2 for i in range(1, len(FPR_dots_sorted)): h = (FPR_dots_sorted[i] - FPR_dots_sorted[i-1]) a = TPR_dots_sorted[i] b = TPR_dots_sorted[i-1] auc += (a + b) * h / 2 if plot: plt.title("ROC Curve") plt.xlabel("FPR") plt.ylabel("TPR") plt.plot(FPR_dots_sorted, TPR_dots_sorted, color="#ffafc8", label="Model") plt.plot(FPR_dots_sorted, FPR_dots_sorted, color="gray", linestyle="--", label="Random Model") plt.fill_between(FPR_dots_sorted, 0, TPR_dots_sorted, alpha=0.3, color="#ffafc8", label=f"AUC = {auc:.3f}") plt.ylim([0, 1]) plt.xlim([0, 1]) plt.legend() plt.show() return sorted_points, auc
В итоге я получила для своих данных такой график:
В метриках классификации также встречается коэффициент Джини (Gini Coefficient), который можно использовать в случае скошенного распределения классов. Например, в моей задаче класс 0 встречался 5 раз чаше, чем 1. Такая метрика прямо пропорциональна эффективности модели.
Таким образом устроен принцип работы логистической регрессии. Как видно, построение линейной регрессии также может помочь в задаче классификации, что является чуть более сложной задачей относительно вычислений.
Источник


