大規模モデル分散学習実践:DeepSpeed ZeRO、FSDPとマルチGPU最適化
AI与大数据
概要
- 大規模モデル学習のメモリボトルネック:7BモデルFP16では28GBパラメータ+14GB勾配+14GBオプティマイザ=56GBが必要、単一A100 80GBでは学習不可
- DeepSpeed ZeRO-3はパラメータ分割によりメモリ使用量を8倍削減、64GPUで70Bモデルの学習が可能
- PyTorch FSDPはMeta公式ソリューション、PyTorchエコシステムとシームレスに統合、2026年には主流の選択肢に
- 3D並列(データ+テンソル+パイプライン)は超大規模モデル学習の標準、Megatron-LMがデファクトスタンダード
- 本記事では単一マシンマルチGPUから千GPUクラスタまでの完全な学習ソリューションを提供、Slurmスケジューリングと障害復旧も含む
目次
- 大規模モデル学習のメモリボトルネック
- DeepSpeed ZeRO:3段階メモリ最適化
- PyTorch FSDP:Meta公式ソリューション
- 3D並列:データ+テンソル+パイプライン
- マルチノード学習と障害復旧
- まとめと関連記事
大規模モデル学習のメモリボトルネック
学習メモリの内訳
┌──────────────────────────────────────────────────────────────┐
│ 大規模モデル学習メモリ内訳 │
│ │
│ モデルパラメータ(Weights): 2 × params × dtype_size │
│ 勾配(Gradients): 2 × params × dtype_size │
│ オプティマイザ状態(Adam): 12 × params × dtype_size (FP32)│
│ 活性化値(Activations): batch × seq_len × hidden × layers│
│ │
│ 7BモデルFP16学習メモリ: │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ パラメータ: 14GB │ │
│ │ 勾配: 14GB │ │
│ │ オプティマイザ: 56GB (FP32のmとv) │ │
│ │ 活性化値: 8-16GB (batchとseq_lenに依存) │ │
│ │ ───────────────────────── │ │
│ │ 合計: 92-100GB ❌ 単一A100 80GBを超過 │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
モデル規模別学習要件
| モデル | パラメータ数 | 学習メモリ(FP16+Adam) | 最低GPU構成 | 推奨構成 |
|---|---|---|---|---|
| 1.8B | 1.8B | 26GB | 1×A100 40GB | 1×A100 80GB |
| 7B | 7B | 100GB | 2×A100 80GB | 4×A100 80GB |
| 13B | 13B | 186GB | 4×A100 80GB | 8×A100 80GB |
| 70B | 70B | 1TB | 16×A100 80GB | 32×H100 80GB |
| 671B | 671B | 9.6TB | 128×H100 80GB | 256×H100 80GB |
DeepSpeed ZeRO:3段階メモリ最適化
ZeRO 3段階の原理
┌──────────────────────────────────────────────────────────────┐
│ ZeRO 3段階メモリ最適化 │
│ │
│ ZeRO-1: オプティマイザ状態分割 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 各GPUはオプティマイザ状態(mとv)の1/Nのみを保存 │ │
│ │ メモリ削減: 4× (オプティマイザが最大の割合) │ │
│ │ 通信量: DDPと同じ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ZeRO-2: オプティマイザ+勾配分割 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 各GPUはオプティマイザ状態+勾配の1/Nのみを保存 │ │
│ │ メモリ削減: 8× │ │
│ │ 通信量: DDPと同じ(勾配reduce-scatter) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ZeRO-3: オプティマイザ+勾配+パラメータ分割 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 各GPUは全状態の1/Nのみを保存 │ │
│ │ メモリ削減: N× (N=GPU数) │ │
│ │ 通信量: 1.5×増加(all-gatherパラメータが必要) │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
DeepSpeed ZeRO-3学習設定
{
"train_batch_size": 128,
"train_micro_batch_size_per_gpu": 2,
"gradient_accumulation_steps": 8,
"zero_optimization": {
"stage": 3,
"offload_optimizer": {
"device": "cpu",
"pin_memory": true
},
"offload_param": {
"device": "cpu",
"pin_memory": true
},
"overlap_comm": true,
"contiguous_gradients": true,
"sub_group_size": 1e9,
"reduce_bucket_size": "auto",
"stage3_prefetch_bucket_size": "auto",
"stage3_param_persistence_threshold": "auto",
"stage3_max_live_parameters": 1e9,
"stage3_max_reuse_distance": 1e9,
"stage3_gather_16bit_weights_on_model_save": true
},
"bf16": {
"enabled": true
},
"gradient_clipping": 1.0,
"prescale_gradients": false,
"wall_clock_breakdown": false
}
ZeRO各段階のメモリ比較(7Bモデル、8×A100 80GB)
| 段階 | パラメータ | 勾配 | オプティマイザ | 活性化値 | 合計 | GPU当たり |
|---|---|---|---|---|---|---|
| DDP | 14GB | 14GB | 56GB | 12GB | 96GB | 96GB |
| ZeRO-1 | 14GB | 14GB | 7GB | 12GB | 47GB | 47GB |
| ZeRO-2 | 14GB | 1.75GB | 7GB | 12GB | 35GB | 35GB |
| ZeRO-3 | 1.75GB | 0.22GB | 0.88GB | 12GB | 15GB | 15GB |
DeepSpeed起動スクリプト
deepspeed --num_gpus=8 \
--num_nodes=2 \
--master_addr=$MASTER_ADDR \
--master_port=29500 \
train.py \
--model_name_or_path Qwen/Qwen2.5-7B \
--data_path ./data/train.jsonl \
--bf16 \
--deepspeed ds_config_z3.json \
--per_device_train_batch_size 2 \
--gradient_accumulation_steps 8 \
--learning_rate 2e-5 \
--num_train_epochs 3 \
--warmup_ratio 0.03 \
--lr_scheduler_type cosine \
--weight_decay 0.01 \
--save_strategy epoch \
--output_dir ./output \
--gradient_checkpointing true \
--logging_steps 10 \
--report_to tensorboard
PyTorch FSDP:Meta公式ソリューション
FSDP vs DeepSpeed ZeRO
| 項目 | DeepSpeed ZeRO-3 | PyTorch FSDP |
|---|---|---|
| 開発元 | Microsoft | Meta |
| 統合度 | 独立ライブラリ | PyTorchネイティブ |
| パラメータ分割 | ✅ | ✅ |
| 勾配分割 | ✅ | ✅ |
| オプティマイザ分割 | ✅ | ✅ |
| CPUオフロード | ✅ | ✅ |
| 混合精度 | ✅ | ✅ |
| パイプライン並列 | ✅ (DeepSpeed PP) | ⚠️ (手動設定が必要) |
| デバッグのしやすさ | 中 | 高(PyTorchネイティブ) |
| コミュニティの活発さ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
FSDP学習コード
import torch
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
from torch.distributed.fsdp import ShardingStrategy, MixedPrecision
from torch.distributed.fsdp.wrap import transformer_auto_wrap_policy
from transformers import AutoModelForCausalLM, AutoTokenizer
torch.distributed.init_process_group(backend="nccl")
local_rank = int(os.environ["LOCAL_RANK"])
torch.cuda.set_device(local_rank)
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2.5-7B",
torch_dtype=torch.bfloat16,
device_map={"": local_rank},
)
mp_policy = MixedPrecision(
param_dtype=torch.bfloat16,
reduce_dtype=torch.bfloat16,
buffer_dtype=torch.bfloat16,
)
auto_wrap_policy = transformer_auto_wrap_policy(
transformer_layer_names=["Qwen2DecoderLayer"],
)
model = FSDP(
model,
sharding_strategy=ShardingStrategy.FULL_SHARD,
mixed_precision=mp_policy,
auto_wrap_policy=auto_wrap_policy,
device_id=local_rank,
use_orig_params=True,
)
optimizer = torch.optim.AdamW(model.parameters(), lr=2e-5, weight_decay=0.01)
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=1000)
for epoch in range(num_epochs):
for batch in dataloader:
batch = {k: v.to(local_rank) for k, v in batch.items()}
outputs = model(**batch)
loss = outputs.loss
loss.backward()
optimizer.step()
scheduler.step()
optimizer.zero_grad()
3D並列:データ+テンソル+パイプライン
3D並列アーキテクチャ
┌──────────────────────────────────────────────────────────────┐
│ 3D並列アーキテクチャ │
│ │
│ データ並列(DP): 4グループ × グループあたり4GPU │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ DPグループ0 DPグループ1 DPグループ2 DPグループ3│ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────┐│ │
│ │ │ TP=2 │ │ TP=2 │ │ TP=2 │ │TP=2 ││ │
│ │ │ PP=2 │ │ PP=2 │ │ PP=2 │ │PP=2 ││ │
│ │ │ ┌──┬──┐ │ │ ┌──┬──┐ │ │ ┌──┬──┐ │ │┌──┬─┐││ │
│ │ │ │G0│G1│ │ │ │G4│G5│ │ │ │G8│G9│ │ ││12│13│││ │
│ │ │ ├──┼──┤ │ │ ├──┼──┤ │ │ ├──┼──┤ │ │├──┼──┤││ │
│ │ │ │G2│G3│ │ │ │G6│G7│ │ │ │10│11│ │ ││14│15│││ │
│ │ │ └──┴──┘ │ │ └──┴──┘ │ │ └──┴──┘ │ │└──┴──┘││ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────┘│ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ TP=テンソル並列(レイヤ内分割) PP=パイプライン並列(レイヤ間分割)│
│ DP=データ並列(データ分割) 合計16GPU │
└──────────────────────────────────────────────────────────────┘
3D並列選択ガイド
| モデル規模 | GPU数 | 推奨並列戦略 | 説明 |
|---|---|---|---|
| 7B | 8 | DP=8 | データ並列のみで十分 |
| 13B | 16 | DP=8, TP=2 | テンソル並列を追加 |
| 70B | 64 | DP=8, TP=4, PP=2 | 3D並列 |
| 671B | 256 | DP=16, TP=8, PP=2 | 3D並列+ZeRO |
マルチノード学習と障害復旧
Slurm起動スクリプト
#!/bin/bash
#SBATCH --job-name=llm-train
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=8
#SBATCH --gres=gpu:8
#SBATCH --cpus-per-task=8
#SBATCH --mem=512G
#SBATCH --time=72:00:00
#SBATCH --partition=gpu-a100
export MASTER_ADDR=$(scontrol show hostname $SLURM_JOB_NODELIST | head -n1)
export MASTER_PORT=29500
export NCCL_DEBUG=INFO
export NCCL_IB_DISABLE=0
export NCCL_IB_HCA=mlx5
srun torchrun \
--nnodes=$SLURM_JOB_NUM_NODES \
--nproc_per_node=8 \
--rdzv_id=$SLURM_JOB_ID \
--rdzv_backend=c10d \
--rdzv_endpoint=$MASTER_ADDR:$MASTER_PORT \
train.py \
--model_name_or_path Qwen/Qwen2.5-7B \
--deepspeed ds_config_z3.json \
--per_device_train_batch_size 2 \
--gradient_accumulation_steps 8 \
--learning_rate 2e-5 \
--num_train_epochs 3 \
--output_dir ./output \
--save_strategy steps \
--save_steps 500 \
--save_total_limit 5
学習障害復旧
from transformers import Trainer
class FaultTolerantTrainer(Trainer):
def _save_checkpoint(self, model, trial, metrics=None):
super()._save_checkpoint(model, trial, metrics)
self._save_training_state()
def _save_training_state(self):
state = {
"global_step": self.state.global_step,
"epoch": self.state.epoch,
"rng_state": torch.cuda.get_rng_state().cpu().numpy().tolist(),
}
with open(f"{self.args.output_dir}/training_state.json", "w") as f:
json.dump(state, f)
def _load_training_state(self):
state_path = f"{self.args.output_dir}/training_state.json"
if os.path.exists(state_path):
with open(state_path) as f:
state = json.load(f)
return state
return None
学習パフォーマンス最適化チェックリスト
| 最適化項目 | 効果 | 設定 |
|---|---|---|
| Flash Attention 2 | 学習速度+30% | attn_implementation="flash_attention_2" |
| 勾配チェックポイント | メモリ-40% | gradient_checkpointing=True |
| BF16混合精度 | メモリ-50% | bf16=True |
| NCCL IB通信 | 通信遅延-60% | NCCL_IB_DISABLE=0 |
| シャードパラメータプリフェッチ | 通信計算オーバーラップ | stage3_prefetch_bucket_size |
| 勾配蓄積 | 大batchと同等 | gradient_accumulation_steps=8 |
まとめと関連記事
大規模モデル分散学習の核心はメモリ最適化と通信最適化です。DeepSpeed ZeRO-3はパラメータ分割によりメモリをN倍削減し、FSDPはPyTorchネイティブソリューションでデバッグが容易、3D並列は超大規模モデルの標準です。
学習のポイント振り返り:
- ZeRO-3はメモリ最適化の究極のソリューションですが、通信量は1.5倍増加します
- FSDPはPyTorchネイティブソリューションで、2026年には主流の選択肢となっています
- 3D並列:7BはDP、13BはTP追加、70B+はDP+TP+PP
- Flash Attention 2 + 勾配チェックポイントは学習の必須設定です
- 障害復旧とチェックポイントは長時間学習に不可欠です
関連記事:
- 大規模モデルファインチューニング実践:LoRA、QLoRAとRLHF — 学習後のファインチューニング最適化
- AIチップ推論デプロイ実践 — 学習完了後の推論デプロイ
- 大規模モデルデータフライホイール実践 — 学習データの自動収集
権威ある参考文献:
ブラウザローカルツールを無料で試す →
#大模型分布式训练#DeepSpeed ZeRO#FSDP训练#Megatron-LM#多GPU训练优化#2026