BFloat16 (BF16)
BFloat16 (BF16) — это 16-битное число с плавающей запятой с 8-битной экспонентой и 7-битной мантиссой, как у FP32. Он сохраняет диапазон FP32, занимая вдвое меньше памяти. Отличия от FP16.
BFloat16 (формат с плавающей точкой для мозга, BF16) — это 16-битный формат с плавающей точкой: 1 знаковый бит, 8 бит экспоненты и 7 бит дробной части. Ширина экспоненты у него такая же, как у стандартного 32-битного формата с плавающей точкой (FP32), поэтому диапазон представимых значений совпадает — примерно от 1,2 × 10⁻³⁸ до 3,4 × 10³⁸, — а для хранения каждого числа требуется вдвое меньше памяти. За это приходится платить точностью: 7 бит дробной части дают всего две-три значащие десятичные цифры. Глубокое обучение хорошо переносит такое округление. Формат разработали в Google Brain для TPU Google, и он стал стандартным 16-битным форматом для обучения больших моделей на современных GPU и ускорителях.
Как BF16 представляет числа#
Значение BF16 — это в точности старшая половина значения FP32: тот же знак и показатель степени, а дробная часть сокращена с 23 до 7 бит. Поэтому преобразование FP32 в BF16 сводится к округлению с отбрасыванием младших 16 бит, а при обратном преобразовании добавляются нули, что позволяет реализовать формат на аппаратном уровне с небольшими затратами. Другой распространённый 16-битный формат, IEEE FP16 или формат половинной точности, иначе распределяет биты: 5 бит отведено под показатель степени и 10 — под дробную часть, что обеспечивает большую точность за счёт диапазона. Сравнение трёх форматов на одинаковых значениях показывает этот компромисс:
import torch
# Largest value, smallest normal value, and spacing just above 1.0 for each format
for dtype in [torch.float32, torch.float16, torch.bfloat16]:
info = torch.finfo(dtype)
print(f"{dtype!s:15} max={info.max:.3g} tiny={info.tiny:.3g} eps={info.eps:.3g}")
# A large activation, a tiny gradient, and a value that needs fine precision
x = torch.tensor([70000.0, 1e-8, 1.001953125])
print("FP16:", x.half().tolist())
print("BF16:", x.bfloat16().tolist())Максимальное значение BF16 — 3.39e38, тогда как у FP16 оно равно 65,504; минимальное нормальное значение BF16, 1.18e-38, совпадает с FP32. FP16 преобразует 70,000 в бесконечность, а 1e-8 — в ноль, тогда как BF16 сохраняет их как 70,144 и примерно 1.0e-8. Более мелкий шаг FP16 (eps 0.000977 против 0.00781 у BF16) позволяет точно представить 1.001953125, тогда как BF16 округляет это значение до 1.0.
BF16 и FP16 при обучении#
При обучении диапазон важнее точности. Градиенты часто становятся меньше минимальных значений FP16 и исчезают, поэтому при обучении с смешанной точностью FP16 функцию потерь перед обратным распространением ошибки умножают на коэффициент масштабирования, а при переполнении градиентов этот коэффициент уменьшают. Этот приём называется масштабированием функции потерь. BF16 не требует масштабирования функции потерь, поскольку его диапазон совпадает с диапазоном FP32, а обучение в BF16 даёт результаты, сопоставимые с FP32, на моделях для компьютерного зрения, распознавания речи, обработки языка и рекомендательных систем за то же число итераций и без изменения гиперпараметров. При обучении со смешанной точностью основная копия весов хранится в FP32, а умножения матриц выполняются в BF16 с накоплением в FP32: сумма множества произведений с 7-битной дробной частью в BF16 теряла бы слишком много точности. Выбор между 16-битными форматами в основном зависит от аппаратуры:
PyTorch поддерживает BF16 через автоматическую смешанную точность с помощью torch.autocast(device_type="cuda", dtype=torch.bfloat16). Ultralytics YOLO предоставляет тот же переключатель для обучения с помощью amp="bf16": он выполняет прямой проход с автоприведением к BF16 и отключает масштабировщик градиентов, используемый по умолчанию в режиме FP16, на CUDA GPU с аппаратной поддержкой BF16.
Поддержка аппаратурой#
TPU Google второго и третьего поколений выполняют умножение в BF16 в матричных блоках с накоплением в FP32; в более поздних поколениях TPU этот формат сохранился. GPU NVIDIA поддерживают BF16 на Tensor Core начиная с поколения Ampere, включая A100, H100 и GPU Blackwell; процессоры Intel Xeon с расширениями для матричных операций (AMX) ускоряют его на CPU. На NVIDIA A100 пиковая производительность вычислений Tensor Core в BF16 и FP16 составляет 312 терафлопс — в 16 раз больше, чем 19,5 терафлопс стандартных вычислений FP32 на этом GPU.
BF16 и смежные форматы#
- Половинная точность (FP16): 16-битный формат IEEE с более высокой точностью и гораздо меньшим диапазоном. Он по-прежнему широко используется для инференса: активации обычно остаются в пределах его диапазона, а дополнительная точность полезна.
- TF32: Режим вычислений NVIDIA Tensor Core с 8-битной экспонентой BF16 и 10-битной дробной частью FP16. Библиотеки могут использовать его для умножения матриц FP32 на GPU Ampere и новее, при этом тензоры остаются в 32-битном формате.
- FP8: 8-битные форматы с плавающей точкой, такие как E4M3 и E5M2, поддерживаемые GPU Hopper и Blackwell. Они ещё вдвое сокращают расход памяти при обучении и инференсе самых больших моделей.
- Квантование моделей: Преобразует веса обученной модели в целочисленные форматы, например INT8 или INT4, для развёртывания.
BF16 в больших языковых моделях#
Большинство больших языковых моделей (LLM) с открытыми весами обучают со смешанной точностью BF16 и публикуют с весами BF16. Эти веса служат эталоном для сравнения квантованных версий FP8 и INT4. При объёме 2 байта на параметр веса модели с 70 миллиардами параметров занимают 140 ГБ в BF16 — больше, чем 80 ГБ одной NVIDIA H100. Поэтому для обслуживания такой модели в BF16 требуется разместить её как минимум на двух GPU.










