Логистическая и softmax-регрессия
Как линейная модель превращается в классификатор — сигмоида, бинарная cross-entropy, граница решения и softmax-регрессия, которая в точности совпадает с выходным слоем LLM.
Зачем это нужно
Заголовок раздела «Зачем это нужно»В мире LLM классификация повсюду. Перед претрейном (предобучением из урока 2.1.1) классификатор решает, математическая ли страница из интернета и стоит ли её брать в корпус. модель вознаграждениямодель вознагражденияEN reward model в RLHF (обучении с подкреплением по оценкам людей) учится предсказывать, какой из двух ответов человек сочтёт лучшим. А сама генерация текста — это классификация на 150 тысяч классов: какой токен будет следующим.
В прошлом уроке линейная модель предсказывала число. Теперь нам нужна вероятность класса. Рецепт простой: берём ту же , пропускаем через сигмоиду и получаем логистическую регрессиюлогистическая регрессияEN logistic regressionНесмотря на название, это метод классификации. У Чжоу Чжихуа («арбузная книга») — «регрессия логарифма шансов», у Ли Хана («Методы статистического обучения») — фонетическая запись английского слова logistic.. Через softmax — softmax-регрессию для многих классов. Это однослойная нейросеть, и она в точности совпадает с последним слоем любой языковой модели.
Слово «log» в этом уроке. Здесь и дальше — натуральный логарифм с основанием
(урок 1.0.2). Так принято в ML-статьях; в Python это np.log.
Почему не взять линейную регрессию
Заголовок раздела «Почему не взять линейную регрессию»Можно обучить линейную регрессию на метках 0 и 1 и резать по порогу 0.5. Но выходы вида 1.7 или — не вероятности. Хуже другое: MSE штрафует даже «слишком правильные» ответы. Очевидно математическая страница с прогнозом 3.0 вместо 1 даёт ошибку и тянет прямую к себе. Граница решения сдвигается из-за примеров, с которыми и так всё ясно. Нужна функция, которая сжимает любое число в интервал .
Сигмоида: из числа — вероятность
Заголовок раздела «Сигмоида: из числа — вероятность»Сигмоидасигмоида (логистическая функция)EN sigmoid (logistic function)σ(z) = 1 / (1 + e^(−z)) — превращает любое число в вероятность от 0 до 1. — именно такая функция. Вы встречали её в уроках 1.0.1 и 1.2.1:
Почему результат всегда между 0 и 1: — положительное число, поэтому знаменатель больше единицы, а дробь меньше единицы. При большом экспонента почти ноль, и ; при большом отрицательном экспонента огромна, и . Пример: .
| 0 | 1 | 2 | 4 | ||||
|---|---|---|---|---|---|---|---|
| 0.018 | 0.119 | 0.269 | 0.5 | 0.731 | 0.881 | 0.982 |
Свойства, которые понадобятся:
- и — кривая симметрична;
- — производная выражается через само значение (урок 1.2.1). Максимум производной — в нуле; при она почти ноль: сигмоида насыщается.
Модель логистической регрессии:
Логит и шансы
Заголовок раздела «Логит и шансы»Обратим сигмоиду. Если , то
Отношение — это шансы (odds), как у букмекеров: при шансы , «4 к 1». Значит, — логарифм шансов, или логитлогитEN logitСтрого — логарифм шансов log(p / (1 − p)). В нейросетях логитами называют любые выходы до softmax или сигмоиды.. Проверка на числах: , и — вернулись к исходной вероятности. Логистическая регрессия утверждает простую вещь: логарифм шансов линейно зависит от признаков.
Отсюда понятный смысл весов. Рост признака на единицу прибавляет к логиту, то есть умножает шансы в раз: прибавить к логарифму — значит умножить само число на (урок 1.0.2).
Пример: фильтр математических страниц. Пусть у страницы два признака: — доля цифр и формульных символов, — сколько раз встретились слова «теорема», «доказательство», «уравнение». Допустим, модель обучилась так:
- Страница A: , . Тогда и .
- Страница B: , . Тогда и .
- Каждое ключевое слово умножает шансы в раза. Проверим на странице A: её шансы . Добавим ещё одно ключевое слово: , , шансы — ровно в 4.5 раза больше.
Функция потерь: бинарная cross-entropy
Заголовок раздела «Функция потерь: бинарная cross-entropy»Метка — это бросок монетки с вероятностью , то есть распределение Бернуллираспределение БернуллиEN Bernoulli distributionВеличина принимает 1 с вероятностью p и 0 с вероятностью 1 − p. Пример — награда «верно/неверно». (урок 1.3.1). Его записывают одной формулой: . При остаётся , при — . Дальше — метод максимального правдоподобия из урока 1.3.2: берём логарифм, ставим минус, усредняем по примерам и получаем бинарную cross-entropy (BCE)бинарная перекрёстная энтропия (BCE)EN binary cross-entropy (BCE), log loss:
Это обычная перекрёстная энтропияперекрёстная энтропияEN cross-entropy для двух классов. Для каждого примера работает только одно слагаемое: , если , и , если : второе слагаемое умножается на и пропадает.
Пример на двух страницах. Первая математическая (), модель дала : потери . Вторая обычная (), модель дала : потери . Средняя BCE — .
| Модель дала | Loss, если | Loss, если |
|---|---|---|
| 0.99 | 0.01 | 4.61 |
| 0.9 | 0.105 | 2.30 |
| 0.5 | 0.693 | 0.693 |
| 0.1 | 2.30 | 0.105 |
| 0.01 | 4.61 | 0.01 |
Уверенная ошибка стоит очень дорого: при и потери уходят в бесконечность.
Градиент: та же форма, что у линейной регрессии
Заголовок раздела «Градиент: та же форма, что у линейной регрессии»Продифференцируем потери одного примера по логиту . Цепное правило (урок 1.2.2): производная loss по , умноженная на производную по , а последняя для сигмоиды равна :
Всё сократилось до «предсказание минус правда». Например, модель дала при : градиент , логит нужно чуть-чуть поднять. При градиент был бы : логит надо сильно опустить. Дальше , поэтому
Сравните с линейной регрессией: там было . Та же схема — признаки, взвешенные ошибками. Это не совпадение. (Для любознательных.) В обоих случаях loss — отрицательное лог-правдоподобие «своего» распределения: гауссовского для чисел, Бернулли для меток 0/1. А выходная функция (тождественная или сигмоида) подобрана под это распределение. Такие согласованные пары «выход + loss» дают градиент вида «ошибка × признаки». В статистике всё это семейство называют обобщёнными линейными моделями (GLM).
Почему не MSE
Заголовок раздела «Почему не MSE»Возьмём пример с , где модель уверенно ошиблась: , .
- BCE: . Сильный сигнал: «срочно исправляйся».
- MSE поверх сигмоиды: . В 50 раз слабее.
Множитель гасит градиент ровно тогда, когда модель ошибается сильнее всего. Вдобавок MSE поверх сигмоиды невыпукла по , а BCE выпуклавыпуклая функцияEN convex function«Чаша» без ложных ям — любой локальный минимум глобальный. Отрезок между двумя точками графика лежит не ниже графика. (урок 1.4.1): ложных локальных минимумов у неё нет.
Устойчивая реализация
Заголовок раздела «Устойчивая реализация»Наивный код -np.log(sigmoid(z)) ломается при : сигмоида округляется до 0 (компьютер хранит дробные
числа с ограниченной точностью, урок 0.0.2), и .
Как и в уроке 1.3.2, считаем потери сразу по логиту.
Главное, что нужно унести из этого раздела, — итоговая формула (она стоит отдельной строкой после трёх шагов) и правило в конце раздела. Сам вывод за три шага — для любознательных.
- и .
- Подставляем в BCE и пользуемся тем, что : .
- Осталось устойчиво посчитать . При экспонента и так не больше единицы. При выносим за скобку: . Обе ветки записываются одной формулой:
Экспонента берётся только от , переполнения нет. Проверка: при и loss
равен — огромный, но конечный штраф за уверенную ошибку. В PyTorch это
F.binary_cross_entropy_with_logits (или nn.BCEWithLogitsLoss). Правило то же, что для softmax: в loss
передают логиты, а не вероятности.
Граница решения
Заголовок раздела «Граница решения»Класс 1 предсказываем, если . Поскольку , условие превращается в
Множество точек, где , — граница решенияграница решенияEN decision boundary. В двумерном случае это прямая, в трёхмерном — плоскость, в -мерном — гиперплоскость (так называют такую же «ровную перегородку» в пространстве любой размерности). В примере с фильтром граница — прямая . Страница совсем без цифр () пройдёт фильтр, только если в ней хотя бы 3 ключевых слова: при двух , при трёх .
Отсюда три важных следствия.
- Логистическая регрессия — линейный классификатор. Если классы нельзя разделить прямой, она бессильна. Классический пример — XOR: класс 1, если ровно один из двух признаков равен 1. Всего четыре точки: и — класс 0, и — класс 1. Нарисуйте их на листке: классы стоят «крест-накрест», и никакая прямая не оставит обе единицы с одной стороны, а оба нуля — с другой. Вот почему. Пусть прямая оставила точки и по одну сторону. Тогда там же и середина отрезка между ними, . Но это и середина отрезка между и , так что хотя бы одна из этих точек тоже окажется на той же стороне. Нужны скрытые слои с нелинейностью — это урок 3.1.1.
- Норма весов — это уверенность. Умножьте и на 10: граница не сдвинется, но сигмоида станет почти ступенькой. На рисунке пунктиры и прижмутся к границе в 10 раз ближе.
- На линейно разделимых данных веса растут бесконечно. Loss можно уменьшать вечно, увеличивая . Спасают регуляризациярегуляризацияEN regularization (L2) и ранняя остановка из урока 2.1.2.
Порог 0.5 — не закон природы. Для фильтра, который не должен пропускать мусор, порог поднимают — например, до 0.8. Как выбирать порог и чем мерить результат — в следующем уроке.
Много классов: softmax-регрессия
Заголовок раздела «Много классов: softmax-регрессия»Теперь классов , например «код», «математика», «проза». Заводим по линейной модели на каждый класс: каждая выдаёт свой логит — «балл» своего класса. Веса всех моделей складываем в матрицу (строка на класс), сдвиги — в вектор . Логиты превращаем в вероятности через softmax из урока 1.3.2: — каждую экспоненту делим на их сумму, и вероятности в сумме дают 1. Потери — минус логарифм вероятности правильного класса :
Это softmax-регрессияsoftmax-регрессия (многоклассовая логистическая регрессия)EN softmax regression (multinomial logistic regression). Градиент по логитам вы уже выводили в уроке 1.3.2. Это снова «предсказание минус правда», только теперь правда — one-hot вектор:
Здесь — one-hot векторone-hot кодирование (унитарный код)EN one-hot encodingВектор длины V из нулей с единицей в позиции номера токена. Все one-hot векторы попарно ортогональны.: единица на месте правильного класса, нули на остальных местах. Градиент по — внешнее произведение (урок 1.1.2): матрица , такая же, как . Для батча из примеров градиенты усредняют, как в бинарном случае.
На числах: логиты , правильный класс — второй. Softmax даёт , loss . Градиент: . Логит правильного класса тянем вверх, остальные — вниз, и тем сильнее, чем больше вероятность они «украли».
Softmax или сигмоида на каждый класс? Softmax подходит, когда правильный класс ровно один. Вероятности конкурируют: если одна растёт, остальные падают, а в сумме всегда 1. Но страница может быть одновременно и кодом, и математикой. Когда классы не исключают друг друга (multi-label), берут независимых сигмоид и BCE на каждом выходе. Получается отдельных логистических регрессий. Например, логиты через сигмоиды дают : «код — да, математика — да, проза — нет». Softmax от тех же логитов дал бы и заставил бы классы делить единицу. Этот выбор вы встретите в китайском углу ниже.
Однослойная нейросеть — и выходной слой LLM
Заголовок раздела «Однослойная нейросеть — и выходной слой LLM»flowchart LR X["Признаки x<br/>(d чисел)"] --> L["Linear: z = Wx + b<br/>W: K × d"] L --> S["softmax"] S --> P["p₁ … p_K"] P --> CE["Loss = −log p_y"] H["Скрытое состояние h<br/>трансформера"] -.->|"в LLM вместо x"| L
Softmax-регрессия — это нейронная сетьнейронная сетьEN neural network из одного линейного слоя без скрытых слоёв. Теперь посмотрите на LLM. Трансформер превращает контекст в вектор размерности . Последний слой — LM headLM-голова (языковая голова)EN LM head (language modeling head)Последний линейный слой GPT: превращает вектор ширины d в V логитов, по одному на токен словаря. Часто делит матрицу с эмбеддингами (weight tying). — считает : логиты по всему словарю, а дальше softmax и cross-entropy.
Это softmax-регрессия с классами (по классу на каждый токен словаря). Её признаки не заданы вручную, а обучаются вместе с ней. Возьмём размеры Qwen3-8B: длина вектора — чисел, словарь токенов. Одна матрица размера содержит млн параметров.
Ещё два места, где вы встретите эту конструкцию:
- Биграммная модель из урока 4.1.2 — это softmax-регрессия, у которой признак — one-hot вектор предыдущего токена.
- Линейный зонд (linear probe). Берут скрытые состояния замороженной LLM и учат на них логистическую регрессию. Если зонд справляется, нужная информация линейно закодирована внутри модели. Это базовый инструмент интерпретируемости (его возможности и ловушки — в уроке 9.3.1).
Практика
Заголовок раздела «Практика»Реализуйте логистическую регрессию на NumPy: устойчивую сигмоиду, BCE по логитам, градиенты, обучение и
предсказание с порогом. Тесты проверят устойчивость на и accuracy ≥ 95% на двух «облаках» точек.
Структура та же, что в задании прошлого урока: fit_logreg почти дословно повторяет fit_gd, меняются только
градиенты и функция потерь.
import numpy as np
def sigmoid(z):
"""Сигмоида σ(z) = 1 / (1 + e^(−z)) поэлементно для массива z.
Должна работать без переполнения и nan даже для z = ±1000.
Подсказка: для z ≥ 0 берите 1 / (1 + e^(−z)), для z < 0 — e^z / (1 + e^z).
Осторожно: np.where вычисляет ОБЕ ветки для всех элементов, поэтому
np.exp(-z) и np.exp(z) по отдельности всё равно переполнятся. Подумайте,
как обойтись одной экспонентой от −|z| (она всегда не больше 1).
"""
# TODO
...
def bce_with_logits(z, y):
"""Средняя бинарная cross-entropy, посчитанная прямо по логитам z (без вызова sigmoid).
Для одного примера: −[y·log σ(z) + (1 − y)·log(1 − σ(z))].
Устойчивая форма той же величины: max(z, 0) − y·z + log(1 + e^(−|z|)).
Возвращает число (float) — среднее по всем примерам.
"""
# TODO
...
def gradients(X, y, w, b):
"""Градиенты средней BCE по w и b для логистической регрессии.
p = σ(X·w + b); dL/dw = Xᵀ(p − y) / n; dL/db = mean(p − y).
Возвращает (dw, db).
"""
# TODO
...
def fit_logreg(X, y, lr=0.5, steps=500):
"""Обучает логистическую регрессию градиентным спуском из нулей.
Возвращает (w, b, history), где history — список средних BCE после каждого шага.
"""
# TODO
...
def predict(X, w, b, threshold=0.5):
"""Метки классов 0/1: 1, если σ(X·w + b) ≥ threshold. Возвращает массив int."""
# TODO
...
# Проверьте себя кнопкой «Запустить»:
print(sigmoid(np.array([-2.0, 0.0, 2.0]))) # ≈ [0.1192 0.5 0.8808]
print(bce_with_logits(np.array([0.0, 0.0]), np.array([1.0, 0.0]))) # ln 2 ≈ 0.6931
Softmax-регрессия на PyTorch (локально)
Заголовок раздела «Softmax-регрессия на PyTorch (локально)»Классифицируем страницы на три класса по двум признакам. Признаки — доля «кодовых» символов и доля цифр и формул:
import torchimport torch.nn.functional as F
torch.manual_seed(0)# Синтетика: 2 признака страницы — доля «кодовых» символов ({}();=) и доля цифр/формулcenters = torch.tensor([[0.30, 0.05], # 0: код [0.05, 0.30], # 1: математика [0.03, 0.03]]) # 2: обычная проза# по 200 точек вокруг каждого центра (центр + шум), склеенные в одну таблицуX = torch.cat([c + 0.07 * torch.randn(200, 2) for c in centers]) # (600, 2)y = torch.arange(3).repeat_interleave(200) # метки: 200 нулей, 200 единиц, 200 двоек
model = torch.nn.Linear(2, 3) # W: (3, 2), b: (3,) — вот и вся «нейросеть»opt = torch.optim.SGD(model.parameters(), lr=5.0)for step in range(300): logits = model(X) # (600, 3): три логита на каждую страницу loss = F.cross_entropy(logits, y) # softmax + NLL в одной устойчивой функции opt.zero_grad() # стандартные три строки шага обучения (урок 1.4.1) loss.backward() opt.step()# argmax(dim=1) — номер класса с наибольшим логитом в каждой строке; сравниваем с правильными меткамиacc = (model(X).argmax(dim=1) == y).float().mean()print(f"loss = {loss.item():.3f}, accuracy = {acc:.3f}")
page = torch.tensor([[0.20, 0.12]]) # немного кода, немного формул# softmax превращает 3 логита в 3 вероятности; tolist() — в обычный список Pythonprint("p(код, математика, проза) =", [round(p, 3) for p in F.softmax(model(page), dim=1).squeeze().tolist()])
# Бинарный случай: softmax по двум логитам = сигмоида от их разностиz = torch.tensor([0.3, 1.7])print(f"{F.softmax(z, dim=0)[1].item():.4f} {torch.sigmoid(z[1] - z[0]).item():.4f}")Ожидаемый вывод (последние знаки могут отличаться в другой версии PyTorch):
loss = 0.158, accuracy = 0.955p(код, математика, проза) = [0.648, 0.173, 0.18]0.8022 0.8022Обратите внимание: F.cross_entropy принимает логиты, а не вероятности. Если подать в неё результат softmax,
softmax применится дважды — классическая ошибка (о ней подробно в уроке 3.3.3). Шаг lr=5.0 большой, потому что признаки маленькие
(сотые доли). Попробуйте стандартизировать их, как в прошлом уроке, и обойтись lr=0.5.
Проверь себя
Заголовок раздела «Проверь себя»- Логистическая регрессия: . Логит — логарифм шансов, и он линеен по признакам.
- Потери — бинарная cross-entropy (MLE для распределения Бернулли). Градиент по логиту — . Считайте loss по
логитам:
BCEWithLogitsLoss. - Граница решения — гиперплоскость. Поэтому модель линейна и не решает XOR.
- Softmax-регрессия: , градиент . Это однослойная нейросеть и в точности LM head любой LLM.
Логистическую регрессию с нуля вы соберёте ещё раз, уже как самостоятельный проект с автотестами, в домашней работе
s2-hw01-logreg-from-scratch в конце ступени 2.
Дальше: метрики качества. Accuracy, precision, recall, F1 и ROC-AUC — и почему метрика, которую вы показываете людям, почти никогда не совпадает с loss, который минимизирует модель.