Accelerate PyTorch Training with Mixed‑Precision (AMP) – A Practical Guide
Learn how PyTorch’s Automatic Mixed‑Precision (AMP) can cut GPU memory usage and speed up training on Tensor‑Core GPUs. This guide shows the core API, a full training loop example, and key pitfalls to avoid.
24 May 2026, 16:00 UTC

Why Mixed‑Precision Matters
Training large neural nets on consumer GPUs can hit memory limits and slow down due to the bandwidth required for 32‑bit floating‑point operations. PyTorch’s Automatic Mixed‑Precision (AMP) API lets you run most of the forward pass in float16 while keeping gradients in float32. This reduces memory traffic and unlocks Tensor Core acceleration on NVIDIA GPUs (Volta, Turing, Ampere, Ada, Hopper). The key benefit is a faster, larger‑batch training run without sacrificing model accuracy.
How AMP Works Under the Hood
AMP is built around two components:
torch.cuda.amp.autocast()– a context manager that automatically casts supported ops tofloat16during the forward pass.torch.cuda.amp.GradScaler– multiplies the loss by a scaling factor beforebackward()to keepfloat16gradients from underflowing, then unscales them before the optimizer step.
During a training step the sequence is:
- Forward pass inside
autocast. - Compute loss.
- Call
scaler.scale(loss).backward()to scale gradients. - Optionally clip gradients.
- Call
scaler.unscale_(optimizer)to bring gradients back to their true magnitude. - Update parameters with
optimizer.step()viascaler.step(optimizer). - Call
scaler.update()to adjust the scale for the next iteration.
Concrete Example: Training a Tiny CNN on CIFAR‑10
Below is a minimal training loop that demonstrates AMP. Replace the placeholder components with your own model, dataset, and optimizer.
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
# Check PyTorch version
assert torch.__version__ >= '1.6', 'AMP requires PyTorch 1.6 or newer'
# Simple CNN (placeholder for )
class TinyCNN(nn.Module):
def __init__(self):
super().__init__()
self.conv = nn.Sequential(
nn.Conv2d(3, 16, 3, padding=1), nn.ReLU(),
nn.Conv2d(16, 32, 3, padding=1), nn.ReLU(),
nn.AdaptiveAvgPool2d(1)
)
self.fc = nn.Linear(32, 10)
def forward(self, x):
x = self.conv(x)
x = torch.flatten(x, 1)
return self.fc(x)
model = TinyCNN().cuda()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
scaler = torch.cuda.amp.GradScaler()
# Simple CIFAR‑10 loader (placeholder for )
transform = transforms.Compose([transforms.ToTensor()])
train_loader = DataLoader(datasets.CIFAR10(root='.', train=True, download=True, transform=transform), batch_size=128, shuffle=True)
for epoch in range(1): # single epoch for illustration
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.cuda(), target.cuda()
optimizer.zero_grad()
# 1. Forward pass with autocast
with torch.cuda.amp.autocast():
output = model(data)
loss = criterion(output, target)
# 2. Scale loss and backward
scaler.scale(loss).backward()
# 3. Optional: gradient clipping on unscaled grads
scaler.unscale_(optimizer)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
# 4. Optimizer step via scaler
scaler.step(optimizer)
scaler.update()
if batch_idx % 50 == 0:
print(f'Epoch {epoch} Batch {batch_idx} | Loss {loss.item():.4f}')
# Check memory usage before and after autocast
mem_before = torch.cuda.memory_allocated()
with torch.cuda.amp.autocast():
_ = model(data)
mem_after = torch.cuda.memory_allocated()
print(f'Memory before: {mem_before/1e6:.1f} MB, after: {mem_after/1e6:.1f} MB')
Key checkpoints you can verify:
- All tensors in
model.parameters()remainfloat32after the optimizer step. - Gradients after
scaler.unscale_arefloat32and not NaN or Inf. - Memory allocated inside
autocastis lower than outside.
Limitations & Common Pitfalls
Hardware Support
AMP’s speedup comes from Tensor Cores. GPUs without Tensor Cores (e.g., older Pascal or non‑NVIDIA GPUs) will see little to no performance gain and may even incur overhead due to casting logic.
Unsupported Ops & Layers
Some custom kernels, large embedding tables, or ops that internally cast to float32 will not benefit from autocast. If you encounter runtime errors like “CUDA error: unsupported data type”, explicitly cast those ops back to float32 or exclude them from autocast using torch.cuda.amp.autocast(enabled=False) around the problematic section.
Loss Scaling Issues
- Too high a scale can cause overflow during
backward(), leading to NaN gradients. AMP’sGradScalerautomatically reduces the scale when it detects overflow. - Too low a scale reduces the benefit of
float16activations because the gradient magnitude is artificially small, potentially slowing convergence.
Gradient Clipping Order
Clipping must happen after scaler.unscale_(optimizer). If you clip before unscaling, you are clipping the already scaled gradients, which has no effect on the actual update and can lead to ineffective regularization.
Non‑Deterministic Behavior
Mixed‑precision training can introduce minor numerical differences. If reproducibility is critical, set torch.backends.cudnn.deterministic = True and keep the same random seed, but be aware that this may reduce performance.
When to Use AMP
- Training models that fit on a single GPU but would otherwise exceed memory limits.
- Large‑batch training where memory bandwidth is a bottleneck.
- Deploying on modern NVIDIA GPUs (Volta or newer) where Tensor Core utilization yields measurable speedups (commonly 1.3–2× for ResNet‑18 on a 32‑GB GPU).
For older GPUs or non‑NVIDIA hardware, you may choose to skip AMP and rely on FP32 training to avoid unnecessary complexity.
Practical Verification Checklist
- Run the example on a target GPU and compare
torch.cuda.memory_allocated()with and withoutautocast. - Measure epoch time for a standard model (e.g., ResNet‑18) with AMP enabled vs disabled; expect a 10‑30% speedup on Tensor Core GPUs.
- Inspect gradient dtypes after
scaler.unscale_to confirm they arefloat32. - Monitor for NaNs in loss or gradients; if they appear, reduce the initial scale or adjust the loss function.
Conclusion
AMP is a low‑friction, high‑impact feature that can dramatically reduce memory usage and accelerate training on modern NVIDIA GPUs. By wrapping the forward pass in autocast, scaling the loss, and correctly unscaling before clipping, you preserve model accuracy while enjoying faster iterations. Keep an eye on hardware compatibility, unsupported ops, and the order of scaling/unscaling when integrating AMP into existing pipelines.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.