Что такое DeepSpeed ZeRO-Offload и когда он полезен?
Краткий тезис
DeepSpeed ZeRO-Offload — это техника распределённого обучения, которая выгружает optimizer state и gradients из памяти GPU в CPU RAM (или NVMe), оставляя на GPU только веса модели и вычисления forward/backward. Это позволяет обучать модели с десятками миллиардов параметров даже на одном GPU с ограниченной памятью (например, 70B на 1× A100 80GB). Цена — снижение скорости обучения в 2–5 раз из-за передачи данных между CPU и GPU.
1. Проблема: память GPU при обучении больших моделей
Обучение большой языковой модели (LLM) требует огромного объёма видеопамяти. Основные компоненты, занимающие память:
- Веса модели (model weights):
Nпараметров × 2 байта (FP16) =2Nбайт. - Градиенты (gradients): ещё
2Nбайт. - Optimizer state (состояние оптимизатора, например Adam): хранит два момента (m и v) — ещё
4Nбайт (в FP32) или2N(в FP16 precision precision precision training|mixed precision). - Активации (activations) для backward pass — могут быть огромными, но их можно сжимать через gradient checkpointing.
Итого для модели 70B параметров в mixed precision (FP16 веса + FP32 optimizer):
- Веса: 70B × 2 = 140 GB
- Градиенты: 70B × 2 = 140 GB
- Optimizer state: 70B × 4 × 2 = 560 GB (m и v в FP32)
- Всего: ~840 GB — невозможно разместить даже на 8× A100 80GB (640 GB).
Нужны методы распределения памяти между устройствами.
2. DeepSpeed ZeRO: три стадии оптимизации памяти
ZeRO (Zero Redundancy Optimizer) — библиотека от Microsoft, которая устраняет избыточность хранения данных между GPU. Выделяют три стадии:
| Стадия | Что разделяется между GPU | Экономия памяти (относительно стандартного DDP) |
|---|---|---|
| ZeRO-1 | Optimizer state | ~4× |
| ZeRO-2 | Optimizer state + gradients | ~8× |
| ZeRO-3 | Optimizer state + gradients + weights | ~N× (N — число GPU) |
Принцип: каждый GPU хранит только свою часть данных, а при необходимости получает недостающие части от других GPU через all-gather или reduce-scatter.
3. Что такое ZeRO-Offload?
ZeRO-Offload — расширение ZeRO, которое выгружает optimizer state и gradients не на другие GPU, а в CPU RAM (или NVMe SSD). GPU остаётся только:
- веса модели (частично, если ZeRO-3)
- вычисления forward/backward
CPU берёт на себя обновление весов (optimizer step) и хранение состояний.
Как это работает (упрощённо):
- Forward/backward — на GPU, веса и активации там же.
- После backward градиенты пересылаются в CPU (через PCIe).
- На CPU выполняется optimizer step (Adam, SGD и т.д.) — обновляются веса.
- Обновлённые веса возвращаются на GPU для следующей итерации.
Для ZeRO-3 с offload веса также могут частично храниться на CPU и подгружаться по мере необходимости.
Offload на NVMe
Если CPU RAM не хватает, можно использовать NVMe SSD как дополнительный уровень памяти (ZeRO-Infinity). Это ещё медленнее, но позволяет обучать модели до триллиона параметров.
4. Когда ZeRO-Offload полезен?
Основной сценарий — обучение модели, которая не помещается в видеопамять доступных GPU, но при этом есть достаточно CPU RAM.
| Ситуация | Решение без offload | С ZeRO-Offload |
|---|---|---|
| Один GPU 24GB, модель 7B | Не помещается (нужна quantisation) | Помещается, скорость ~2× ниже |
| 4× A100 40GB, модель 70B | Нужно 8+ GPU или pipeline parallelism | Помещается на 4 GPU с offload, скорость ~3× ниже |
| Один GPU 80GB, модель 70B | Не помещается (нужно 840GB) | Помещается с offload на CPU (если CPU RAM ≥ 500GB) |
Когда НЕ стоит использовать
- Если модель помещается в GPU без offload — offload только замедлит.
- Если CPU RAM недостаточна (например, 32GB для модели 70B не хватит).
- Если требуется максимальная скорость (offload добавляет latency).
5. Trade-offs: скорость vs память
| Параметр | Без offload (все на GPU) | ZeRO-Offload (CPU) | ZeRO-Offload (NVMe) |
|---|---|---|---|
| Потребление GPU памяти | Высокое | Низкое | Очень низкое |
| Скорость обучения | 1× (базовая) | 2–5× медленнее | 10–50× медленнее |
| Загрузка CPU/NVMe | Нет | Высокая (PCIe) | Очень высокая |
| Максимальный размер модели | Ограничен суммой GPU | Ограничен CPU RAM | Ограничен NVMe |
Формула оценки времени шага
T_total = T_compute + T_comm + T_offload
где T_offload — время передачи данных CPU↔GPU (пропускная способность PCIe Gen4 ~32 GB/s, NVMe ~7 GB/s).
6. Сравнение с другими подходами
| Метод | Суть | Экономия памяти | Влияние на скорость |
|---|---|---|---|
| Gradient Checkpointing | Не хранить активации, пересчитывать на backward | Умеренная (активации) | ~1.3× медленнее |
| Pipeline Parallelism | Разделить слои модели по GPU | Линейная по числу GPU | ~1.5× медленнее (из-за пузырей) |
| Tensor Parallelism | Разделить матрицы весов по GPU | Линейная по числу GPU | ~1.2× медленнее (много коммуникаций) |
| ZeRO-Offload | Выгрузить optimizer/gradients в CPU | Большая (optimizer + gradients) | 2–5× медленнее |
| FSDP (Fully Sharded Data Parallel) | Аналог ZeRO-3 в PyTorch | Аналогично ZeRO-3 | ~1.1–1.3× медленнее (без offload) |
ZeRO-Offload часто комбинируют с gradient checkpointing и ZeRO-2/3 для максимальной экономии.
7. Пример конфигурации DeepSpeed для ZeRO-Offload
Файл ds_config.json:
{
"train_batch_size": 8,
"gradient_accumulation_steps": 4,
"fp16": {
"enabled": true
},
"zero_optimization": {
"stage": 2,
"offload_optimizer": {
"device": "cpu",
"pin_memory": true
},
"offload_param": {
"device": "cpu",
"pin_memory": true
},
"allgather_partitions": true,
"allgather_bucket_size": 2e8,
"overlap_comm": true,
"reduce_scatter": true,
"reduce_bucket_size": 2e8,
"contiguous_gradients": true
},
"optimizer": {
"type": "Adam",
"params": {
"lr": 1e-5,
"betas": [0.9, 0.999],
"eps": 1e-8
}
},
"scheduler": {
"type": "WarmupLR",
"params": {
"warmup_min_lr": 0,
"warmup_max_lr": 1e-5,
"warmup_num_steps": 1000
}
}
}
Запуск:
deepspeed --num_gpus=1 train.py --deepspeed ds_config.json
8. Практические рекомендации
- Измеряйте bottleneck: используйте
nvidia-smiиhtopдля отслеживания загрузки GPU/CPU. - Настраивайте
pin_memory: ускоряет передачу на CPU. - Используйте
overlap_comm: перекрывайте коммуникацию с вычислениями. - Для очень больших моделей комбинируйте ZeRO-Offload с ZeRO-3 и NVMe offload (ZeRO-Infinity).
- Не забывайте про CPU RAM: для модели 70B нужно минимум 500GB CPU RAM (optimizer state 560GB + градиенты 140GB + веса 140GB ≈ 840GB, но часть может оставаться на GPU).
Пет-проект для закрепления
Задача Обучить модель LLaMA-7B (или её меньшую версию, например, TinyLLaMA-1B) на одном GPU с 8GB памяти, используя ZeRO-Offload. Сравнить скорость и потребление памяти с обычным обучением (без offload).
Инструменты
- Python, PyTorch, Transformers, DeepSpeed
- GPU с 8GB (например, RTX 3070/3080)
- Датасет: любой небольшой (например, Alpaca 52k)
Шаги:
- Установить DeepSpeed:
pip install deepspeed. - Написать скрипт обучения с использованием
deepspeed.initialize. - Создать конфиг ZeRO-Offload (stage 2, offload optimizer на CPU).
- Запустить обучение с
deepspeedи замерить:- Потребление GPU памяти (
nvidia-smi) - Время на шаг (через
timeили логи)
- Потребление GPU памяти (
- Запустить обучение без offload (если модель помещается — для TinyLLaMA-1B может поместиться) и сравнить.
- Построить таблицу: память GPU, время шага, loss.
Ожидаемый результат
- С offload: модель обучается, GPU память ~6-7GB, время шага ~2-3 секунды.
- Без offload: либо OOM, либо ~1 секунда (если помещается).
- Вывод: offload позволяет обучать модели, которые иначе не влезают, ценой скорости.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 465 | Что такое FSDP и как он соотносится с ZeRO? |
| 466 | Как работает pipeline parallelism? |
| 467 | Что такое gradient checkpointing? |
| 468 | Как устроен Tensor Parallelism в Megatron-LM? |
| 469 | Какие стратегии mixed precision вы знаете? |
| 471 | Как профилировать использование памяти при обучении LLM? |
Навигация
- Предыдущий: 469
- Следующий: 471
- Индекс: 00. Индекс разборов