The Anatomy of GPU Memory

GPU memory is not a monolithic block. To predict usage, you must categorize memory into static and dynamic components. Static memory includes your model weights, which stay constant throughout the session. Dynamic memory includes gradients, optimizer states, and activations, which fluctuate based on your training configuration.

Training a transformer model typically requires significantly more VRAM than inference. For instance, while inference might only require 2 bytes per parameter in half-precision, training can easily demand 16 to 20 bytes per parameter when using the Adam optimizer.

  • Model Weights: The parameters of your neural network. In FP32, each parameter takes 4 bytes. In BF16 or FP16, it takes 2 bytes.
  • Gradients: During training, PyTorch stores a gradient for every trainable parameter. These usually match the precision of the weights.
  • Optimizer States: This is often the largest hidden cost. The Adam optimizer stores two additional states (momentum and variance) for every parameter, typically in FP32 for numerical stability.
  • Activations: These are the intermediate outputs of each layer stored during the forward pass to calculate gradients during the backward pass.

The Math of Memory Estimation

Predicting VRAM starts with the precision of your tensors. The base cost for model weights depends on the data type, and Hugging Face’s accelerate estimate-memory tool reports the same per-dtype figures for any model on the Hub [6]:

PrecisionBytes per ParameterExample: 7B Model Weights
FP32 (Full)4 Bytes28.0 GB
FP16 / BF16 (Half)2 Bytes14.0 GB
INT8 (Quantized)1 Byte7.0 GB
INT4 (Quantized)0.5 Bytes3.5 GB

For inference, the formula is straightforward: Total VRAM = (Parameters * Bytes per Parameter) * 1.2. The 1.2 multiplier is a published heuristic [1] that accounts for CUDA kernels and the KV cache in LLMs, and it holds only at batch size 1 and short context. For training, the math becomes more complex. If you are using mixed-precision training with the AdamW optimizer, your per-parameter cost looks like this:

  1. Model Weights (FP16): 2 bytes
  2. Gradients (FP16): 2 bytes
  3. Optimizer States (FP32): 8 bytes (4 for momentum, 4 for variance)
  4. Master Weights (FP32): 4 bytes (used in mixed precision to prevent underflow)

That is 16 bytes per parameter before activations, the standard mixed-precision AdamW figure, of which the optimizer state accounts for 12 [1]. A 7B model would therefore need roughly 112 GB of VRAM for the static components alone, which calls for multi-GPU setups or sharding techniques like FSDP.

Activations: The Silent Killer

While weights are predictable, activations are volatile. They scale linearly with your batch size and sequence length. In deep networks, activations often exceed the size of the model itself. A large batch size can increase peak VRAM usage several-fold compared to a batch size of one.

With full activation recomputation in half precision, a published lower bound for transformer activation memory is 2 * seq_length * batch_size * hidden_size * num_layers bytes [1]; without recomputation it grows several-fold, since each layer then retains its attention and MLP intermediates. FlashAttention-3, released in July 2024 and optimized for Hopper GPUs such as the H100 [5], lets you reduce the memory footprint of the attention matrix, and its successor kernel extends the technique to Blackwell. The linear layers still produce large activation maps either way.

To mitigate this, engineers use gradient checkpointing. This technique discards activations during the forward pass and recomputes them during the backward pass. It trades a 20-30 percent increase in computation time for a massive reduction in VRAM, often allowing you to double your batch size on the same hardware.

Profiling with Terminal Precision

Theoretical formulas provide a baseline, but real-world performance requires empirical data. PyTorch provides built-in tools to inspect memory allocation without external dependencies. The most direct method is using torch.cuda.memory_summary(), which provides a detailed breakdown of reserved versus allocated memory.

For a deeper dive, torch.cuda.memory._record_memory_history() followed by a snapshot dump exports a trace of every allocation with stack traces [3]. You can drag the pickled snapshot into the PyTorch memory visualizer to identify fragmentation. Fragmentation occurs when small tensors are scattered across the VRAM, preventing the allocation of large contiguous blocks even if the total free memory appears sufficient.

Try a cold-start profiling strategy: run one full training step, then call torch.cuda.max_memory_allocated(). This captures peak usage, which usually occurs during the optimizer step when weights, gradients, and states coexist in memory. Relying on nvidia-smi alone is often misleading, as it shows the memory reserved by the PyTorch caching allocator rather than the memory actively used by your tensors [2].

Optimization for Sovereign Clouds

Efficiency drives digital sovereignty. By reducing the VRAM footprint of your models, you decrease reliance on massive, proprietary GPU clusters and enable deployment on localized European infrastructure. Several techniques are now standard for high-performance AI engineering.

  • Quantized Optimizers: Using 8-bit optimizers via libraries like bitsandbytes can reduce optimizer state memory from 8 bytes per parameter to 2 bytes, a 75 percent saving at 32-bit accuracy [7].
  • Gradient Accumulation: If your batch size is too large for your VRAM, split it into smaller micro-batches. You maintain the mathematical gradient of a large batch without the activation cost.
  • Distributed Data Parallel (DDP): For multi-node setups, DDP is more memory-efficient than the older DataParallel, as it avoids redundant model replicas on the primary device.

By using these strategies, European enterprises can run state-of-the-art models on sovereign GPU zones, ensuring data residency while maintaining performance.

Sources

[1] EleutherAI: Transformer Math 101; [2] PyTorch Docs: CUDA Semantics, Memory Management; [3] PyTorch Docs: Understanding CUDA Memory Usage; [4] PyTorch: Understanding GPU Memory 1, Visualizing All Allocations over Time; [5] PyTorch: FlashAttention-3, Fast and Accurate Attention with Asynchrony and Low-precision; [6] Hugging Face Accelerate: Model Memory Estimator; [7] Dettmers et al.: 8-bit Optimizers via Block-wise Quantization