Машинное обучение
Логистическая регрессия
От кривых роста населения к логиту
S-образная кривая, лежащая в основе логистической регрессии, родилась в демографии, а не в машинном обучении. В 1838 году бельгийский математик Пьер Франсуа Ферхюльст ввёл логистическую функцию, чтобы описать, как население растёт сначала быстро, а затем выходит на плато из-за ограниченных ресурсов. Спустя век, в 1944 году, врач и статистик Джозеф Берксон ввёл термин «логит» и отстаивал применение логистической модели в биопробах против сторонников пробита. А в 1958 году британский статистик Дэвид Кокс формализовал логистическую регрессию как общий инструмент моделирования бинарных исходов - именно этот метод сегодня применяют для классификации в медицине, финансах и не только.
Каждый раз, когда Gmail отправляет письмо в спам, банк блокирует подозрительную транзакцию или врач получает от AI предварительный диагноз по анализу крови - за этим стоит алгоритм, который принимает решение: да или нет, болен или здоров, мошенничество или нет. Но как превратить набор чисел (признаков пациента, параметров транзакции) в чёткий ответ «класс 0» или «класс 1»? И почему алгоритм с обманчивым названием «регрессия» на самом деле решает задачу классификации?
- **Медицинская диагностика:** логистическая регрессия используется для предварительного скрининга заболеваний по анализам крови, маммографии и симптомам - модель выдаёт вероятность заболевания, а врач выбирает порог для направления на дополнительные обследования
- **Кредитный скоринг:** банки оценивают вероятность невозврата кредита на основе десятков признаков (доход, кредитная история, возраст) - логистическая регрессия популярна именно потому, что её решения легко объяснить регулятору и клиенту
- **Рекомендательные системы:** когда Spotify решает, показать ли вам плейлист, а Amazon - предложить ли товар, они оценивают вероятность клика (click-through rate) через softmax по сотням признаков пользователя и контента
Предварительные знания
Sigmoid-функция: из числа в вероятность
В уроке о линейной регрессии мы научились предсказывать **непрерывные числа**: цену дома, температуру, зарплату. Но что если задача другая - определить, **спам** письмо или нет? **Болен** пациент или здоров? **Одобрить** кредит или отказать? Ответ здесь не число, а **класс**: 0 или 1, да или нет. Это задача **классификации**, и линейная регрессия с ней не справляется напрямую.
Проблема в том, что линейная регрессия выдаёт числа от минус бесконечности до плюс бесконечности: -3.7, 0.2, 158.4 - что угодно. А нам нужна **вероятность** от 0 до 1: «с вероятностью 0.92 это спам». Нужна функция, которая «сжимает» любое число в интервал [0, 1]. Такая функция существует - и называется **sigmoid** (сигмоида).
Идея логистической регрессии проста: берём линейную комбинацию **z = w*x + b** (как в линейной регрессии) и пропускаем её через sigmoid. Результат - вероятность принадлежности к классу 1. Если у нас несколько признаков: **z = w1*x1 + w2*x2 + ... + wn*xn + b**. Веса w и смещение b - параметры, которые модель подбирает при обучении.
**Почему нельзя использовать MSE для логистической регрессии?** MSE (Mean Squared Error) прекрасно работает для линейной регрессии, но для sigmoid создаёт **неконвексную** функцию потерь - с множеством локальных минимумов, в которых градиентный спуск застревает. Поэтому используют **Binary Cross-Entropy**: Loss = -[y * log(p) + (1 - y) * log(1 - p)]. Если y=1, штрафуем за малое p; если y=0, штрафуем за большое p. Эта функция **конвексная** - у неё ровно один минимум, и градиентный спуск гарантированно его находит.
Зачем в логистической регрессии линейную комбинацию z = w*x + b пропускают через sigmoid-функцию?
Decision Boundary: граница решения
Sigmoid выдаёт вероятность, но конечное решение - бинарное: спам или не спам, одобрить или отказать. Нужен **порог (threshold)**: если вероятность >= 0.5, предсказываем класс 1, иначе - класс 0. Линия (или поверхность), где вероятность ровно 0.5, называется **decision boundary** - граница решения. Всё по одну сторону от неё - класс 1, по другую - класс 0.
Когда вероятность p = 0.5, sigmoid(z) = 0.5, а это значит z = 0. То есть decision boundary - это множество точек, где **w*x + b = 0**. Для двух признаков: w1*x1 + w2*x2 + b = 0 - это уравнение **прямой** на плоскости. Для трёх признаков - плоскость в 3D-пространстве. Decision boundary логистической регрессии всегда **линейный** - это и сила, и ограничение алгоритма.
**Расстояние от boundary = уверенность модели.** Точка далеко справа от boundary: z = +5, p = 0.993 - модель на 99.3% уверена, что это класс 1. Точка рядом с boundary: z = +0.1, p = 0.525 - модель почти не уверена (52.5%), предсказание ненадёжное. Именно поэтому в критичных задачах (медицина, финансы) смотрят не просто на предсказанный класс, а на **вероятность** - степень уверенности модели.
Порог 0.5 - не единственный вариант. Его можно менять в зависимости от задачи. В медицинской диагностике рака пропустить больного (false negative) страшнее, чем ложная тревога (false positive). Поэтому снижают порог до **0.3**: модель чаще будет говорить «болен», увеличивая **recall** (полноту) за счёт некоторого падения **precision** (точности). Для спам-фильтра наоборот: лучше пропустить спам, чем отправить важное письмо в корзину - порог повышают до **0.7** или выше.
**Ограничение: линейный decision boundary.** Логистическая регрессия может разделить классы только прямой линией (или гиперплоскостью). Если данные разделяются нелинейно - например, один класс внутри кольца, а другой снаружи - логистическая регрессия не справится. Для таких случаев нужны нелинейные методы: SVM с ядрами, деревья решений или нейросети.
Врач использует логистическую регрессию для диагностики серьёзного заболевания. Что лучше сделать с порогом (threshold)?
Мультиклассовая классификация
До сих пор мы разделяли данные на **два** класса: спам/не спам, больной/здоровый. Но в реальности классов часто больше: распознать цифру от 0 до 9 (10 классов), определить язык текста (200+ языков), классифицировать тип опухоли (десятки вариантов). Как адаптировать бинарный классификатор к **мультиклассовой** задаче?
Первый подход - **One-vs-Rest (OvR)**, также известный как One-vs-All. Для N классов обучаем **N бинарных классификаторов**: каждый отделяет один конкретный класс от всех остальных. Для распознавания цифр: классификатор #0 отвечает на вопрос «это 0 или не 0?», классификатор #1 - «это 1 или не 1?», и так далее. При предсказании запускаем все N классификаторов и выбираем класс с наибольшей вероятностью.
Второй подход - **One-vs-One (OvO)**. Обучаем классификатор для **каждой пары** классов: «0 vs 1», «0 vs 2», «1 vs 2». Для N классов это **N*(N-1)/2** классификаторов. Для 10 цифр: 10*9/2 = 45 классификаторов. Звучит как много, но каждый обучается на меньшем объёме данных (только на примерах двух классов), что может быть быстрее. При предсказании каждый классификатор «голосует» за один из двух своих классов, и побеждает класс с наибольшим числом голосов.
**OvR vs OvO: когда что выбирать?** - **OvR** - быстрее (N классификаторов вместо N*(N-1)/2), проще, хорошо работает для большинства задач. Это дефолтный выбор в sklearn. - **OvO** - может быть точнее, когда классы сильно несбалансированы или границы между ними сложные. Каждый классификатор видит только 2 класса, что упрощает задачу. - **На практике:** sklearn для LogisticRegression по умолчанию использует стратегию 'ovr', но можно переключить на 'multinomial' (softmax) - об этом в следующем концепте.
Вы обучаете логистическую регрессию распознавать 10 видов животных на фото. В стратегии OvO (One-vs-One) сколько бинарных классификаторов нужно обучить?
Softmax: вероятности для N классов
OvR и OvO - это «обёртки» вокруг бинарных классификаторов. Но существует элегантное решение, которое **напрямую** моделирует вероятности N классов одновременно - функция **softmax**. Вместо N отдельных sigmoid-ов, softmax берёт N чисел (по одному для каждого класса) и превращает их в **распределение вероятностей**: все значения от 0 до 1, и сумма ровно 1.0.
Softmax - это **обобщение sigmoid** для N > 2 классов. Для двух классов softmax математически эквивалентна sigmoid: если подставить N=2, формулы упрощаются до sigma(z) = 1 / (1 + e^(-z)). Это не совпадение - sigmoid является частным случаем softmax. Поэтому когда sklearn использует multi_class='multinomial', он применяет softmax вместо N отдельных sigmoid-ов.
**Temperature scaling на практике:** - **T = 1.0** - стандартный softmax, используется при обучении - **T < 1.0** - делает распределение более «острым»: модель уверенно выбирает один класс. Используется при inference в продакшене. - **T > 1.0** - делает распределение «мягким»: вероятности ближе к равномерному. Используется в **knowledge distillation** (передача знаний от большой модели к маленькой) и при генерации текста в LLM (temperature в ChatGPT). Когда вы ставите «temperature = 0.2» в ChatGPT - вы буквально меняете T в softmax, делая модель более предсказуемой.
Для softmax используют **Cross-Entropy Loss** - обобщение Binary Cross-Entropy на N классов: Loss = -sum(y_i * log(p_i)) по всем классам. Поскольку y - one-hot вектор (только один элемент равен 1), на практике это сводится к -log(p) для правильного класса. Если модель предсказывает правильный класс с вероятностью 0.95 - штраф маленький (-log(0.95) = 0.05). Если с вероятностью 0.01 - штраф огромный (-log(0.01) = 4.6).
**Численная нестабильность softmax.** Вычисление e^z при больших z приводит к переполнению (overflow): e^1000 = бесконечность. Решение - вычитать max(z) из всех logits перед экспоненцированием: softmax(z - max(z)) дает тот же результат, но без переполнения. Все библиотеки (PyTorch, TensorFlow, numpy) делают это автоматически, но при ручной реализации об этом нужно помнить.
Логистическая регрессия - это разновидность регрессии, а не классификации, потому что в названии есть слово «регрессия»
Несмотря на название, логистическая регрессия - это алгоритм классификации, который предсказывает вероятность принадлежности к классу, а не непрерывное число
Название «регрессия» историческое: алгоритм «регрессирует» (подбирает) параметры линейной функции. Но sigmoid превращает выход в вероятность, а порог (threshold) - в бинарное решение. Результат - класс, а не число, что делает это классификацией. Аналогично, softmax обобщает подход на N классов - это тоже классификация, хотя внутри работает линейная модель.
Softmax от logits [3.0, 1.0, 1.0] даёт вероятности [0.78, 0.11, 0.11]. Что произойдёт при temperature T = 0.1?
Ключевые идеи
- **Sigmoid** превращает линейную комбинацию z = w*x + b в вероятность от 0 до 1: sigma(z) = 1 / (1 + e^(-z)), обучается через Binary Cross-Entropy
- **Decision boundary** - линия, где p = 0.5 (z = 0), разделяющая классы; порог можно сдвигать для баланса precision/recall в зависимости от цены ошибок
- **Мультиклассовая классификация:** OvR обучает N бинарных классификаторов (линейная сложность), OvO обучает N*(N-1)/2 (квадратичная), выбор зависит от задачи
- **Softmax** обобщает sigmoid на N классов, нормализуя logits в распределение вероятностей с суммой 1.0; temperature scaling управляет уверенностью - именно это стоит за тем, как банк решает заблокировать транзакцию, а Gmail - отправить письмо в спам, как мы обсуждали в начале
Связанные темы
Логистическая регрессия - мост между линейными методами и более сложными алгоритмами классификации. Вот куда двигаться дальше:
- Линейная регрессия — Логистическая регрессия использует ту же линейную комбинацию w*x + b, но добавляет sigmoid для классификации. Понимание линейной регрессии - фундамент для понимания логистической.
- Метрики оценки моделей — Precision, recall, F1-score, ROC-AUC - метрики, без которых невозможно выбрать правильный threshold для decision boundary и оценить качество классификатора
- Деревья решений — В отличие от логистической регрессии, деревья создают нелинейные decision boundaries, разделяя пространство на прямоугольные регионы - следующий шаг в классификации
- SVM (метод опорных векторов) — SVM тоже строит линейную границу, но максимизирует зазор (margin) между классами, а с kernel trick решает нелинейные задачи, которые логистической регрессии не под силу
Вопросы для размышления
- Логистическая регрессия создает линейный decision boundary. Приведите пример реальной задачи, где линейная граница будет работать хорошо, и пример, где она принципиально не справится.
- В медицинском скрининге мы снижаем threshold до 0.3 для повышения recall. Но что если ложных срабатываний станет слишком много - перегруженные врачи начнут игнорировать предупреждения? Где баланс между безопасностью и практичностью?
- Softmax с temperature = 0 дал бы вероятность 1.0 для класса с максимальным logit и 0.0 для остальных (argmax). В каких задачах это полезно, а в каких опасно терять информацию о неуверенности модели?
Связанные уроки
- ml-06-linear-regression — Logistic regression - линейная модель с нелинейной активацией
- ml-05-evaluation — Precision/recall/AUC - ключевые метрики для бинарной классификации
- ml-09-gradient-descent — Cross-entropy минимизируется через градиентный спуск
- stat-38-logistic-regression
- prob-11-normal