چیتشیت
DDP، FSDP، ZeRO و موازیسازی پایپلاین
DDP, FSDP, ZeRO and Pipeline Parallel
انتخاب روش موازیسازی، اندازهگیری حافظه، سیمکشی اعمال اشتراکی و رفع قفلشدگیها. با PyTorch gloo روی CPU.
فهرست مطالب
مقایسهی رو در روی چهار استراتژی
مقایسه| فاکتور | DDP | FSDP یا ZeRO-3 | Pipeline | تنسور موازی |
|---|---|---|---|---|
| بخشبندی | دسته (batch) | پارامترها، گرادیانها، بهینهساز | مدل بر اساس لایهها | هر ماتریس وزن |
| حافظه بهازای هر GPU | نسخهی کامل، بدون کاهش | 1/N از هر وضعیت | یک گروه از لایهها | یک برش از هر لایه |
| ترافیک | all-reduce روی گرادیانها | all-gather بهازای لایه، reduce-scatter | اکتیویشنها در مرزهای لایهها | all-reduce پس از هر matmul |
| اتصال (Link) | همپوشانی با محاسبات | بیشتر از DP معمولی | پهنای باند کم، بین نودها | NVLink درون یک نود |
| هزینهی اصلی | حافظه | گردآوری در هر گذر رو به جلو | حباب پایپلاین | تاخیر پس از هر لایه |
موارد استفاده
مفهومها- DDP
- مدل روی یک دستگاه جا میشود، اما دیتاست جا نمیشود.
- FSDP
- مدل روی یک دستگاه هم جا نمیشود.
- ZeRO-1
- حالت پیشفرض. پهنای باند مشابه DDP، کاهش خطی حافظه نسبت به N.
- ZeRO-2 یا ZeRO-3
- حافظهی شارد پارامترها هم یک مشکل است.
- تنسور موازی
- درون یک نود. به اتصالات سریع بین GPUها نیاز دارد.
- پایپلاین
- بین نودها. نیاز به پهنای باند کم است.
- DualPipe
- مدلهای MoE با بار شدید EP در مقیاس چند هزار GPU.
- DualPipeV
- مدلهای چگال (dense) یا با بار سبک EP. تکنسخه از پارامتر.
مراحل ZeRO
مقایسه| فاکتور | DDP | ZeRO-1 | ZeRO-2 | ZeRO-3 |
|---|---|---|---|---|
| شارد شده | هیچکدام | وضعیت بهینهساز | بهینهساز + گرادیانها | بهینهساز + گرادیانها + پارامترها |
| حافظه بهازای هر رتبه | پارامترها + گرادیانها + بهینهساز | پارامترها + گرادیانها + بهینهساز/N | پارامترها + گرادیانها/N + بهینهساز/N | پارامترها/N + گرادیانها/N + بهینهساز/N |
| ارتباطات بهازای هر گام | 1 بار allreduce | 1 بار reduce_scatter + 1 بار allgather | 1 بار reduce_scatter + 1 بار allgather | allgather و reduce_scatter بهازای هر لایه |
| صرفهجویی | بدون کاهش | ~4 برابر کاهش | ~8 برابر کاهش | ~N برابر کاهش |
| نام در PyTorch FSDP | در لیست نیست | در لیست نیست | ShardingStrategy.SHARD_GRAD_OP | FULL_SHARD |
ارقام حافظه
مفهومها- وضعیتهای Adam
- مقادیر m و v در FP32. برای 70B x 4 بایت x 2 = 560GB.
- ساده (Vanilla)، دقت ترکیبی
- 16P بایت بهازای هر رتبه.
- ZeRO-1
- معادل 4P + 12P/N بایت. در N=8 میشود 5.5P، یعنی 65% کاهش.
- FSDP برای 70B روی 8 تا GPU
- معادل 105 گیگابایت بهازای هر GPU. همچنان بالای 80GB.
- دقت ترکیبی، 7B
- کاهش از 112 گیگابایت به 84 گیگابایت. بهینهساز در FP32 میماند.
- دستهی مؤثر
- per_gpu_batch_size x N.
زمانبندیهای پایپلاین
مقایسه| فاکتور | 1F1B | Zero Bubble | DualPipe | DualPipeV |
|---|---|---|---|---|
| نسخههای پارامتر | 1 | 1 | 2 | 1 |
| حباب در برابر ریزدستهها | رشد میکند | رشد میکند | ثابت | رشد جزئی |
| همپوشانی | حداقل | جزئی | کامل | جزئی |
| شبیهساز | bubble_1f1b | bubble_zero_bubble | bubble_dualpipe | bubble_dualpipev |
اعمال اشتراکی
مقایسه| فاکتور | Ring allreduce | Tree allreduce | Allgather | Reduce_scatter |
|---|---|---|---|---|
| بایت بهازای هر رتبه | 2T(N-1)/N | T log2(N) | T(N-1)/N | T(N-1)/N |
| گامها | 2(N-1) | 2 log2(N) | N-1 | N-1 |
| کاربرد | T بزرگ | T کوچک، تاخیر زیاد | خروج از شارد ZeRO | شارد گرادیان ZeRO |
| اندازهی پیام | بیشتر از ~1 مگابایت | کمتر از ~1 مگابایت | هر اندازهای | هر اندازهای |
آلردیوس حلقهای روی یک مش صف
معماریحلقهی ورکر DDP
کدpython
dist.init_process_group(
backend="gloo",
init_method=f"file://{init_file}", # راندوو مبتنی بر فایل
rank=rank,
world_size=world_size,
)
torch.manual_seed(SEED) # مقداردهی اولیهی یکسان در هر رتبه
ddp = DistributedDataParallel(model, world_size=world_size) # همهپخشی پارامترها
for step in range(steps):
offset = step * world_size * batch + rank * batch # برش اختصاصی
optim.zero_grad(set_to_none=True)
loss.backward()
ddp.sync_grads() # all_reduce SUM، سپس div_ بر world_size
optim.step()
فراخوانیهای اعمال اشتراکی
کار و کد| وظیفه | کد |
|---|---|
| تنظیم پورت راندوو | os.environ["MASTER_PORT"] = str(port) |
| شروع گروه پردازشی CPU | dist.init_process_group(backend="gloo", rank=rank, world_size=world_size) |
| پارامترهای یکسان روی تمام رتبهها | dist.broadcast(p.data, src=0) |
| جمع گرادیانها | dist.all_reduce(p.grad.data, op=dist.ReduceOp.SUM) |
| میانگین گرادیانها | p.grad.data.div_(world_size) |
| شارد گرادیان ZeRO-1 | dist.reduce_scatter(local_grad, chunks, op=dist.ReduceOp.SUM) |
| بازگردانی پارامترهای ZeRO-1 | dist.all_gather(gathered, self.master_shard) |
| حباب GPipe | bubble_fraction(num_stages, num_microbatches) |
اجرای درسها
دستورها| دستور | عملکرد |
|---|---|
python3 code/main.py | درس 48: دو رتبهی gloo، نوشتن در outputs/ddp-demo.json |
--world-size 4 | درس 48: چهار رتبه. پراکندگی پارامترها زیر 1e-3 میماند |
--no-write | درس 48: صرفنظر از فایل JSON |
python3 code/main.py | درس 76: مش صف در مقایسه با gloo، شمارندهی بایت |
python3 code/main.py | درس 77: تک فرایند در مقایسه با DDP روی 4 رتبه |
python3 code/main.py | درس 78: تابع زیان و جدول حافظهی ZeRO-1 |
python3 code/main.py | درس 79: نمودار گانت و درصد حباب پایپلاین |
پیشفرضها در کد
پیشفرضها| تنظیم | پیشفرض | نکته |
|---|---|---|
--world-size | 2 | رتبههای درس 48 |
--backend | gloo | روی GPU از nccl استفاده کنید |
--num-steps | 6 | گامهای آموزش درس 48 |
--batch-size | 8 | بهازای هر رتبه |
RECV_TIMEOUT_S | 30.0 | مهلت زمانی دریافت مش صف |
DDP bucket size | 25 MB | یک allreduce بهازای هر باکت |
NUM_STAGES | 4 | پایپلاین درس 79 |
NUM_MICROBATCHES | 8 | پایپلاین درس 79 |
عیبیابی
عیبیابیواگرایی رتبهها از گام اول
- علت
- هر رتبه از مقداردهی اولیهی خود استفاده میکند
- راهحل
- استفاده از
dist.broadcast(tensor, src=0)در زمان ساخت
تمام رتبهها دستههای یکسانی را میبینند
- علت
- یک سید مشترک برای بههمریختن (shuffling)
- راهحل
- استفاده از
torch.manual_seed(seed + rank)برای بههمریختن
بنبست (deadlock) در Allreduce
- علت
- پارامترهای نادیدهگرفتهشده گرادیان ندارند
- راهحل
find_unused_parameters=True
انباشت (accumulation) کند است
- علت
- اجرای Allreduce روی تکتک ریزدستهها
- راهحل
- ریزدستهها را درون
no_sync()قرار دهید
با تغییر N، نرخ یادگیری (LR) بههم میریزد
- علت
- انجام SUM بدون عمل تقسیم
- راهحل
- تقسیم بر world_size
پایپلاین در شبکه قفل میکند
- علت
- هر مرحله قبل از دریافت، اقدام به ارسال میکند
- راهحل
- رتبههای زوج اول ارسال کنند، رتبههای فرد اول دریافت کنند
یک مرحله تمام چرخهها را مسدود میکند
- علت
- تقسیم مراحل بر اساس تعداد پارامتر
- راهحل
- یکسانسازی FLOPs بهازای هر مرحله
حافظه FSDP برای 70B بیش از 80GB روی 8 کارت
- علت
- 105 گیگابایت بهازای هر GPU
- راهحل
- 16 تا GPU یا چکپوینتگذاری اکتیویشن
پرسش و پاسخ
پرسش و پاسخ- چرا میانگین و نه جمع؟
- تا LR تنظیمشده روی یک رتبه، روی چهار رتبه هم کار کند.
- آیا دقت ترکیبی حافظه را نصف میکند؟
- خیر. حدود 25% صرفهجویی میکند.
- حباب GPipe در M=8 و N=4؟
- 27%. در M=64 و N=4 برابر 4.5% است.
- مدل GPipe یا 1F1B؟
- حباب پایپلاین یکسان است. 1F1B حافظهی اکتیویشن را محدود میکند.
- چرا در ZeRO از reduce_scatter استفاده میشود؟
- هر رتبه فقط جمع مربوط به شارد خود را دریافت میکند.
- آیا ZeRO-3 همان FSDP است؟
- بله. سازوکار دقیقاً یکی است.
- استفاده از DualPipe زیر 1000 تا GPU؟
- افراطی است. حبابها بخش کوچکی از هزینه را تشکیل میدهند.