EnglishРусский Map

BF16 против FP16

title
BF16 против FP16
type
concept
summary
BF16 сохраняет 8-битный порядок FP32 и урезает мантиссу, а FP16 урезает порядок: при обучении нейросетей диапазон важнее точности
tags
llm, numerics, training
created
2026-05-13
updated
2026-05-13
lang
ru
translation_of
bf16-vs-fp16
source_updated
2026-05-13
translated
2026-09-01
translator
lllm/antigravity/gemini-3.7-flash-medium

BF16 (bfloat16) и FP16 - это 16-битные форматы с плавающей точкой. Они распределяют биты по-разному:

  • FP32 - 1 знак, 8 порядок, 23 мантисса
  • FP16 - 1 знак, 5 порядок, 10 мантисса
  • BF16 - 1 знак, 8 порядок, 7 мантисса

FP16 сохраняет точность мантиссы FP32 и вдвое уменьшает диапазон порядка. BF16 сохраняет диапазон порядка FP32 и вдвое снижает точность мантиссы. Эти форматы не взаимозаменяемы.

Почему FP16 тормозил обучение

Когда в GPU появилась 16-битная арифметика с плавающей точкой, первым решением стало использовать FP16, поскольку он сохранял более "наглядное" свойство - значащие цифры. На практике же для обучения нейросетей динамический диапазон куда важнее точности:

  • Градиентный спуск заходит в диапазон величин порядка $10^{-7}$ - $10^{-8}$. 5-битный порядок FP16 в нормальном диапазоне представляет значения лишь до ~$6 \times 10^{-5}$, а с денормализованными числами (subnormals) доходит до $\sim 6 \times 10^{-8}$. Градиенты меньше этого порога обнуляются, и обновление весов тихо ничего не делает.
  • Обратное распространение ошибки делает малые поправки, которые требуют сохранения небольших разниц в весах. В FP16 происходит потеря значимости (underflow).
  • Функции активации во время обучения могут выдавать экстремальные значения. Максимальное представимое в FP16 число - около $6 \times 10^4$; переполнение скалярного произведения даёт $+\infty$, которое затем через функцию потерь расползается в виде NaN.

Ранним обходным решением было обучение со смешанной точностью (Mixed Precision) - хранить веса в FP32, большую часть вычислений делать в FP16, держать мастер-копию в FP32 и масштабировать loss (loss scaling), чтобы загнать величины градиентов в диапазон представимых для FP16 значений. Метод рабочий, но хрупкий, и из-за мастер-копий он увеличивал общее потребление памяти по сравнению с чистым FP32.

Что изменил BF16

Разработанный Google формат BF16 сохраняет порядок FP32 (тот же диапазон примерно от $10^{-38}$ до $10^{38}$) и урезает мантиссу с 23 до 7 бит. Точность значащих цифр падает, но поток градиентов не прерывается без всякого loss scaling. Обучение идёт по тому же циклу, что и для FP32, только с 16-битными весами.

Следствие для инференса: все современные модели с открытыми весами публикуются в BF16, а не в FP16. Когда вы видите размер модели вроде "Qwen 3.6 35B = 70 GB в BF16", это 35B × 2 байта. Та же модель в FP16 весила бы столько же (70 GB), но вела бы себя иначе по точности, а сами веса в FP16 с самого начала было бы невозможно обучить.

Почему это до сих пор важно для локальных LLM

Выбор BF16 влияет на работу квантования. Стандартные форматы квантования в llama.cpp ориентируются на базу в BF16: замеры KLD (сравнение, которое Вячеслав использует в своём разборе) делаются относительно BF16, а не FP16 или FP32. Когда пишут "этот квант деградировал на +2.1% относительно эталона", эталоном выступает именно BF16-релиз.

Цифра в 70 GB также определяет дальнейшие архитектурные решения: для модели 35B в BF16 минимальный размер на диске, сохраняющий большую часть качества, составляет ~12 GB (UD-Q2_K_XL). Это сжатие в пять с половиной раз и делает инференс 35B-модели на потребительских GPU вообще возможным.

Ссылки