BFloat16 (BF16)
브레인 부동소수점 16비트(BF16)는 FP32의 8비트 지수와 7비트 가수를 사용하는 16비트 부동소수점 형식으로, 메모리는 절반만 사용하면서 FP32의 표현 범위를 유지합니다. FP16과의 차이를 다룹니다.
BFloat16(뇌 부동소수점, BF16)은 부호 비트 1개, 지수 비트 8개, 가수 비트 7개로 구성된 16비트 부동소수점 형식입니다. 표준 32비트 부동소수점(FP32)과 지수 폭이 같으므로 약 1.2 × 10⁻³⁸에서 3.4 × 10³⁸까지 동일한 값 범위를 표현하면서 숫자 하나를 저장하는 데 메모리는 절반만 사용합니다. 그 대가로 정밀도가 낮아집니다. 가수 비트 7개로 표현할 수 있는 유효 숫자는 소수점 이하 두세 자리뿐입니다. 딥러닝은 이러한 반올림 오차를 잘 견디며, Google Brain에서 Google의 TPU를 위해 고안된 이 형식은 대규모 모델을 최신 GPU와 가속기에서 학습할 때 기본으로 사용하는 16비트 형식이 되었습니다.
BF16의 숫자 표현 방식#
BF16 값은 FP32 값의 상위 절반과 정확히 같습니다. 부호와 지수는 동일하며 가수는 23비트에서 7비트로 잘립니다. 따라서 FP32를 BF16으로 변환할 때는 하위 16비트를 반올림해 버리기만 하면 되고, 다시 변환할 때는 0을 덧붙이면 되므로 하드웨어에서 형식을 구현하는 비용이 낮습니다. 흔히 사용하는 다른 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보다 크며, 최솟값인 정규화 수 1.18e-38은 FP32와 같습니다. FP16은 70,000을 무한대로, 1e-8을 0으로 바꾸지만 BF16은 두 값을 각각 70,144와 약 1.0e-8로 유지합니다. FP16은 간격이 더 촘촘하므로(eps 0.000977, BF16은 0.00781) 1.001953125를 정확하게 보존하지만, BF16은 이를 1.0으로 반올림합니다.
학습에서의 BF16과 FP16 비교#
학습 중에는 정밀도보다 표현 범위가 더 중요합니다. 그래디언트는 FP16에서 표현 가능한 최솟값보다 작아져 0으로 소실되는 경우가 많으므로, FP16 혼합 정밀도 학습에서는 역전파 전에 손실값에 스케일 계수를 곱하고 그래디언트가 오버플로될 때마다 해당 계수를 낮추는 기법인 손실 스케일링을 적용합니다. BF16은 표현 범위가 FP32와 같으므로 손실 스케일링이 필요하지 않으며, BF16 학습은 비전, 음성, 언어 및 추천 모델에서 FP32와 일치하는 결과를 내고 하이퍼파라미터를 변경하지 않은 채 동일한 반복 횟수로 수행됩니다. 혼합 정밀도 학습에서는 가중치의 마스터 사본을 FP32로 유지하고 행렬 곱셈은 BF16으로 수행하되, 가수부가 7비트인 곱셈 결과를 BF16으로 여러 개 합산하면 정밀도 손실이 지나치게 커지므로 누산에는 FP32를 사용합니다. 두 16비트 형식 중 어느 것을 선택할지는 대체로 하드웨어에 따라 달라집니다:
PyTorch는 torch.autocast(device_type="cuda", dtype=torch.bfloat16)를 사용한 자동 혼합 정밀도를 통해 BF16을 적용합니다. Ultralytics YOLO는 CUDA GPU에서 네이티브 BF16을 지원할 경우 순전파를 BF16 자동 캐스팅으로 실행하고 기본 FP16 모드에서 사용하는 그래디언트 스케일러를 끄는 amp="bf16" 옵션으로 학습에 동일한 전환 기능을 제공합니다.
하드웨어 지원#
Google Cloud TPU의 2세대와 3세대는 행렬 연산 유닛에서 BF16 곱셈을 수행하고 FP32로 누산하며, 이후 TPU 세대도 이 형식을 유지했습니다. NVIDIA GPU는 Ampere 세대부터 A100, H100, Blackwell GPU를 포함해 Tensor Core에서 BF16을 지원하며, Advanced Matrix Extensions(AMX)가 탑재된 Intel Xeon 프로세서는 CPU에서 BF16 연산을 가속합니다. NVIDIA A100에서는 BF16과 FP16 Tensor Core 연산이 모두 최대 312 테라플롭스에 도달하며, 이는 GPU의 표준 FP32 연산 성능인 19.5테라플롭스의 16배입니다.
BF16과 관련 형식 비교#
- 반정밀도(FP16): 정밀도는 높지만 표현 범위가 훨씬 좁은 IEEE 16비트 형식입니다. 활성화 값이 표현 범위 안에 머물고 추가 정밀도가 도움이 되는 추론 작업에서 여전히 널리 사용됩니다.
- TF32: NVIDIA Tensor Core의 연산 모드로, BF16의 8비트 지수와 FP16의 10비트 가수로 구성됩니다. 라이브러리는 Ampere 이상 GPU에서 텐서를 32비트로 저장한 상태로 FP32 행렬 곱셈에 이 모드를 사용할 수 있습니다.
- FP8: E4M3 및 E5M2와 같은 8비트 부동소수점 형식으로, Hopper 및 Blackwell GPU에서 지원되며 최대 규모 모델의 학습과 추론에 필요한 메모리를 다시 절반으로 줄여 줍니다.
- 모델 양자화: 학습된 가중치를 INT8 또는 INT4와 같은 정수 형식으로 변환하여 배포합니다.
대규모 언어 모델에서의 BF16#
대부분의 오픈 웨이트 대규모 언어 모델(LLM)은 BF16 혼합 정밀도로 학습되고 BF16 가중치와 함께 공개됩니다. 이 가중치는 양자화된 FP8 및 INT4 버전을 비교하는 기준으로 사용됩니다. 파라미터당 2바이트를 사용하므로 파라미터 700억 개 모델의 가중치는 BF16에서 140GB를 차지해 NVIDIA H100 한 대의 80GB보다 큽니다. 따라서 BF16으로 이러한 모델을 서빙하려면 최소 두 개의 GPU에 모델을 분할해야 합니다.










