The problem
Before training a large model, you need to know whether it fits in GPU (graphics processing unit) memory, and with how many GPUs. You will compute it for the four stages of ZeRO (Zero Redundancy Optimizer).
With mixed-precision Adam, each parameter costs 16 bytes of model states:
- 2 bytes for its weight in FP16 (16-bit floating point);
- 2 bytes for its gradient in FP16;
- 12 bytes of optimizer states in FP32 (32-bit floating point): a master copy of the weight, the momentum and the variance, 4 bytes each.
Plain data parallelism stores all of it on every GPU. ZeRO shards it: it cuts one more piece per stage into parts, one per GPU (Rajbhandari et al., 2020).
The four stages
| Stage | Sharded | Bytes per GPU for parameters |
|---|---|---|
| 0 | nothing (plain data parallelism) | |
| 1 | optimizer states | |
| 2 | + gradients | |
| 3 | + weights |
where (psi) is the number of parameters and the number of GPUs. Each row adds the pieces that stay whole, at full size, to the pieces that are sharded, divided by . At stage 2, for example, only the 2 bytes of weights stay whole, and the other bytes are sharded.
Example. parameters on GPUs:
- stage 0: GB (gigabytes) per GPU;
- stage 1: GB;
- stage 2: GB;
- stage 3: GB.
The other way round: the largest model that fits
Every row is times a number of bytes per parameter that depends only on and the stage: , , , . So the largest model that fits in a given memory is the memory divided by that number, rounded down.
Example. 80 GB per GPU and :
- stage 0: parameters;
- stage 1: ;
- stage 3: .
What to implement
model_state_bytes(n_params, n_gpus, stage): the bytes per GPU, forstagein{0, 1, 2, 3}. RaiseValueErrorfor any other stage.max_params(gpu_bytes, n_gpus, stage): the largest number of parameters, anintrounded down, whose model states fit ingpu_bytesper GPU. Activations are ignored here.
Hint
Write one helper that returns the bytes per parameter for a stage and , and use it in both functions.