Transformers documentation
Song song chuyên gia
Song song chuyên gia
Expert parallelism là một chiến lược song song hóa cho các mô hình mixture-of-experts (MoE). Lớp feedforward của mỗi expert nằm trên một bộ tăng tốc phần cứng khác nhau. Một router sẽ điều phối các token đến các expert phù hợp và thu thập kết quả. Cách tiếp cận này cho phép mở rộng mô hình lên số lượng tham số lớn hơn nhiều mà không làm tăng chi phí tính toán, vì mỗi token chỉ kích hoạt một vài expert.
DistributedConfig
Bật song song chuyên gia bằng lớp DistributedConfig và đối số ep_size. Hầu hết các mô hình MoE mặc định dùng token dispatch, nên ep_size có thể được đặt độc lập với tp_size. Một số mô hình, chẳng hạn Llama 4 và Gemma 4, vẫn mặc định dùng masking và all-reduce ("ep_router" và "moe_tp_experts" trong model.ep_plan). Masking cũng khả dụng trên bất kỳ mô hình nào có ghi đè ep_plan, và yêu cầu ep_size=tp_size để mọi rank trong một nhóm chuyên gia nhận cùng các token.
import os
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.distributed.configuration_utils import DistributedConfig
distributed_config = DistributedConfig(
tp_size=int(os.environ["WORLD_SIZE"]),
ep_size=int(os.environ["WORLD_SIZE"]),
)
model = AutoModelForCausalLM.from_pretrained(
"openai/gpt-oss-120b",
distributed_config=distributed_config,
)Mỗi mô hình MoE định nghĩa hai kế hoạch trong cấu hình của nó: base_model_tp_plan cho các module dense và base_model_ep_plan cho các chuyên gia. Chúng được hiển thị trên mô hình đã tải dưới dạng model.tp_plan và model.ep_plan. Với tp_size > 1 và ep_size > 1, cả hai đều được áp dụng: kế hoạch tensor parallel chia nhỏ attention và các MLP dense, còn kế hoạch expert parallel chia nhỏ các chuyên gia. Các quy tắc EP có ưu tiên cao hơn các quy tắc TP đối với cùng một module, do đó trọng số chuyên gia chỉ được chia nhỏ một lần, bởi kế hoạch EP. Trong kế hoạch EP, kiểu GroupedGemmParallel chia nhỏ trọng số chuyên gia dọc theo chiều chuyên gia để mỗi rank chỉ tải các chuyên gia cục bộ của nó, và ep_router che các chuyên gia nằm trên các rank khác trước khi all-reduce kết hợp các đầu ra chuyên gia.
tp_plan chỉ được áp dụng khi tp_size > 1, và ep_plan chỉ khi ep_size > 1. Khi TP được bật và EP bị tắt, toàn bộ kế hoạch TP sẽ được áp dụng, bao gồm cả các quy tắc chuyên gia.
Quy tắc tiến của chuyên gia trong ep_plan xác định cách các token đến các chuyên gia:
| quy tắc | cơ chế | layout |
|---|---|---|
"moe_tp_experts" với "ep_router" trên router | masking và all-reduce: mỗi rank chạy các chuyên gia cục bộ của nó trên toàn bộ batch, router sẽ mask các phần còn lại, và all-reduce kết hợp các đầu ra | ep_size=tp_size |
"ep_dispatch_experts" | phân phối token: mỗi rank giữ các token của riêng mình và chỉ trao đổi các cặp (token, expert) được định tuyến thông qua hai phép thu thập all-to-all | ep_size là một bội số của tp_size và chia hết fsdp_size * tp_size |
enable_expert_parallel=Truemà không cóep_sizeđã bị ngừng hỗ trợ và sẽ phát raFutureWarning. Nó chỉ chia nhỏ các chuyên gia, giống nhưep_size=tp_size, tp_plan={}.
Chạy script suy luận của bạn bằng torchrun. Số lượng tiến trình phải bằng tp_size * fsdp_size * pp_size, và ep_size phải chia hết cho số lượng chuyên gia.
torchrun --nproc-per-node 8 your_script.py
Ghi đè các kế hoạch
Truyền tp_plan={...} hoặc ep_plan={...} vào DistributedConfig để ghi đè các quy tắc riêng lẻ của các kế hoạch được định nghĩa trước. Các quy tắc không được chỉ định sẽ được giữ nguyên, và các kế hoạch đã hợp nhất sẽ được lưu trên model. Mỗi key phải khớp với một module, một parameter, hoặc một mục có sẵn trong kế hoạch; nếu không, quá trình tải sẽ gây ra ValueError trước khi bất kỳ phần nào được sharding. Hãy sử dụng đường dẫn đầy đủ như nhìn từ model đã được tải, ví dụ model.layers.* cho một causal LM và layers.* cho base model của nó.
distributed_config = DistributedConfig(
tp_size=4,
ep_size=4,
tp_plan={"model.layers.*.self_attn.q_proj": "colwise_rep"},
ep_plan={"model.layers.*.mlp.experts.down_proj": "grouped_gemm"},
)Việc cung cấp một kế hoạch không suy ra kích thước song song: hãy đặt tp_size và ep_size một cách rõ ràng.
Hầu hết các mô hình MoE mặc định sử dụng "ep_dispatch_experts". Để sử dụng masking và all-reduce thay thế, hãy đặt ep_size=tp_size và ghi đè cả quy tắc forward của router lẫn expert (tên module router phụ thuộc vào mô hình, ví dụ mlp.router trên gpt-oss):
distributed_config = DistributedConfig(
tp_size=4,
ep_size=4,
ep_plan={
"model.layers.*.mlp.gate": "ep_router",
"model.layers.*.mlp.experts": "moe_tp_experts",
},
)Ngược lại, ghi đè quy tắc forward của expert trong một model có kế hoạch sử dụng masking bằng "ep_dispatch_experts" để dùng token dispatch. Khi đó, quy tắc router sẽ bị bỏ qua, vì dispatch cần các id expert toàn cục để tìm chủ sở hữu của từng expert.
Phân phối token
Với token dispatch, mỗi rank huấn luyện trên phần riêng của batch. Tại mỗi lớp MoE, một rank định tuyến các token của nó, gửi mỗi cặp (token, expert) đến rank sở hữu expert đó bằng all-to-all, chạy các expert cục bộ trên những gì nó nhận được, nhận kết quả trở lại bằng một all-to-all thứ hai và kết hợp chúng với các trọng số định tuyến. Chỉ các kích hoạt được định tuyến và đầu ra của expert di chuyển, và không rank nào tính toán expert cho các token mà nó không sở hữu.
from transformers import AutoModelForCausalLM
from transformers.distributed import DistributedConfig
distributed_config = DistributedConfig(
tp_size=1,
fsdp_size=8,
ep_size=4,
)
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-30B-A3B", distributed_config=distributed_config)Với tp_size=1, ep_size phải chia hết cho fsdp_size và số lượng chuyên gia, và attention không bị ràng buộc bởi num_key_value_heads. Đối với phần còn lại của mô hình:
- Các tham số nằm ngoài các chuyên gia được chia nhỏ bằng FSDP2 trên
fsdp, điều này làm giảm gradient của chúng. - Các chuyên gia được chia nhỏ (sharded) trên
epvà, khiefsdp_size = fsdp_size * tp_size // ep_sizelớn hơn một, chúng còn được chia nhỏ theo FSDP trênefsdp. Chúng luôn được bọc (wrapped) bởi FSDP, do đófsdp_mixed_precisionvàfsdp_cpu_offloadcũng áp dụng cho chúng và save_pretrained() sẽ thu thập (gather) chúng giống như bất kỳ tham số nào khác. - Một nhóm song song chuyên gia giữ
ep_size / tp_sizelô, do đó gradient của một chuyên gia là tổng trên số lô đó. Việc giảmefsdpchia chofsdp_sizethay vì kích thước nhóm của nó, cho ra cùng một giá trị trung bình trên mỗi lô mà FSDP2 áp dụng cho các module đặc. - Mỗi chuyên gia cục bộ cũng xử lý một hàng đệm zero cho mỗi lớp. Một rank mà các chuyên gia của nó không nhận được token nào vẫn tham gia vào quá trình reverse all-to-all và giảm gradient chuyên gia.
Cách các kích thước kết hợp
tp_size * fsdp_sizelà số lượng tiến trình.ep_sizekhông thêm gì: nó chỉ nhóm lại các rank giống nhau cho trọng số chuyên gia.ep_sizechia danh sách chuyên gia thànhep_sizekhối. Mỗi rank tính toánnum_experts / ep_sizechuyên gia, vàep_sizerank liên tiếp giữ một bộ đầy đủ. Tập hợp các rank đó là nhóm mà all-to-all chạy trong đó.efsdp_size = fsdp_size * tp_size / ep_sizelà số lượng bản sao đầy đủ của tập chuyên gia tồn tại. Các rank ở cùng vị trí trong các bản sao khác nhau sẽ chia nhỏ (shard) các chuyên gia đó để tiết kiệm bộ nhớ và trung bình hóa gradient của chúng, giống như cách FSDP thực hiện cho các module dense.- Batch mà một rank giữ phụ thuộc duy nhất vào
fsdp. Các rank liên tiếp tạo thành một nhóm TP và nhận cùng một batch;fsdp_sizenhóm nhận các batch khác nhau.
Tám tiến trình, tp_size=2, fsdp_size=4, tám chuyên gia:
rank 0 1 2 3 4 5 6 7 batch [====B0====] [====B1====] [====B2====] [====B3====] one batch per TP pair tp 0 1 0 1 0 1 0 1 ep_size=2 E0-3 E4-7 E0-3 E4-7 E0-3 E4-7 E0-3 E4-7 group = a TP pair, efsdp_size=4 ep_size=4 E0E1 E2E3 E4E5 E6E7 E0E1 E2E3 E4E5 E6E7 group = two pairs, efsdp_size=2 ep_size=8 E0 E1 E2 E3 E4 E5 E6 E7 group = all ranks, efsdp_size=1
Hai con số rút ra từ hình ảnh:
- Bên trong một nhóm EP, mỗi token tồn tại
tp_sizelần, một lần cho mỗi rank của cặp giữ batch của nó. Điều này không phụ thuộc vàoep_size. - Một nhóm EP giữ
ep_size / tp_sizecác lô dữ liệu khác nhau. Đây là số lượng mà gradient của một chuyên gia được cộng dồn.
Với song song tensor
Đặt tp_size > 1 để chia nhỏ các module dense theo kế hoạch TP trong khi các expert sử dụng dispatch. Trên tám tiến trình:
distributed_config = DistributedConfig(
tp_size=2,
fsdp_size=4,
ep_size=4,
)Mỗi cặp rank TP nhận cùng một batch, vì tensor parallelism nhân bản các activation bên trong cặp đó. Nếu cả hai rank đều dispatch toàn bộ token của mình, rank sở hữu một expert sẽ nhận mỗi token hai lần, tính toán hai lần, và gradient trọng số của nó sẽ bị nhân đôi. Các expert nằm trọn trên một rank, nên bản sao thừa không thể được chia theo trọng số, và rank sở hữu thường là một rank khác, nên cũng không thể giải quyết bằng quyền sở hữu như cách masking làm. Do đó, cặp này chia các hàng: mỗi rank TP dispatch một phần rời rạc 1 / tp_size của các token, kết quả được trả về rank đã gửi, và một all-reduce trên cặp của các nửa được zero-pad khôi phục đầu ra được nhân bản mà lớp tiếp theo kỳ vọng. Việc chia được thực hiện theo tp_size, không phải ep_size, vì chỉ các rank giữ một batch mới có thể gửi nó. Các nhóm expert trải dài trên bốn rank và mỗi expert được FSDP-sharded trên efsdp_size = 2 rank, trong khi nhóm FSDP của trunk trải dài trên bốn rank. Các ràng buộc TP thông thường của mô hình, chẳng hạn như tính chia hết của attention-head, vẫn áp dụng cho các module dense. Các lát token có thể không đều hoặc rỗng, kể cả trong quá trình giải mã đơn token.
Token dispatch hiện chưa thể kết hợp với pipeline parallelism; hãy sử dụng pp_size=1 (chưa được kiểm tra)
Các cấu hình này đều sử dụng tám GPU:
| Cấu hình | Kết quả |
|---|---|
DistributedConfig(tp_size=4, fsdp_size=2, ep_size=4) | Dispatch với các nhóm TP gồm bốn, mỗi nhóm chia nhỏ batch của nó thành bốn phần (kế hoạch mặc định); trừ khi bạn chỉ định một ep_plan để sử dụng EP bị che khuất theo kiểu cũ |
DistributedConfig(tp_size=1, fsdp_size=8, ep_size=4) | Dispatch với một batch độc lập trên mỗi rank, không có slicing, và các expert được chia nhỏ theo FSDP trên các cặp rank. |
DistributedConfig(tp_size=2, fsdp_size=4, ep_size=4) | Dispatch với một cặp TP cho mỗi batch, mỗi cặp sẽ chia batch của nó thành hai; hai batch cho mỗi nhóm chuyên gia. |
DistributedConfig(tp_size=8, ep_size=8) | Dispatch với mỗi rank chia sẻ một batch, được chia thành tám phần, hoặc sử dụng masking và all-reduce cho một kế hoạch có masking. |
Trainer hiện chưa hỗ trợ việc phân phối token: việc đếm batch và token giả định bố cục all-reduce, trong đó các rank của một nhóm TP chia sẻ một batch và tồn tại
fsdp_sizeshard song song dữ liệu. Hỗ trợ dispatch cho Trainer sẽ được bổ sung trong một bản cập nhật tiếp theo.
Kết hợp với FSDP2
Tensor và expert parallelism chia nhỏ (shard) trọng số trên tp, nhưng trạng thái optimizer và các module không có quy tắc vẫn được nhân bản (replicated) trên mọi rank của nhóm, điều này giới hạn kích thước mô hình mà bạn có thể huấn luyện. Thêm FSDP2 trên một chiều mesh thứ hai với fsdp_size. Với masking và all-reduce, giữ ep_size=tp_size và truyền `ep_plan={“layers..mlp.gate”: “ep_router”, “layers..mlp.experts”: “moe_tp_experts”}“.
from transformers import AutoModelForCausalLM
from transformers.distributed import DistributedConfig
distributed_config = DistributedConfig(
tp_size=4,
ep_size=4, # expert parallel size, must match tp_size with masking and all-reduce
fsdp_size=2, # data parallel shards
ep_plan={
"model.layers.*.mlp.gate": "ep_router",
"model.layers.*.mlp.experts": "moe_tp_experts",
},
)
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-30B-A3B", distributed_config=distributed_config)Tải model như bình thường, sau đó huấn luyện với Trainer. Nó lấy chuẩn gradient trên cả hai mesh và cấp cho mỗi mesh một nhóm tham số optimizer riêng. save_model() thu thập các trọng số đã chia mảnh thành một checkpoint thông thường. Điều này yêu cầu accelerate>=1.12 để Trainer có thể sao chép tp_size và fsdp_size vào ~Accelerate.ParallelismConfig.
Bảng dưới đây so sánh huấn luyện chỉ dùng EP với 2D EP+FSDP2 trên 8 GPU H100. Khối lượng công việc là fine-tuning đầy đủ Qwen3-30B-A3B ở bf16 với độ dài chuỗi 2048. Nhiều shard FSDP hơn sẽ cắt giảm bộ nhớ đỉnh, và tokens/s giảm một chút vì FSDP2 thực hiện all-gather và reduce-scatter các expert qua fsdp.
| cấu hình | tokens/s/GPU | bộ nhớ đỉnh/GPU |
|---|---|---|
tp_size=8 | 3485 | 38,6 GB |
tp_size=4, fsdp_size=2 | 2900 | 34,2 GB |
tp_size=2, fsdp_size=4 | 2830 | 32,3 GB |
Việc tiếp tục từ một checkpoint hiện chưa được hỗ trợ cho các mô hình được chia mảnh (sharded) tại thời điểm tải, do đó Trainer chỉ chấp nhận
save_only_model=Truehoặcsave_strategy="no"cho chúng.
Tham khảo API
lớp transformers.DistributedConfig
< source >( tp_size: int | None = Nonetp_plan: typing.Union[dict[str, str], typing.Literal['auto'], NoneType] = Noneenable_sequence_parallel: bool = Falseenable_expert_parallel: bool = Falsefsdp_size: int | None = Nonefsdp_plan: dict[str, str] | None = Nonefsdp_cpu_offload: bool = Falsefsdp_mixed_precision: bool = Falsepp_size: int | None = Noneep_size: int | None = Noneep_plan: dict[str, str] | None = None )
Tham số
- tp_size (
int, tùy chọn) — Số lượng thiết bị cho tensor parallelism. NếuNonevàtp_planđược đặt, mặc định làWORLD_SIZE // (other_parallel_size). NếuNonevà không cótp_plannào được đặt, mặc định là 1. - tp_plan (
dict[str, str]hoặc"auto", tùy chọn) — Kế hoạch chia nhỏ song song tensor. Truyền"auto", hoặc đểNonekhitp_sizeđược đặt, để sử dụngbase_model_tp_planđược định nghĩa trước của mô hình. Truyền một từ điển để ghi đè các quy tắc riêng lẻ của kế hoạch đó; các quy tắc không được chỉ định sẽ được giữ nguyên. Một từ điển rỗng sẽ không áp dụng quy tắc song song tensor nào. - enable_sequence_parallel (
bool, tùy chọn, mặc định làFalse) — Dành cho song song hóa chuỗi. Chưa được kết nối. - enable_expert_parallel (
bool, tùy chọn, mặc định làFalse) — Đã bị loại bỏ, xóa trong v5.21. Nếu không cóep_size, tương đương vớiep_size=tp_size, tp_plan={}: chỉ dùng kế hoạch song song chuyên gia trêntp_sizerank. Giá trịep_sizeđược chỉ định rõ ràng sẽ có ưu tiên cao hơn. Cờ này không thay đổitp_sizehoặcfsdp_size. - fsdp_size (
int, tùy chọn) — Số lượng thiết bị cho FSDP (song song dữ liệu). NếuNonevàtp_sizeđược đặt, mặc định là 1. - fsdp_plan (
dict[str, str], tùy chọn) — Kế hoạch chia nhỏ FSDP2. ĐểNoneđể sử dụngbase_model_fsdp_planđược định nghĩa trước của mô hình và_fsdp_planở cấp lớp. Truyền một từ điển để ghi đè các quy tắc riêng lẻ; các quy tắc không được chỉ định sẽ được giữ nguyên. Chỉ được áp dụng khifsdp_size > 1. - fsdp_cpu_offload (
bool, tùy chọn, mặc định làFalse) — Có bật offloading CPU cho FSDP2 hay không. - fsdp_mixed_precision (
bool, tùy chọn, mặc định làFalse) — Có bật độ chính xác hỗn hợp cho FSDP2 hay không. - pp_size (
int, tùy chọn) — Số lượng thiết bị cho pipeline parallelism. NếuNonevà một chế độ song song khác được đặt, mặc định là 1. - ep_size (
int, tuỳ chọn) — Số lượng thiết bị sở hữu các shard chuyên gia riêng biệt. Mặc định là 1. Đặt giá trị này một cách rõ ràng để kích hoạt EP. Phải là bội số củatp_sizevà chia hếtfsdp_size * tp_size. Các kế hoạch all-reduce chuyên gia yêu cầuep_size=tp_size; việc điều phối token ("ep_dispatch_experts") cũng cho phépep_size > tp_size. - ep_plan (
dict[str, str], tuỳ chọn) — Kế hoạch chia nhỏ song song chuyên gia. ĐểNoneđể sử dụngbase_model_ep_planđược định nghĩa trước của mô hình. Truyền một từ điển để ghi đè các quy tắc riêng lẻ của kế hoạch đó; các quy tắc không được chỉ định sẽ được giữ nguyên. Chỉ được áp dụng khiep_size > 1, và các quy tắc của nó có ưu tiên cao hơn các quy tắc củatp_planđối với cùng các module. Quy tắc"ep_dispatch_experts"chọn việc điều phối token all-to-all thay vì che mặt nạ router và all-reduce.
Cấu hình cho suy luận và huấn luyện phân tán bản địa với song song tensor, pipeline hoặc FSDP2.