Mixed Precision Architecture: bf16 vs fp16 vs fp8 — Why Pure bf16 on RTX 4090?
fp16's loss scaling complexity, bf16's 'master fp32' pattern, fp8 (Ada supports but H100 is native), TF32 matmul precision flag, autocast nuances — cookbook's clear choice of pure bf16 for RTX 4090. NaN cost and training stability math.
Şükrü Yusuf KAYA
30 min read
Advanced🎯 Karar netliği
RTX 4090 üzerinde cookbook'un seçimi saf bf16. `torch.bfloat16` her yerde — weights, activations, gradients. Master fp32 yok, loss scaling yok, autocast karmaşası yok. Bu ders niye bunun doğru karar olduğunu, istisnaları ne zaman olduğunu, fp8'in 4090'da niye prematür olduğunu açıklar.
1. Üç Format Karşılaştırması#
| Format | Bit yapısı | Range | Precision (ulp) | RTX 4090 destek | Use case |
|---|---|---|---|---|---|
| fp32 | 1 + 8 + 23 | ±3.4e38 | 1.2e-7 | native | master weights eski mixed precision |
| fp16 | 1 + 5 + 10 | ±65504 | 9.8e-4 | native (V100+) | eski training, GAN |
| bf16 | 1 + 8 + 7 | ±3.4e38 | 7.8e-3 | native (Ampere+) | LLM training default |
| fp8 (e4m3) | 1 + 4 + 3 | ±448 | 0.0625 | Ada (limited) | LLM inference, H100 training |
| fp8 (e5m2) | 1 + 5 + 2 | ±57344 | 0.125 | Ada (limited) | LLM gradient |
Key insight:
- fp16 range küçük → büyük gradient'lar overflow → "loss scaling" gerekir
- bf16 range fp32 ile aynı, sadece precision düşük → overflow yok, basit
- fp8 range çok küçük → per-tensor scaling factor zorunlu (Transformer Engine)
2. fp16'nın Yaşattığı Acı: Loss Scaling#
fp16 range . Gradient'lar küçük (genelde 1e-4 — 1e-6 mertebesinde) → fp16'da underflow → sıfır → no update.
±65504Klasik çözüm — dinamik loss scaling:
- Forward'da loss'u büyük bir ile çarp (örn. 2^15 = 32768)
S - Backward grad'lar S × normal_grad → fp16'da temsil edilebilir
- Optimizer step'ten önce grad'ları S'e böl
- Eğer gözlemlenirse S'i yarıya indir, step'i atla
inf/NaN
from torch.cuda.amp import GradScaler, autocast scaler = GradScaler() with autocast(dtype=torch.float16): loss = model(x).loss scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()
Pratik sorun: Loss scaling'in 'doğru' sayısını bulmak işkence. Çok büyük → grad inf. Çok küçük → underflow. Modern LLM dynamics'inde aşırı hassas.
3. bf16'nın Sade Reçetesi#
bf16 range fp32 ile aynı → loss scaling yok. Cookbook'un her Lab'ında:
model.to(torch.bfloat16) # weights bf16 with torch.autocast(device_type="cuda", dtype=torch.bfloat16): out = model(x) loss = compute_loss(out) loss.backward() # grad'lar bf16 optimizer.step()
Veya HF Trainer ile:
TrainingArguments(bf16=True, ...) # her şey halloluyor
Niye master fp32 yok?
- Modern LLM'lerin loss surface'ı bf16'ya gayet uyumlu
- AdamW 8-bit zaten internal state'i quantize ediyor; master fp32 marginal yarar
- ~%30 memory tasarrufu (W + G + grads bf16, fp32 copy yok)
4. TF32 Matmul Precision Flag#
PyTorch 2.0+'ta fp32 matmul'lar TF32 (19-bit precision) ile koşulabilir:
torch.set_float32_matmul_precision("high") # = TF32 (Ada/Ampere native) # "highest" = saf fp32 # "high" = TF32 (default cookbook) # "medium" = bf16 matmul (loss surface'a göre riskli)
Çoğu LLM kodunda matmul'lar zaten bf16'da (autocast). Ama HF Trainer'ın çağrısı fp32 path'i kullanabilir → TF32 ile %30-50 hızlı.
evaluateCookbook varsayılan: (TF32 aktif).
high5. fp8 — Ada'da Hazır Değil mi?#
RTX 4090 (AD102) fp8 GEMM destekler (FP8 Tensor Cores). Ancak:
- PyTorch native fp8 training pipeline (NVIDIA Transformer Engine) H100'e optimize
- Ada üzerinde fp8 kernel'lar var ama fallback ortak
- Cookbook test'lerinde: 4090 + TE + fp8 → throughput %15-25 daha hızlı ama scaling factor management buggy bazı modellerde
Cookbook'un kararı (2026 başı):
- Training: bf16 (stable, hızlı, test edilmiş)
- Inference: fp8 dene (vLLM 0.6+ fp8 quant 4090 destekler, +%30-40 throughput)
- Fp8 training'i Part X'te (advanced quantization engineering) anlatılır
python
# === Training stability check — bf16 ile fp16 karşılaştırma ===import torchfrom torch import nn torch.manual_seed(42) def measure_grad_stats(dtype): model = nn.TransformerEncoderLayer(d_model=512, nhead=8, dim_feedforward=2048).to("cuda", dtype=dtype) x = torch.randn(8, 128, 512, device="cuda", dtype=dtype) out = model(x) loss = out.sum() loss.backward() grads = [p.grad.float() for p in model.parameters() if p.grad is not None] flat = torch.cat([g.flatten() for g in grads]) return { "dtype": str(dtype), "grad_min": flat.min().item(), "grad_max": flat.max().item(), "grad_abs_mean": flat.abs().mean().item(), "n_inf": (~torch.isfinite(flat)).sum().item(), "n_zero": (flat == 0).sum().item(), } print(measure_grad_stats(torch.float16))# fp16: bazı grad'lar zero (underflow) veya inf (overflow)print(measure_grad_stats(torch.bfloat16))# bf16: tüm grad'lar finite ve mantıklıprint(measure_grad_stats(torch.float32))# fp32: referencestability check — bf16 niye fp16'dan rahat
🐛 FMD — 'bf16 ile train ediyorum, loss NaN olarak başlıyor'
Hipotezler: (a) Model checkpoint fp16'da kayıtlı, bf16'ya cast'te değer aralıkları taşmış (range aynı ama precision rounding). Çözüm: `AutoModelForCausalLM.from_pretrained(..., torch_dtype=torch.bfloat16)` ile load et, sonradan cast etme. (b) Tokenizer pad_token_id model'in vocab'ında değil → embedding lookup garbage → NaN propagate. Çözüm: `model.resize_token_embeddings(len(tokenizer))`. (c) Custom loss'ta log(0) → NaN. Çözüm: `torch.clamp(p, min=1e-7).log()`. (d) FlashAttention v2.5- versiyonda bf16 grad buggy → güncelle. Drill: 4 hipotez için tek tek elimine et.
6. Bench (RTX 4090, Llama 3.1 8B QLoRA)#
| Precision | step/s | Peak GB | Final loss (1 epoch) | NaN encountered? |
|---|---|---|---|---|
| fp32 | 0.42 | 24 GB (OOM bazen) | 1.84 | hiç |
| fp16 + AMP + loss-scale | 1.51 | 12.1 GB | 1.86 | 12 step (scale adapt) |
| bf16 (cookbook) | 1.78 | 12.4 GB | 1.85 | hiç |
| bf16 + TE fp8 (4090 native) | 2.04 | 11.8 GB | 1.87 | 3 step (scale issue) |
Sonuç: bf16 saf — daha yavaş değil (TE fp8'den ufak fark), stable, sıfır loss-scaling acısı. Cookbook'un default'u.
✅ Teslim
- Yukarıdaki `measure_grad_stats`'ı kendi modeli üzerinde çalıştır. 2) fp16 vs bf16 karşılaştırma tablosunu görsel olarak elde et. 3) Sonraki ders: 1.5 — PCIe vs NVLink vs InfiniBand: Bandwidth Matematiği.
Frequently Asked Questions
Genellikle TR-MMLU üzerinde 0.3-1.0 puan kayıp (vLLM fp8 quant ile). MT-Bench score'da fark <%2. Bu kayıp çoğu use-case için kabul edilebilir. Cookbook'un Part X'te detaylı.
Yorumlar & Soru-Cevap
(0)Yorum yazmak için giriş yap.
Yorumlar yükleniyor...
Related Content
Part 0 — Engineering Foundations
Welcome to the Fine-Tuning Cookbook: System, Stage Taxonomy, and the Reproducibility Contract
Start LearningPart 0 — Engineering Foundations
Reproducibility Stack: Seeds, cuDNN Flags, and Deterministic CUDA — End the 'Works on My Machine' Problem
Start LearningPart 0 — Engineering Foundations