Логистическая регрессия — градиентный спуск

Модель делает предсказание, предсказание почти всегда неточное, и нужен способ подправить веса так, чтобы промах в следующий раз стал чуть меньше. Этот способ называется градиентным спуском, а конкретный набор формул, по которому он считается для логистической регрессии, — тема этой статьи. Разберём весь путь для одного обучающего примера: от чисел на входе до обновлённых весов, — и проверим каждое число в Python.

Три буквы, которые нужны для старта: z, σ, ŷ

Прежде чем говорить о спуске, нужно на пальцах понимать три величины, из которых строится предсказание логистической регрессии.

z — взвешенная сумма входов. Если вход — вектор чисел x=[x1,x2,…], а модель хранит вес wi для каждого признака и один общий сдвиг b, то:

z=wTx+b=w1x1+w2x2+⋯+b

Это обычное число, которое может быть любым: отрицательным, нулём, огромным. Само по себе оно не годится в качестве вероятности — вероятность должна лежать между 0 и 1.

σ (сигмоида) — функция, которая сжимает любое число в диапазон от 0 до 1:

σ(z)=11+e−z

Большое положительное z даёт σ(z) близкое к 1, большое отрицательное — близкое к 0, а z=0 даёт ровно σ(0)=0.5. Форма графика — плавная буква S, поэтому и название «сигмоида» (σ) (от греческого «сигма», буква S).

yˆ=σ(z) — итоговое предсказание модели, число от 0 до 1, которое читается как уверенность: «на сколько процентов модель считает, что ответ — 1». В формулах ниже это же число часто обозначают буквой a (от «activation», результат активации) — это одно и то же число, просто a — привычное обозначение внутри вычислений, а yˆ — обозначение снаружи, когда говорим про итоговый ответ модели.

Если можешь пересказать: z — взвешенная сумма, любое число; σ сжимает его в 0..1; yˆ=a=σ(z) — итоговая уверенность модели, — двигаемся дальше.

Один полный шаг спуска: от предсказания до нового веса

Возьмём один обучающий пример: вход x=[0.5,0.3,0.9] (три признака), правильный ответ y=1. Стартовые веса модели — ещё не обученные, просто взяты для примера: w=[1.2,−0.4,2.0], b=−0.9. Скорость обучения (α) — насколько сильно двигать веса за один шаг — α=0.1.

Шаг градиентного спуска для одного примера состоит из четырёх частей: прямой ход, сигнал ошибки, градиенты, обновление весов. Разберём каждую по очереди, считая всё число за числом.

Часть 1 — прямой ход. Считаем z, потом a=σ(z), потом штраф за ошибку (loss) L:

z=wTx+b=1.2⋅0.5+(−0.4)⋅0.3+2.0⋅0.9−0.9=0.6−0.12+1.8−0.9=1.38

a=σ(1.38)=11+e−1.38≈0.799

Loss (L) для логистической регрессии — кросс-энтропия. При y=1 она упрощается до L=−log⁡a (полный вывод формулы и почему она выбрана именно такой — в отдельной статье про вывод dL/dz, здесь достаточно знать, что это способ штрафовать модель за неуверенность в правильном ответе):

L=−log⁡(0.799)≈0.2244

Часть 2 — сигнал ошибки (dz). Обратный ход начинается с одной короткой формулы:

dz=a−y

Здесь dz — это сокращённая запись производной L по z: число, которое говорит, в какую сторону и насколько сильно нужно подвинуть z, чтобы уменьшить ошибку. Формула получается из цепного правила (детально — в статье «Вывод dL/dz»), а здесь мы просто её используем: предсказание минус правильный ответ.

dz=0.799−1=−0.201

Отрицательный знак означает: модель дала меньше уверенности, чем нужно было (y=1, а a пока только 0.799), поэтому z нужно увеличить.

Часть 3 — градиенты по весам. Из dz получаются градиенты — насколько нужно двинуть каждый вес и сдвиг:

dw=x⋅dz,db=dz

Смысл в словах: каждый вес wi отвечает за свой признак xi, и вклад этого признака в общую ошибку — просто xi, умноженный на общий сигнал ошибки (dz) dz. Признак с большим значением (0.9 у нас) тянет за собой больший градиент, чем признак с маленьким значением (0.3), — при одном и том же сигнале ошибки (dz). А сдвиг b ни на что не умножается, у него нет своего признака, поэтому его градиент — это просто сам dz.

dw=[0.5,0.3,0.9]⋅(−0.201)=[−0.1005,−0.0603,−0.1809]

db=−0.201

Часть 4 — обновление весов. Веса двигаются против градиента, на шаг размера α:

w:=w−αdw,b:=b−αdb

Минус — потому что градиент указывает направление роста ошибки, а нам нужно её уменьшать: значит, идём в противоположную сторону.

w:=[1.2,−0.4,2.0]−0.1⋅[−0.1005,−0.0603,−0.1809]=[1.2101,−0.394,2.0181]

b:=−0.9−0.1⋅(−0.201)=−0.8799

Проверим, что шаг подействовал: если пересчитать z с новыми весами, получится z≈1.4232, a≈0.8058 — уверенность подросла с 0.799 до 0.8058, ровно в ту сторону, куда мы и хотели её подвинуть.

Если можешь пересказать: прямой ход даёт a и L; dz=a−y — сигнал ошибки; dw=x⋅dz, db=dz — вклад каждого веса; w:=w−αdw — шаг против градиента, — листай дальше.

Что означает знак и размер dz

Прежде чем читать дальше: если бы правильный ответ был y=0 (а не 1), а предсказание осталось тем же a=0.799, каким был бы знак dz (сигнал ошибки) — положительным или отрицательным? Прикинь по формуле dz=a−y, а потом проверь.

Ответ

Положительным: dz=0.799−0=0.799. Знак dz (сигнал ошибки) всегда показывает, в какую сторону промахнулась модель: отрицательный — недооценила (предсказание меньше нужного), положительный — переоценила.

Размер |dz| тоже несёт смысл: чем ближе предсказание a к правильному ответу y, тем меньше |dz|, и тем меньше шаг у весов. У идеального предсказания (a=y точно) dz=0 — и веса вовсе не двигаются, потому что двигать их уже не нужно.

Два противоречивых примера: чей сигнал сильнее

Что если в наборе не один пример, а два, с одним и тем же входом x=[0.5,0.3,0.9], но противоречивыми ответами: у одного y=1, у другого y=0?

Представь двух людей, которые тянут одно одеяло в разные стороны: кто сильнее не прав в этот момент, тот и тянет сильнее. При тех же стартовых весах w=[1.2,−0.4,2.0], b=−0.9 предсказание для обоих одинаковое, yˆ=0.799 (это тот же z=1.38, что и в основном примере выше), но сигналы ошибки противоположны. Для первого примера: dz1=0.799−1=−0.201 (веса нужно поднять). Для второго: dz2=0.799−0=0.799 (веса нужно опустить).

Угадай сначала: во сколько раз второй пример тянет «одеяло» сильнее первого — в 2 раза, в 4 раза или в 10 раз? Прикинь по числам dz1 и dz2 выше, а потом проверь.

Ответ

Примерно в 4 раза: dz2/|dz1|=0.799/0.201≈3.97.

Два значения dz как противоположно направленные полосы: dz1=−0.201 (пример 1, y=1) короткая влево, dz2=+0.799 (пример 2, y=0) почти в 4 раза длиннее и вправо

Твоя очередь: что произойдёт с dz2, если правильный ответ y для второго примера перевернётся с 0 на 1 (то есть оба примера станут согласны, y=1)? Посчитай — формула та же, dz=a−y, только y теперь другое.

Ответ

dz2=0.799−1=−0.201 — то есть станет точно равен dz1: оба примера теперь согласны и тянут в одну сторону с одинаковой силой.

Когда примеров несколько, спуск не использует градиент одного из них — он усредняет градиенты всех. Для двух примеров выше усреднённый градиент по весам получается dwavg=[0.1495,0.0897,0.2691], dbavg=0.299: спуск идёт в сторону, заданную вторым примером, потому что его ошибка почти вчетверо сильнее. Противоречивые примеры не «побеждают» друг друга поровну, а тянут спуск пропорционально тому, насколько каждый из них неправ. (Как это усреднение выглядит для целого набора из многих примеров, а не только двух, — тема статьи «Градиентный спуск на m примерах».)

Если можешь пересказать: два примера с общим входом, но разными y, тянут веса в разные стороны с силой, пропорциональной своему |dz|; усреднённый градиент идёт за тем, чей сигнал сильнее, — листай дальше.

Мини-квиз: собери шаг по порядку

Возьми свои числа: x=[1.0,0.2], y=0, w=[0.5,−0.3], b=0.1, α=0.1. Посчитай по порядку: z, затем a=σ(z), затем dz=a−y, затем dw и db, затем новые w и b.

Ответ

z=0.5⋅1.0+(−0.3)⋅0.2+0.1=0.5−0.06+0.1=0.54

a=σ(0.54)≈0.6318

dz=0.6318−0=0.6318 (модель переоценила: правильный ответ 0, а уверенность за него высокая)

dw=[1.0⋅0.6318,0.2⋅0.6318]=[0.6318,0.1264], db=0.6318

w:=[0.5,−0.3]−0.1⋅[0.6318,0.1264]=[0.4368,−0.3126]

b:=0.1−0.1⋅0.6318=0.0368

Что дальше

Один шаг спуска для логистической регрессии — это четыре считаемые части: прямой ход даёт предсказание a, сигнал ошибки (dz) dz=a−y говорит, куда промахнулись, градиенты dw=x⋅dz и db=dz раскладывают этот промах по весам, и обновление w:=w−αdw двигает веса против ошибки. Повторённый много раз на многих примерах, этот шаг и есть всё обучение логистической регрессии.

Формула dz=a−y выглядит как трюк — слишком короткая для того, что она делает. На самом деле это результат честного вывода по цепному правилу, и он показывает, почему кросс-энтропия так хорошо сочетается с сигмоидой. Полный вывод — в статье «Вывод dL/dz».

Если можешь пересказать: прямой ход → dz=a−y → dw,db → обновление весов — это и есть один шаг обучения, — тема усвоена.