Files
2026-05-16 17:16:51 +08:00

446 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Megatron-LM
## 概述
Megatron-LM是NVIDIA开发的大规模Transformer训练框架专门针对超大规模语言模型的张量并行Tensor Parallelism和流水线并行Pipeline Parallelism优化。
### 核心特性
| 特性 | 说明 |
|------|------|
| 张量并行 | 实现Column切分和Row切分支持超大规模单层切分 |
| 序列并行 | 解决序列维度上的显存问题 |
| 流水线并行 | 优化的1F1B调度 |
| 混合并行 | 支持TP+PP+DP组合 |
| 通信优化 | NVLink优化AllReduce融合 |
## 张量并行实现
### ColumnParallelLinear
用于QKV投影、FFN第一个线性层
```python
class ColumnParallelLinear(torch.nn.Module):
"""
将权重按列切分每个GPU持有 (out_features / TP, in_features)
"""
def __init__(self, in_features, out_features, tp_size):
super().__init__()
self.tp_size = tp_size
self.output_size_per_partition = out_features // tp_size
# 分片参数
self.weight = torch.nn.Parameter(
torch.randn(self.output_size_per_partition, in_features)
)
def forward(self, x):
# x: (batch, seq, in_features) 完整输入
# 在每个GPU上独立计算
y = F.linear(x, self.weight)
# AllReduce合并结果因为输入相同输出应求和
# 但Column切分时输出沿hidden维度拼接不需要AllReduce
# 实际是拼接操作
return y
```
### RowParallelLinear
用于FFN第二个线性层
```python
class RowParallelLinear(torch.nn.Module):
"""
将权重按行切分每个GPU持有 (out_features, in_features / TP)
"""
def __init__(self, in_features, out_features, tp_size):
super().__init__()
self.tp_size = tp_size
self.input_size_per_partition = in_features // tp_size
self.weight = torch.nn.Parameter(
torch.randn(out_features, self.input_size_per_partition)
)
def forward(self, x):
# x被切分到各GPU (batch, seq, in_features / TP)
# 每个GPU计算部分结果
y_parallel = F.linear(x, self.weight)
# AllReduce求和因为结果需要汇总
y = tensor_model_parallel_all_reduce(y_parallel)
return y
```
### 通信操作
```python
# Tensor并行通信原语
def tensor_model_parallel_all_reduce(tensor):
"""TP域内的AllReduce"""
return torch.distributed.all_reduce(
tensor, op=torch.distributed.ReduceOp.SUM,
group=tensor_model_parallel_group
)
def broadcast_from_first_rank(tensor):
"""从TP组第一个GPU广播到所有GPU"""
return torch.distributed.broadcast(
tensor,
src=first_rank_in_group,
group=tensor_model_parallel_group
)
def all_gather_coalesced(tensor):
"""收集TP组内所有GPU的tensor"""
return torch.distributed.all_gather(
tensor, group=tensor_model_parallel_group
)
```
## 序列并行Sequence Parallelism
### 问题背景
在张量并行中LayerNorm等操作需要对完整序列做归约但各GPU只有部分数据
```
TP=8时每个GPU只有 seq_len/8 长度的数据
但LayerNorm需要 seq_len 长度的统计量
```
### 解决方案
```python
class LayerNorm(nn.Module):
"""序列并行版本的LayerNorm"""
def forward(self, x):
# x: (batch, seq/tp, hidden)
# 1. 收集完整序列用于计算均值和方差
x_gather = all_gather_sequence_parallel(x) # (batch, seq, hidden)
# 2. 计算统计量
mean = x_gather.mean(dim=-2, keepdim=True) # (batch, 1, hidden)
var = x_gather.var(dim=-2, keepdim=True) # (batch, 1, hidden)
# 3. 广播回各GPU
mean = broadcast_to_sequence_parallel_region(mean)
var = broadcast_to_sequence_parallel_region(var)
# 4. 归一化
x_norm = (x - mean) / sqrt(var + eps)
return x_norm
```
### 收益分析
```
显存节省:
- LayerNorm输入: batch × seq/tp × hidden → 节省TP倍
- Attention score: batch × heads/tp × seq/tp × seq/tp → 节省TP²倍
通信开销:
- AllGather用于归一化统计
- 与计算重叠后开销可忽略
```
## 模型切分策略
### 整体切分方案
```python
def get_tensor_model_parallel_world_size():
return tp_size
def get_pipeline_model_parallel_world_size():
return pp_size
def get_data_parallel_world_size():
return dp_size
# 计算全局GPU数
total_gpus = tp_size * pp_size * dp_size
```
### Transformer层内切分
```
Layer结构:
[Input] → [Input LayerNorm] → [Attention] → [+] → [Post Attention LayerNorm] → [MLP] → [+]
↓ ↓
Column切分(QKV) Row切分(Projection)
Column切分(MLP1) Row切分(MLP2)
```
### 各TP程度的切分方式
| TP Size | 切分层 | 说明 |
|--------|-------|------|
| 1 | 无 | 标准模型 |
| 2 | QKV, MLP1 | 2路切分 |
| 4 | QKV, MLP1 | 4路切分 |
| 8 | QKV, MLP1 | 8路切分需NVLink |
### 负载均衡
```
切分策略选择原则:
1. 计算量均衡每GPU计算量接近
2. 通信均衡:避免通信成为瓶颈
3. 显存均衡各GPU峰值显存接近
对于Transformer
- Attention: 计算量 ∝ 4×batch×seq²×heads×head_dim
- MLP: 计算量 ∝ 6×batch×seq×hidden×intermediate
均衡切分需考虑注意力头的划分
```
## 与DeepSpeed对比
| 特性 | Megatron-LM | DeepSpeed |
|------|------------|-----------|
| 张量并行 | 原生支持,优化完善 | 支持但非核心focus |
| 流水线并行 | 支持,优化调度 | 支持 |
| ZeRO | 不支持使用FSDP替代 | 完整实现 |
| 序列并行 | 原生支持 | 有限支持 |
| CPU Offload | 有限 | 完善 |
| 3D并行 | TP+PP原生DP需配合 | 完整支持 |
| 适用场景 | 超大规模单节点/少数节点 | 大规模多节点 |
### 架构差异
```
Megatron-LM设计哲学
- 张量并行是核心,专注于单层内的高效切分
- 认为通信是瓶颈优化AllReduce模式
- 对NVLink等高速互联友好
DeepSpeed设计哲学
- 数据并行是核心ZeRO解决显存问题
- 通过分片减少通信需求
- 通过Offload支持超大规模
```
## Megatron核心实现
### 并行注意力
```python
class ParallelSelfAttention(nn.Module):
"""
序列并行的自注意力
"""
def __init__(self, hidden_size, num_heads, tp_size):
super().__init__()
self.tp_size = tp_size
self.num_heads_per_partition = num_heads // tp_size
# QKV投影Column切分
self.query_key_value = ColumnParallelLinear(
hidden_size,
3 * hidden_size, # Q, K, V 拼接
tp_size
)
# 输出投影Row切分
self.dense = RowParallelLinear(
hidden_size,
hidden_size,
tp_size
)
def forward(self, x, attention_mask=None):
# x: (batch, seq/tp, hidden)
# QKV计算
qkv = self.query_key_value(x)
q, k, v = qkv.split(hidden_size, dim=-1)
# 收集完整K, V用于跨TP组计算注意力
# 需要AllGather获取其他GPU的K, V
k = all_gather_kv_along_seq_dim(k)
v = all_gather_kv_along_seq_dim(v)
# 注意力计算
attn_output = scaled_dot_product_attention(q, k, v)
# 输出投影
output = self.dense(attn_output)
return output
```
### 环形通信优化
```python
def ring_send_recv(k_chunks, v_chunks):
"""
环形通信计算注意力
用于减少AllGather通信量
"""
num_chunks = len(k_chunks)
local_k, local_v = k_chunks[0], v_chunks[0]
outputs = []
for i in range(num_chunks):
# 计算当前chunk的注意力
attn = scaled_dot_product_attention(q, local_k, local_v)
outputs.append(attn)
if i < num_chunks - 1:
# 接收下一个chunk环形
recv_idx = (rank + 1) % num_chunks
local_k = k_chunks[recv_idx]
local_v = v_chunks[recv_idx]
return outputs
```
### Megatron训练配置
```python
# Megatron训练配置示例
megatron_config = {
"tensor_model_parallel_size": 8,
"pipeline_model_parallel_size": 4,
"num_layers": 32,
"hidden_size": 4096,
"num_attention_heads": 32,
"ffn_hidden_size": 16384,
"micro_batch_size": 4,
"global_batch_size": 1024,
"lr": 1e-4,
"seq_len": 2048,
"bf16": True
}
# 启动训练
# python train.py --config megatron_config.yaml
```
### 分布式优化器
```python
# Megatron的分布式优化器
# 每个GPU持有优化器状态的1/TP分片
class MegatronOptimizer:
"""
分片优化器状态
"""
def __init__(self, model, optimizer_class, tp_size):
self.tp_size = tp_size
# 优化器状态按TP分片
for param in model.parameters():
param.state_full = None # 未分片的完整状态
param.state_sharded = self.shard_tensor(param) # 分片后状态
def shard_tensor(self, tensor):
# 沿第一个维度切分
shard_size = tensor.shape[0] // self.tp_size
shard = tensor[shard_size * rank : shard_size * (rank + 1)]
return shard
def step(self):
# 更新参数
for param in self.params:
# 本地更新
param_sharded = self.update_local(param.state_sharded)
# AllReduce同步更新
param_updated = all_reduce(param_sharded)
# 写回完整参数(如需要)
```
## 最佳实践
### 硬件配置建议
| GPU数 | TP | PP | 备注 |
|-------|----|----|------|
| 8 | 8 | 1 | 最大TP单节点 |
| 16 | 8 | 2 | TP+PP组合 |
| 32+ | 8 | 4 | 需要网络优化 |
### 通信优化
```python
# 启用融合通信
model = parallel_model(model)
# 使用NVLink通信
# 确认 NCCL_ALGO=RING
# 确认 NCCL_IB_HCA=mlx5_0,mlx5_1
```
### 调试技巧
```bash
# 查看张量并行等级
echo $NV_TENSOR_MODEL_PARALLEL_SIZE
# 查看流水线等级
echo $PIPELINE_MODEL_PARALLEL_SIZE
# 检查通信性能
python -c "import torch.distributed as dist; dist.is_available()"
# 启用详细日志
export PYTHONFAULTHANDLER=1
export NCCL_DEBUG=INFO
```
### 性能监控
```python
# Megatron性能指标
class PerformanceMonitor:
"""监控TP训练的性能指标"""
def __init__(self):
self.forward_time = []
self.backward_time = []
self.comm_time = []
def record_forward(self, duration):
self.forward_time.append(duration)
def compute_stats(self):
return {
"avg_forward": mean(self.forward_time),
"avg_backward": mean(self.backward_time),
"throughput": batch_size * seq_len / mean(self.forward_time)
}
```
## 框架生态
```
NVIDIA NGC Container: 包含优化好的Megatron镜像
├── Megatron-LM (主干)
├── NeMo-Megatron (企业级封装)
├── PyTorch Lightning (集成)
└── Triton (算子优化)
```
## 常见问题
| 问题 | 原因 | 解决方案 |
|------|------|---------|
| TP通信瓶颈 | 跨节点TP | 限制TP在节点内 |
| 梯度不一致 | AllReduce未同步 | 检查梯度分片逻辑 |
| 显存不均 | 参数未均匀切分 | 检查TP分片 |
| 速度慢 | Occupancy低 | 调整batch size/seq len |