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

  1. Model Parameters: The weights themselves
  2. Gradients: Same size as parameters
  3. Optimizer States: For Adam optimizer, 2x parameter size (momentum + variance)
  4. 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:

  1. Process mini-batches
  2. Accumulate gradients without updating
  3. Update model after N mini-batches
  4. 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?

  1. First: Try DeepSpeed ZeRO-1 or ZeRO-2
  2. Still OOM: Enable gradient checkpointing
  3. Still OOM: Try ZeRO-3
  4. Last resort: CPU offloading (will be slow)

Speed Issues?

  1. Use Flash Attention (almost always beneficial)
  2. Try Liger Kernel for fused operations
  3. Use torch.compile for graph optimization

Tools Summary

ToolPurposeDifficulty
DeepSpeedMulti-GPU, memory optimizationMedium
Flash AttentionFast attention, less memoryEasy
Liger KernelFused kernelsEasy
QuantizationReduce precisionEasy

Key Takeaways

  1. Know your bottleneck: Is it parameters, activations, or optimizer states?
  2. Start simple: Don’t over-engineer; add complexity only when needed
  3. Flash Attention is essential: Use it for any serious LLM work
  4. DeepSpeed ZeRO scales: From single GPU to hundreds

台大李宏毅教授”生成式AI时代下的机器学习(2025)“课程助教课笔记。

为什么需要多GPU训练?

大型语言模型确实很大。一个8B参数的模型需要:

  • 仅模型权重就需要约32GB(FP32)
  • 还需要额外内存用于梯度、优化器状态和激活值
  • 总计:训练需要128GB+

单个GPU(即使是80GB的H100)通常无法容纳训练所需的所有内容。

LLM训练中的内存组成

  1. 模型参数:权重本身
  2. 梯度:与参数大小相同
  3. 优化器状态:对于Adam优化器,是参数大小的2倍(动量+方差)
  4. 激活值:每层的中间输出(对于长上下文可能非常大)

第一部分:优化参数、梯度和优化器状态

DeepSpeed ZeRO(零冗余优化器)

微软的DeepSpeed提供三个级别的内存优化:

ZeRO-1:在GPU之间分割优化器状态

  • 每个GPU存储1/N的优化器状态
  • 最小的通信开销

ZeRO-2:分割优化器状态+梯度

  • 进一步减少内存
  • 稍多的通信

ZeRO-3:分割所有内容(优化器+梯度+参数)

  • 最大的内存节省
  • 最多的通信开销

CPU卸载

当GPU内存仍然不足时:

  • 将优化器状态移到CPU RAM
  • 将梯度移到CPU
  • 权衡:由于CPU-GPU传输,训练会变慢

梯度累积

当批量大小受内存限制时:

  1. 处理小批量
  2. 累积梯度而不更新
  3. 在N个小批量后更新模型
  4. 有效批量大小 = 小批量 × 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位模型常用于部署
  • 显著减少内存,质量损失最小

实用建议

内存问题?

  1. 首先:尝试DeepSpeed ZeRO-1或ZeRO-2
  2. 仍然OOM:启用梯度检查点
  3. 仍然OOM:尝试ZeRO-3
  4. 最后手段:CPU卸载(会很慢)

速度问题?

  1. 使用Flash Attention(几乎总是有益的)
  2. 尝试Liger Kernel进行融合操作
  3. 使用torch.compile进行图优化

工具总结

工具用途难度
DeepSpeed多GPU,内存优化中等
Flash Attention快速注意力,更少内存简单
Liger Kernel融合内核简单
量化降低精度简单

关键要点

  1. 了解你的瓶颈:是参数、激活值还是优化器状态?
  2. 从简单开始:不要过度工程化;只在需要时增加复杂性
  3. Flash Attention是必需的:任何严肃的LLM工作都要使用它
  4. DeepSpeed ZeRO可扩展:从单GPU到数百个GPU