Notes from the TA session of Professor Hung-yi Lee’s (李宏毅) course “Machine Learning in the Era of Generative AI (2025)” at National Taiwan University.
Why Multi-GPU Training?
Large language models are… large. An 8B parameter model requires:
- ~32GB just for model weights (FP32)
- Additional memory for gradients, optimizer states, and activations
- Total: ~128GB+ for training
A single GPU (even an 80GB H100) often can’t fit everything needed for training.
Memory Components in LLM Training
- Model Parameters: The weights themselves
- Gradients: Same size as parameters
- Optimizer States: For Adam optimizer, 2x parameter size (momentum + variance)
- Activations: Intermediate outputs from each layer (can be huge for long contexts)
Part 1: Optimizing Parameters, Gradients, and Optimizer States
DeepSpeed ZeRO (Zero Redundancy Optimizer)
Microsoft’s DeepSpeed provides three levels of memory optimization:
ZeRO-1: Partition optimizer states across GPUs
- Each GPU stores 1/N of optimizer states
- Minimal communication overhead
ZeRO-2: Partition optimizer states + gradients
- Further memory reduction
- Slightly more communication
ZeRO-3: Partition everything (optimizer + gradients + parameters)
- Maximum memory savings
- Most communication overhead
CPU Offloading
When GPU memory is still insufficient:
- Move optimizer states to CPU RAM
- Move gradients to CPU
- Trade-off: Slower training due to CPU-GPU transfer
Gradient Accumulation
When batch size is limited by memory:
- Process mini-batches
- Accumulate gradients without updating
- Update model after N mini-batches
- Effective batch size = mini-batch × N
Part 2: Optimizing Activations
The Activation Problem
For long context training (32K+ tokens), activations dominate memory usage:
- Each layer stores intermediate outputs
- Attention computation is O(N²) in sequence length
Flash Attention
A revolutionary kernel that:
- Rewrites attention computation for GPU efficiency
- Reduces memory from O(N²) to O(N)
- Significantly faster than naive implementation
Key insight: Standard attention is memory-bound, not compute-bound. Flash Attention optimizes memory access patterns.
Liger Kernel
An open-source project providing fused kernels for common LLM operations:
- Combines multiple operations into single GPU kernels
- Reduces memory transfers
- Easy to use: just add a decorator
Activation Checkpointing (Gradient Checkpointing)
Trade compute for memory:
- Don’t store all activations during forward pass
- Recompute activations during backward pass as needed
- Significant memory savings with modest compute overhead
Part 3: Quantization
What is Quantization?
Reduce precision of stored values:
- FP32 (32-bit) → FP16 (16-bit) → INT8 (8-bit) → INT4 (4-bit)
- Lossy compression but often acceptable accuracy loss
Mixed Precision Training
- Store master weights in FP32
- Compute forward/backward in FP16
- Best of both worlds: memory savings + numerical stability
Quantization for Inference
Even more aggressive quantization possible:
- 8-bit and 4-bit models common for deployment
- Significant memory reduction with minimal quality loss
Practical Recommendations
Memory Issues?
- First: Try DeepSpeed ZeRO-1 or ZeRO-2
- Still OOM: Enable gradient checkpointing
- Still OOM: Try ZeRO-3
- Last resort: CPU offloading (will be slow)
Speed Issues?
- Use Flash Attention (almost always beneficial)
- Try Liger Kernel for fused operations
- Use
torch.compilefor graph optimization
Tools Summary
| Tool | Purpose | Difficulty |
|---|---|---|
| DeepSpeed | Multi-GPU, memory optimization | Medium |
| Flash Attention | Fast attention, less memory | Easy |
| Liger Kernel | Fused kernels | Easy |
| Quantization | Reduce precision | Easy |
Key Takeaways
- Know your bottleneck: Is it parameters, activations, or optimizer states?
- Start simple: Don’t over-engineer; add complexity only when needed
- Flash Attention is essential: Use it for any serious LLM work
- DeepSpeed ZeRO scales: From single GPU to hundreds
台大李宏毅教授”生成式AI时代下的机器学习(2025)“课程助教课笔记。
为什么需要多GPU训练?
大型语言模型确实很大。一个8B参数的模型需要:
- 仅模型权重就需要约32GB(FP32)
- 还需要额外内存用于梯度、优化器状态和激活值
- 总计:训练需要128GB+
单个GPU(即使是80GB的H100)通常无法容纳训练所需的所有内容。
LLM训练中的内存组成
- 模型参数:权重本身
- 梯度:与参数大小相同
- 优化器状态:对于Adam优化器,是参数大小的2倍(动量+方差)
- 激活值:每层的中间输出(对于长上下文可能非常大)
第一部分:优化参数、梯度和优化器状态
DeepSpeed ZeRO(零冗余优化器)
微软的DeepSpeed提供三个级别的内存优化:
ZeRO-1:在GPU之间分割优化器状态
- 每个GPU存储1/N的优化器状态
- 最小的通信开销
ZeRO-2:分割优化器状态+梯度
- 进一步减少内存
- 稍多的通信
ZeRO-3:分割所有内容(优化器+梯度+参数)
- 最大的内存节省
- 最多的通信开销
CPU卸载
当GPU内存仍然不足时:
- 将优化器状态移到CPU RAM
- 将梯度移到CPU
- 权衡:由于CPU-GPU传输,训练会变慢
梯度累积
当批量大小受内存限制时:
- 处理小批量
- 累积梯度而不更新
- 在N个小批量后更新模型
- 有效批量大小 = 小批量 × N
第二部分:优化激活值
激活值问题
对于长上下文训练(32K+令牌),激活值主导内存使用:
- 每层存储中间输出
- 注意力计算在序列长度上是O(N²)
Flash Attention
一个革命性的内核:
- 为GPU效率重写注意力计算
- 将内存从O(N²)减少到O(N)
- 比朴素实现快得多
关键洞察:标准注意力是内存受限的,而非计算受限的。Flash Attention优化了内存访问模式。
Liger Kernel
一个提供融合内核的开源项目,用于常见的LLM操作:
- 将多个操作组合成单个GPU内核
- 减少内存传输
- 易于使用:只需添加一个装饰器
激活检查点(梯度检查点)
用计算换内存:
- 前向传播时不存储所有激活值
- 反向传播时根据需要重新计算激活值
- 显著的内存节省,计算开销适中
第三部分:量化
什么是量化?
降低存储值的精度:
- FP32(32位)→ FP16(16位)→ INT8(8位)→ INT4(4位)
- 有损压缩,但通常精度损失可接受
混合精度训练
- 以FP32存储主权重
- 以FP16计算前向/反向传播
- 两全其美:内存节省+数值稳定性
推理量化
可以进行更激进的量化:
- 8位和4位模型常用于部署
- 显著减少内存,质量损失最小
实用建议
内存问题?
- 首先:尝试DeepSpeed ZeRO-1或ZeRO-2
- 仍然OOM:启用梯度检查点
- 仍然OOM:尝试ZeRO-3
- 最后手段:CPU卸载(会很慢)
速度问题?
- 使用Flash Attention(几乎总是有益的)
- 尝试Liger Kernel进行融合操作
- 使用
torch.compile进行图优化
工具总结
| 工具 | 用途 | 难度 |
|---|---|---|
| DeepSpeed | 多GPU,内存优化 | 中等 |
| Flash Attention | 快速注意力,更少内存 | 简单 |
| Liger Kernel | 融合内核 | 简单 |
| 量化 | 降低精度 | 简单 |
关键要点
- 了解你的瓶颈:是参数、激活值还是优化器状态?
- 从简单开始:不要过度工程化;只在需要时增加复杂性
- Flash Attention是必需的:任何严肃的LLM工作都要使用它
- DeepSpeed ZeRO可扩展:从单GPU到数百个GPU