1. Основы численного анализа, компьютерная арифметика и природа ошибок вычислений
Основы численного анализа, компьютерная арифметика и природа ошибок вычислений
Представьте, что вы обучаете глубокую нейронную сеть в течение нескольких недель на дорогостоящем кластере GPU. На миллионной итерации градиент внезапно превращается в NaN (Not a Number), или веса модели застывают, хотя функция потерь еще далека от минимума. Часто причина кроется не в архитектуре сети или плохих данных, а в фундаментальном ограничении вычислительной техники: компьютеры не умеют работать с вещественными числами. Они работают лишь с их дискретными аппроксимациями. В мире численной оптимизации разница между математическим идеалом и машинной реальностью — это не просто погрешность, а фактор, определяющий сходимость алгоритма и стабильность всей системы.
Анатомия машинного числа: стандарт IEEE 754
Математическая прямая непрерывна и бесконечна. Память компьютера конечна и дискретна. Чтобы представить число в цифровом виде, мы используем стандарт IEEE 754, который определяет формат чисел с плавающей точкой (floating-point).
Любое число в этой системе представляется в виде:
Где:
В машинном обучении мы чаще всего сталкиваемся с форматами float32 (Single Precision) и float64 (Double Precision), а в последнее время — с float16 и bfloat16 для ускорения обучения нейросетей.
| Формат | Биты (S+E+M) | Значащие десятичные цифры | Диапазон (прибл.) | | :--- | :--- | :--- | :--- | | float16 | 1 + 5 + 10 | ~3.3 | | | bfloat16| 1 + 8 + 7 | ~2.1 | | | float32 | 1 + 8 + 23 | ~7.2 | | | float64 | 1 + 11 + 52 | ~15.9 | |
Обратите внимание на bfloat16 (Brain Floating Point). У него такая же экспонента, как у float32, но гораздо более короткая мантисса. Это позволяет сохранять широкий динамический диапазон (важно для градиентов), жертвуя точностью отдельных значений.
Машинный эпсилон и относительная точность
Ключевым понятием численного анализа является машинный эпсилон ( или ). Это наименьшее положительное число, такое что:
Для float32 , для float64 . Это означает, что если вы пытаетесь прибавить к единице число, которое меньше , результат в компьютерной логике останется равным . В контексте оптимизации это ставит предел точности, с которой мы можем найти минимум функции.
Источники и классификация ошибок
Ошибки в численных методах неизбежны, но их можно и нужно контролировать. Мы разделяем их на три основных типа:
Потеря значимости (Catastrophic Cancellation)
Самый опасный вид ошибки округления в оптимизации — это вычитание двух близких по величине чисел. Рассмотрим вычисление производной через конечные разности:
Математически, чем меньше , тем точнее результат. Однако численно, если слишком мало, значения и становятся почти идентичными. При их вычитании старшие разряды мантиссы обнуляются, и в результате остаются только случайные «хвосты» — ошибки округления, которые затем делятся на крошечное , превращаясь в гигантский шум.
Исчезновение и взрыв градиентов (Underflow & Overflow)
В глубоком обучении мы часто перемножаем длинные последовательности чисел (например, при обратном распространении ошибки через множество слоев).
NaN.Обусловленность задач и устойчивость алгоритмов
Почему одни функции оптимизируются легко, а другие заставляют алгоритм «блуждать»? Ответ кроется в понятии обусловленности.
Число обусловленности (Condition Number) задачи характеризует чувствительность решения к малым изменениям входных данных. Для функции относительное число обусловленности определяется как:
Если велико, задача называется плохо обусловленной. В контексте минимизации квадратичной формы число обусловленности матрицы (отношение максимального собственного значения к минимальному, ) напрямую определяет скорость сходимости градиентного спуска.
Представьте узкий и длинный овраг. Если вы находитесь на его склоне, градиент направлен почти перпендикулярно дну оврага, а не к его глобальному минимуму. Это классический пример плохой обусловленности ландшафта потерь.
Численная устойчивость
Алгоритм называется численно устойчивым, если ошибки округления, возникающие в процессе вычислений, не растут экспоненциально. Рассмотрим задачу вычисления Softmax, которая повсеместно используется в классификации:
Если среди есть большое положительное число (например, ), то вызовет Overflow. Однако математически Softmax инвариантен к сдвигу: .
Устойчивая реализация выбирает :
Теперь самый большой показатель экспоненты равен 0 (), и переполнение невозможно. Это простейший пример того, как знание компьютерной арифметики меняет код.
Прямой и обратный анализ ошибок
Как понять, насколько можно доверять результату работы алгоритма? Существует два подхода:
В машинном обучении мы часто полагаемся на то, что стохастическая природа алгоритмов (например, SGD) сама по себе является регуляризатором, «прощающим» ошибки округления. Однако при переходе к вычислениям с низкой точностью (Quantization до 4 или 8 бит) понимание этих механизмов становится критическим.
Практические аспекты реализации
Перевод математики в код требует внимания к деталям, которые опускаются в учебниках по анализу.
Суммирование длинных последовательностей
При обучении на больших базах данных мы часто вычисляем среднее значение градиента или функции потерь. Простое суммирование при больших приводит к накоплению ошибки. Если текущая сумма уже очень велика, а очередное слагаемое мало, то может оказаться меньше, чем , и просто «исчезнуть».
Для решения этой проблемы используется алгоритм суммирования Кэхэна (Kahan Summation). Он вводит дополнительную переменную для компенсации накопленной ошибки:
Этот метод позволяет достичь точности, близкой к float64, используя только float32.
Логарифмическое пространство
Многие вероятностные модели (например, наивный Байес или скрытые марковские модели) требуют перемножения множества малых вероятностей . Это гарантированно ведет к Underflow.
Стандартный прием — переход в логарифмическое пространство:
Вместо вероятностей мы храним их логарифмы (log-probs), что превращает умножение в сложение и расширяет диапазон представимых значений. При необходимости вернуться к сумме вероятностей (например, для нормализации) используется трюк Log-Sum-Exp:
Численное дифференцирование и его ловушки
Хотя в современном ML доминирует автоматическое дифференцирование (Autograd), понимание численного дифференцирования необходимо для проверки корректности градиентов (Gradient Checking).
Мы уже упоминали проблему вычитания близких чисел. Чтобы минимизировать ошибку, вместо односторонней разности используют центральную разность:
Ошибка этой аппроксимации имеет порядок , в то время как у односторонней — . Однако существует еще более элегантный метод — дифференцирование с использованием комплексного шага (Complex Step Differentiation):
Здесь — мнимая единица. Этот метод не страдает от потери значимости при вычитании, так как вычитание в нем вообще не используется. Мы можем брать и получать точность производной вплоть до машинного эпсилона. Это работает для функций, которые можно аналитически продолжить в комплексную плоскость.
Арифметика в тензорных вычислениях
Современные библиотеки (PyTorch, JAX, TensorFlow) оптимизируют вычисления на уровне ядер CUDA. Важно понимать, что порядок операций в коде может не совпадать с порядком в математике. Из-за неассоциативности плавающей точки результаты на GPU и CPU могут незначительно различаться.
В задачах оптимизации это может привести к тому, что на одной архитектуре алгоритм сойдется за 100 итераций, а на другой — за 105 или вовсе застрянет. Это не баг, а свойство численных методов. Для обеспечения воспроизводимости часто приходится фиксировать не только seed генератора случайных чисел, но и использовать детерминированные алгоритмы сверток и суммирования, что может замедлить обучение.
Границы точности и критерии остановки
Когда мы реализуем алгоритм оптимизации, нам нужно решить, когда прекратить итерации. Типичные условия:
Здесь — порог (tolerance). Выбор напрямую зависит от машинного эпсилона. Если вы установите для float32, алгоритм никогда не остановится по этому критерию, так как шум округления будет всегда больше этого порога. Разумный выбор для первого порядка оптимизации обычно лежит в пределах для одинарной точности.
Понимание природы чисел в компьютере превращает «магию» настройки нейросетей в инженерную дисциплину. Каждый раз, когда вы видите странное поведение модели, спрашивайте себя: не пытаюсь ли я вычесть два слишком близких числа? Не вылетел ли я за пределы динамического диапазона? Обусловлена ли моя матрица весов? Ответы на эти вопросы часто лежат в фундаментах численного анализа, заложенных еще до появления первого транзистора.