Смешанная точность (Mixed Precision)

Материал из MachineLearning.

Версия от 13:55, 18 июля 2026; Osman Osmanov (Обсуждение | вклад)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск
Статья написана с использованием LLM Gemini 3.1 Pro и проверена участником Osman Osmanov 16:32, 18 июля 2026 (MSD)

Промпт приводится полностью в Обсуждение:Смешанная точность (Mixed Precision)


Содержание

Смешанная точность

Смешанная точность (англ. Mixed Precision Training) — метод обучения глубоких нейронных сетей, при котором вычислительно интенсивные операции прямого и обратного проходов выполняются в формате пониженной точности (FP16, BF16), тогда как критические для сходимости параметры хранятся и обновляются в полной точности (FP32). Метод реализует фундаментальный компромисс между численной стабильностью и производительностью: современные GPU и TPU задействуют специализированные аппаратные блоки (тензорные ядра), способные выполнять матричные умножения в 2–4 раза быстрее в режиме пониженной точности, но прямое использование низкой разрядности приводит к потере младших битов градиентов и срыву сходимости[1].

Важно различать режимы обучения (training) и инференса (inference). При обучении алгоритм многократно вычисляет градиенты и корректирует веса; любая потеря информации на этапе обратного прохода аккумулируется по итерациям и уводит траекторию оптимизатора от истинного минимума. На инференсе, напротив, достаточно однократного прямого прохода; численная погрешность сказывается лишь на предсказании, поэтому здесь допустимо применение 8-битного целочисленного квантования. Смешанная точность при обучении балансирует на границе допустимой погрешности, ускоряя вычисления без ущерба для точности модели.

Форматы представления данных

Все операции в VLM, LLM и других архитектурах опираются на числа с плавающей запятой стандарта IEEE 754. Число представляется тройкой (s, e_{\text{stored}}, m) — знак, смещённая экспонента (хранимое значение) и дробная часть мантиссы. Истинное значение восстанавливается как:

x = (-1)^s \times 2^{e_{\text{stored}} - \text{bias}} \times (1 + m)

Смещение \text{bias} равно 2^{k-1} - 1, где k — число бит, отведённых под экспоненту. Экспонента определяет динамический диапазон, мантисса — число значащих разрядов внутри этого диапазона. Рассмотрим ключевые форматы:

  • FP32 (Single Precision, 32 бита): 1 знак, 8 экспонента (\text{bias}=127), 23 мантисса. Динамический диапазон нормализованных чисел: от \approx 1.18 \times 10^{-38} до \approx 3.40 \times 10^{38}, точность около 7 десятичных знаков. Считается золотым стандартом глубокого обучения, обладающий значительной избыточностью: большинство градиентов малы, и младшие разряды мантиссы FP32 почти не влияют на обновление весов.
  • TF32 (TensorFloat-32, 32 бита на устройстве): логически использует 1 знак, 8 экспонента (как FP32) и 10 мантисса (как FP16). Это внутренний формат тензорных ядер архитектур Ampere и новее. Динамический диапазон совпадает с FP32, а точность — с FP16. Используется для матричных умножений, обеспечивая ускорение без ручного изменения кода.
  • FP16 (Half Precision, 16 бит): 1 знак, 5 экспонента (\text{bias}=15), 10 мантисса. Динамический диапазон: от 6.10 \times 10^{-5} (минимальное положительное нормализованное) до 6.55 \times 10^{4}. Жесткое ограничение сверху и снизу приводит к тому, что значительная доля градиентов (по модулю меньше 2^{-14} \approx 6.1 \times 10^{-5}) уходит в ноль (исчезновение). Малая длина мантиссы также ограничивает точность сложения чисел разных порядков.
  • BF16 (Brain Floating Point, 16 бит): 1 знак, 8 экспонента (\text{bias}=127), 7 мантисса. Экспонента совпадает с FP32, поэтому динамический диапазон идентичен FP32: числа могут достигать ~3.4 \times 10^{38} и падать до 1.18 \times 10^{-38}, что полностью исключает затухание большей части градиентов. Плата — сниженная точность (всего 7 бит мантиссы), но для алгоритмов на основе SGD это часто приемлемо. Формат предложен Google для TPU и быстро стал основным для обучения больших языковых моделей[1].
  • FP8 (8 бит): два варианта стандарта NVIDIA/ARM/Intel — E4M3 (4 бита экспонента, bias=7, 3 мантисса) и E5M2 (5 бит экспонента, bias=15, 2 мантиссы). E4M3 обеспечивает более высокую точность (до 7.5% относительной погрешности), тогда как E5M2 — расширенный динамический диапазон (за счёт большей экспоненты). Как правило, E4M3 применяется для прямого прохода (веса и активации), а E5M2 — для обратного прохода (градиенты), чтобы минимизировать риск зануления градиента[1].
Формат Экспонента (бит) Мантисса (бит) Динамический диапазон (нормализованные значения) Ключевая особенность / Применение
FP32 8 23 \approx 1.18 \times 10^{-38}3.40 \times 10^{38} Полная точность, эталон для обучения и хранения весов
TF32 8 10 \approx 1.18 \times 10^{-38}3.40 \times 10^{38} Прозрачное ускорение матричных умножений на Ampere/Blackwell, диапазон FP32 при точности FP16
FP16 5 10 6.10 \times 10^{-5}6.55 \times 10^{4} Основной формат для вычислений на тензорных ядрах (Volta+), требует масштабирования потерь
BF16 8 7 1.18 \times 10^{-38}3.40 \times 10^{38} Диапазон FP32, обучение без масштабирования, стандарт для LLM
FP8 E4M3 4 3 \approx 1 \times 10^{-8} – 448 Повышенная точность; прямой проход в Hopper/Blackwell
FP8 E5M2 5 2 \approx 1.5 \times 10^{-5} – 57344 Расширенный диапазон; обратный проход, уменьшение исчезновения градиента

Предпосылки к появлению смешанной точности

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

Исчезновение градиентов под нижнюю границу (Underflow)

В глубоких сетях (LSTM, ResNet, Transformer) на ранних этапах обучения или при использовании функций активации с насыщением значения градиентов часто становятся меньше 6.10 \times 10^{-5} — минимального нормализованного числа FP16. Как уже было затронуто ранее, субнормальные числа FP16 могут представлять ещё меньшие величины, но резко теряют точность, и многие GPU обрабатывают их крайне медленно. На практике такие градиенты просто зануляются. При нулевых градиентах обратное распространение останавливается, и соответствующие слои перестают обучаться.

Проблема округления при обновлении весов

Правило обновления весов:

W_{\text{new}} = W_{\text{old}} - \eta \cdot g.

В FP16 типичное значение веса W_{\text{old}} может быть порядка 10^0, а произведение скорости обучения \eta (например, 10^{-4}) на градиент g (порядка 10^{-2}) даёт поправку \Delta W \approx 10^{-6}. Если относительный сдвиг не превышает 2^{-10} \approx 9.8 \times 10^{-4}, то FP16 не сможет различить W_{\text{old}} и W_{\text{new}}, поскольку результат сложения просто округлится до исходного значения. Математически, 1.0 + 10^{-6} = 1.0 в арифметике FP16. Это делает обучение невозможным.

Классический пайплайн смешанной точности (по методике NVIDIA)

Алгоритм автоматической смешанной точности (AMP) был описан и реализован в библиотеках NVIDIA Apex и PyTorch[1]. Его цель — использовать преимущества FP16 для быстрых матричных умножений и экономии памяти, одновременно защитив обучение от численных потерь. Пайплайн состоит из шести шагов.

  1. Создание FP32-копии весов (Master Weights). Помимо привычных весов модели, поддерживается их точная мастер-копия в FP32. Прямой проход выполняется с FP16-весами, но аккумулирование обновлений происходит в FP32, что позволяет накапливать малые поправки без потери информации.
  2. Прямой проход (Forward Pass). Веса и активации конвертируются в FP16. Все операции свёрток, линейных слоёв, умножений матриц выполняются на тензорных ядрах с использованием FP16-арифметики. Функция потерь также вычисляется в FP16, экономя пропускную способность памяти.
  3. Масштабирование потерь (Loss Scaling). Градиенты многих слоёв могут быть настолько малы, что в FP16 окажутся за пределами динамического диапазона и занулятся. Чтобы решить эту проблему, функцию потерь умножают на большой коэффициент S (типично 8192 или выше). Тогда градиенты во всех слоях масштабируются в S раз: g_{\text{scaled}} = S \cdot g. Если исходный градиент был 10^{-7}, умножение на 8192 даст 8.192 \times 10^{-4}, что уже представимо в FP16.
  4. Обратный проход (Backward Pass). Обратное распространение выполняется в FP16, но с масштабированными градиентами. Таким образом, большая часть арифметики задействует быстрые ядра, а проблема исчезновения градиентов временно купирована.
  5. Де-масштабирование (Unscaling) и обновление. Перед применением градиентов к весам выполняется обратное деление: g_{\text{actual}} = g_{\text{scaled}} / S, после чего градиенты копируются в FP32. Затем обновление применяется к FP32-мастер-весам, где и накапливаются поправки.
  6. Динамический выбор масштаба (Dynamic Loss Scaling). При слишком большом S отдельные градиенты могут превысить максимальное значение FP16 (6.55 \times 10^{4}) и превратиться в NaN/Inf. Алгоритм отслеживает такие переполнения: если в батче обнаружены некорректные значения, шаг оптимизатора пропускается (skip update), а масштаб S уменьшается (например, вдвое). Если же на протяжении многих итераций переполнений нет, S плавно увеличивают, чтобы вернуть защиту самых малых градиентов. Такая стратегия реализована в PyTorch AMP автоматически.

Тензорные ядра

Ускорение смешанной точности стало возможным благодаря специализированным вычислительным блокам — тензорным ядрам (Tensor Cores), впервые представленным в архитектуре NVIDIA Volta (2017)[1]. Тензорное ядро выполняет операцию над матрицами 4×4 за один такт:

\mathbf{D} = \mathbf{A} \times \mathbf{B} + \mathbf{C}

Здесь перемножаемые матрицы \mathbf{A} и \mathbf{B} хранятся в FP16 (в последних поколениях — также BF16, TF32, FP8), а \mathbf{C} и результат \mathbf{D} могут находиться в FP32 или FP16. Накопление суммы выполняется с точностью не ниже FP32, что гарантирует корректность результата без потери значащих цифр. Таким образом, дорогостоящее матричное умножение выполняется на скорости ввода-вывода FP16, а результирующая точность соответствует FP32.

В архитектурах Ampere (A100) и Hopper (H100) тензорные ядра поддерживают BF16 и TF32 (19-битный внутренний формат), а в Blackwell — и FP8, что даёт дальнейшее удвоение пропускной способности. На практике использование Tensor Cores с FP16/BF16 даёт пиковое ускорение вычислений в 2–8 раз по сравнению с чистым FP32 на тех же GPU.

Практическая реализация и влияние на ресурсы

Программный стек

В PyTorch смешанная точность включается менеджером контекста torch.autocast и обёрткой над оптимизатором torch.amp.GradScaler[1]. Модель, оптимизатор и функция потерь остаются без изменений; автоматический кастинг определяет, какие операции безопасно выполнять в FP16 (матричные умножения, свёртки), а какие требуют FP32 (нормализация, softmax). Инженеру достаточно добавить несколько строк:

   scaler = torch.amp.GradScaler('cuda')
   for data, target in dataloader:
       optimizer.zero_grad()
       with torch.autocast(device_type='cuda', dtype=torch.float16):
           output = model(data)
           loss = criterion(output, target)
       scaler.scale(loss).backward()
       scaler.step(optimizer)
       scaler.update()

Профиль эффективности

Смешанная точность не всегда радикально сокращает память, занятую параметрами модели. Например, для оптимизатора Adam[1] в чистом FP32 требуется 4 байта на веса + 8 байт на состояния (моменты первого и второго порядка) = 12 байт на параметр. В режиме AMP добавляются FP32 мастер-веса (4 байта), FP16 копия весов (2 байта) и те же FP32 состояния Adam (8 байт) — итого 14 байт на параметр. Основная экономия памяти (20–40% и более) достигается за счёт двукратного сжатия сохранённых активаций (saved activations): в моделях типа Трансформер активации обратного прохода могут занимать больше памяти, чем сами параметры. Хранение их в FP16 вдвое снижает этот объём, позволяя увеличить размер мини-батча или разрядность модели. Прирост скорости обучения (wall-clock time) составляет от 1.5x до 3x в зависимости от архитектуры и доли операций, задействующих тензорные ядра.

Преимущества и недостатки

Применение смешанной точности в обучении и инференсе глубоких нейронных сетей связано с компромиссом между аппаратной эффективностью и численной стабильностью вычислений.

Преимущества

  • Ускорение вычислений. Перенос матричного умножения и свёрточных операций из формата FP32 в форматы сниженной разрядности (FP16, BF16 или FP8) позволяет задействовать специализированные аппаратные блоки — тензорные ядра (например, Tensor Cores в архитектурах NVIDIA). Это обеспечивает прирост производительности в 2–4 раза по сравнению с чистым FP32.
  • Экономия видеопамяти (VRAM). Переменные в формате FP16 занимают 2 байта вместо 4 байт в FP32. Снижение объема хранимых активаций и градиентов позволяет существенно увеличить размер пакета данных (размер батча) либо обучать более масштабные архитектуры (например, большие языковые модели) на существующем оборудовании.
  • Снижение нагрузки на шину памяти. Уменьшение размера данных вдвое сокращает время, затрачиваемое на их пересылку между памятью GPU и вычислительными блоками, что устраняет частые узкие места (memory bottleneck) в пропускной способности.
  • Сохранение целевых метрик. Благодаря использованию мастер-копии весов в формате FP32 для шага оптимизатора, итоговое качество сходимости, точность (Accuracy) и функция потерь (Loss) обученной модели остаются на уровне стандартного обучения высокой точности.

Недостатки

  • Проблема исчезновения градиентов (Underflow). Формат FP16 имеет ограниченный динамический диапазон (минимальное ненулевое значение порядка 6 \cdot 10^{-5}). В процессе обратного распространения ошибки малые значения градиентов часто округляются до нуля, что приводит к остановке обучения отдельных слоев.
  • Необходимость масштабирования потерь (Loss Scaling). Для борьбы с исчезновением градиентов разработчикам приходится внедрять механизмы динамического или статического масштабирования: умножать значение функции потерь на большой коэффициент перед расчетом градиентов и делить обратно перед обновлением весов. Это усложняет логику пайплайна и отладку.
  • Аппаратная зависимость. Эффективность метода напрямую привязана к микроархитектуре графического процессора. На устаревшем оборудовании (без аппаратной поддержки FP16/BF16 вычислений) применение смешанной точности приводит к падению производительности из-за накладных расходов на постоянную конвертацию типов данных (кастинг).
  • Дополнительные затраты памяти на оптимизатор. Поскольку методология требует одновременного хранения весов в низкой точности (для прямого и обратного проходов) и мастер-копии вместе со спецификациями оптимизатора (например, Adam) в FP32, для небольших моделей чистая экономия памяти может оказаться менее выраженной, чем ожидалось.

См. также

Ссылки

Личные инструменты