Математика глубокого обучения
Численная устойчивость
logsumexp, стабильный softmax, температура — приёмы, без которых формулы не работают
Катастрофическое сокращение
Из прошлого урока: шаг сетки пропорционален величине числа. Отсюда первое следствие — вычитание близких величин теряет значащие цифры.
Пример, который стоит увидеть, потому что он ломает и в двойной точности. Посчитаем дисперсию набора двумя способами:
| формула | результат |
|---|---|
Верный ответ — . Наивная формула ошибается втрое, и не в fp32, а в fp64: она вычитает два числа порядка , различающиеся на единицу, и от результата остаётся мусор.
Отсюда общее правило:
logsumexp
Второй источник беды — экспонента. переполняется, обнуляется, и оба случая встречаются в логитах постоянно.
Тождество проверяется вынесением за скобку. Но численно два выражения совершенно разные:
| наивно | со сдвигом | |
|---|---|---|
После сдвига наибольший показатель равен нулю, то есть — переполнение невозможно по построению. Остальные слагаемые не больше единицы, и если какое-то обнулилось, оно и было пренебрежимо мало.
Второй случай — обнуление, и он опаснее: наивная формула даёт , что дальше
превращается в nan при первом же вычитании. Первый хотя бы шумит явно.
Стабильный softmax
Ровно тот же приём:
И у softmax есть свойство, которое делает это законным: инвариантность к сдвигу. Прибавление константы ко всем логитам не меняет результат, потому что сокращается в числителе и знаменателе.
Проверьте на виджете: сдвиньте все логиты вместе — распределение не изменится. Это не приближение, а тождество, и потому вычитание максимума бесплатно с точки зрения математики и обязательно с точки зрения арифметики.
- сумма вероятностей
- 1
- максимум
- 60.9%
Температура и затухание градиента
Температура делит логиты: . При распределение стремится к argmax, при — к равномерному.
Практически важна не сама температура, а то, что происходит с градиентом. Якобиан softmax содержит , и когда распределение схлопывается, эта величина обращается в нуль:
| максимальная вероятность | масштаб градиента | |
|---|---|---|
Последняя строка — не «маленький градиент», а его отсутствие: неотличимо от нуля даже в fp32. Отсюда прямая связь с уроком про внимание: масштабирование на нужно именно затем, чтобы логиты не оказались в этой области.
Приёмы списком
| проблема | решение |
|---|---|
| переполняется | вычесть максимум |
log_softmax, а не log(softmax(x)) | |
| при малом | log1p |
| при малом | expm1 |
| произведение вероятностей | сумма логарифмов (блок 3, урок 120) |
| дисперсия | центрировать до квадрата, или алгоритм Уэлфорда |
через logsigmoid(-x) | |
| накопление длинной суммы | fp32-накопитель даже при bf16-данных |
Вторая строка стоит отдельного внимания. log(softmax(x)) вычисляет экспоненты, нормирует,
затем берёт логарифм — и если какая-то вероятность обнулилась, получается .
log_softmax считает напрямую: ни одной экспоненты в опасной
области, ни одного логарифма от нуля. Именно поэтому в PyTorch кросс-энтропия принимает
логиты, а не вероятности — она внутри вызывает log_softmax, и передача уже
нормированных вероятностей ломает эту защиту.
Накопители
Последний приём, который часто забывают. Из прошлого урока: в bf16 прибавление к единице не меняет ничего, потому что эпсилон равен .
Значит, суммируя тысячу малых чисел в bf16, вы потеряете почти все: как только частичная сумма подрастёт, следующие слагаемые начнут округляться в неё бесследно. Поэтому:
- накопители держат в fp32, даже когда данные в bf16. Матричные ядра так и устроены: умножения в bf16, накопление в fp32;
- мастер-копия весов в fp32 — по той же причине, а не ради точности вычислений;
- для очень длинных сумм существует суммирование Кэхана, восстанавливающее потерянные младшие биты.
Здесь полезно вспомнить формат-виджет: сравните эпсилон bf16 и fp32 и прикиньте, сколько слагаемых нужно, чтобы разница стала заметной.
раскладка битов
- всего бит
- 16
- эпсилон
- 7.81e-3
- максимум
- 3.390e+38
- мин. нормальное
- 1.18e-38
- десятичных цифр
- 2.4
Последнее наблюдение из этого сравнения полезно само по себе: у bf16 и fp32 одинаковая экспонента. bf16 — это fp32 с отрезанной мантиссой, поэтому преобразование между ними сводится к отбрасыванию или дописыванию нулей, без всякой обработки диапазона. Это одна из причин, по которой bf16 удобен аппаратно.
Источники
- Goldberg — What Every Computer Scientist Should Know About Floating-Point Arithmetic — Cancellation и устойчивые формулы
- Blanchard и др. — Accurately computing the log-sum-exp and softmax functions — Разбор устойчивых вариантов
Проверки
0 из 2Приёмы устойчивости
Отметьте все верные утверждения о численной устойчивости.
logsumexp со сдвигом
Реализуйте
logsumexp_facts(z)для вектора логитов. Верните[naive_finite, lse, max_prob, entropy]:naive_finite—1.0, если наивная сумма получилась конечной и строго положительной, иначе0.0. В Python переполнениеmath.expбросаетOverflowError— перехватите его; в JavaScript получитсяInfinity;lse= , где — устойчивая формула;max_prob— наибольшая вероятность softmax, посчитанная как ;entropy— энтропия этого распределения в битах.
Обратите внимание, что
lseсчитается устойчиво всегда, независимо от того, что показалnaive_finite: первое число сообщает, провалилась ли наивная формула, а не как считать правильную.Полезное свойство: — это и есть softmax, потому что равен логарифму знаменателя. Отдельно нормировать не нужно.
Загрузка редактора…
Ctrl/⌘ + Enter