Transformers documentation
Song song tensor cho suy luận
Song song tensor cho suy luận
Tensor parallelism chia một lớp của mô hình thành các phần để nhiều bộ tăng tốc phần cứng xử lý chúng đồng thời. Điều này cho phép bạn chạy các mô hình vượt quá dung lượng bộ nhớ của một GPU duy nhất và đạt được thông lượng cao hơn. Bạn sẽ cần liên lạc nhanh trong node vì các GPU trao đổi kết quả trung gian ở mỗi lớp.
Một model hỗ trợ tensor parallelism nếu config của nó định nghĩa base_model_tp_plan. Kiểm tra một model đã được nạp bằng thuộc tính supports_tp_plan.
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("meta-llama/Meta-Llama-3-8B-Instruct")
print(model.supports_tp_plan)Hướng dẫn này bao gồm cách bật song song tensor trong Transformers và các chiến lược phân chia có sẵn.
Chia nhỏ một mô hình
Cấu hình số lượng thiết bị song song tensor bằng tp_size trong DistributedConfig.
- Đặt
DistributedConfig(tp_size=N)để sử dụng kế hoạch được định nghĩa trước của mô hình. - Định nghĩa
tp_planthủ công và truyền nó vào DistributedConfig cùng vớitp_size.
Bạn cũng có thể đặt tp_plan="auto" để yêu cầu kế hoạch được định nghĩa trước một cách rõ ràng. Khi tp_size bị bỏ qua và một tp_plan được đặt, tp_size sẽ được suy ra từ WORLD_SIZE chia cho các kích thước song song khác. Việc truyền tp_plan trực tiếp vào from_pretrained() đã bị ngừng hỗ trợ và sẽ bị loại bỏ trong v5.18.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, DistributedConfig
# model_id = "meta-llama/Llama-4-Scout-17B-16E-Instruct" # better to visualize all the possible strategies
distributed_config = DistributedConfig(tp_size=4)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Meta-Llama-3-8B-Instruct",
dtype=torch.bfloat16,
distributed_config=distributed_config,
)
print(model.tp_plan)
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Meta-Llama-3-8B-Instruct")
prompt = "Can I help"
inputs = tokenizer(prompt, return_tensors="pt").input_ids.to(model.device)
# distributed run
outputs = model(inputs)Chạy script suy luận bằng torchrun. Sử dụng một tiến trình cho mỗi GPU.
torchrun --nproc-per-node 4 demo.py
Định nghĩa kế hoạch song song tensor cho từng lớp trong tp_plan và truyền nó qua DistributedConfig. Ví dụ bên dưới sử dụng phân vùng cột và phân vùng hàng. Xem phần Chiến lược phân vùng để biết các chiến lược khác được hỗ trợ.
Việc phân vùng thủ công đòi hỏi hiểu biết sâu sắc về kiến trúc mô hình và sự tương tác giữa các chiến lược. Những lựa chọn phân vùng kém sẽ tạo ra các mô hình chậm, bị lỗi hoặc cho ra kết quả không chính xác. Ultra-Scale Playbook giải thích chi tiết các chiến lược phân vùng.
Các khóa là tên module hoặc tham số, trong đó * đại diện cho chỉ số lớp. Một tên chiến lược không được công nhận sẽ gây ra ValueError liệt kê các tên được hỗ trợ, và Transformers sẽ ghi log cảnh báo cho các quy tắc kế hoạch không khớp với gì và cho các tham số không được quy tắc nào bao phủ.
from transformers import AutoModelForCausalLM, DistributedConfig
tp_plan = {
"model.layers.*.self_attn.q_proj": "colwise",
"model.layers.*.self_attn.k_proj": "colwise",
"model.layers.*.self_attn.v_proj": "colwise",
"model.layers.*.self_attn.o_proj": "rowwise",
...
}
distributed_config = DistributedConfig(tp_size=4, tp_plan=tp_plan)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Meta-Llama-3-8B-Instruct",
dtype="auto",
distributed_config=distributed_config,
)
print(model.tp_plan)Chiến lược phân vùng
ALL_PARALLEL_STYLES, một ParallelInterface được định nghĩa trong distributed/parallel_utils.py, ánh xạ mỗi tên chiến lược mà bạn có thể sử dụng trong một kế hoạch đến một instance chiến lược đã được cấu hình. Bạn không tương tác trực tiếp với nó để shard một model, nhưng đây là danh sách chính thức các tên khả dụng. distributed/tensor_parallel.py đăng ký các chiến lược tensor parallel vào đó:
ALL_PARALLEL_STYLES.register("embedding_rowwise", EmbeddingParallel(shard_dim=0))
ALL_PARALLEL_STYLES.register("embedding_colwise", EmbeddingParallel(shard_dim=1))
ALL_PARALLEL_STYLES.register("colwise_gather_output", ColwiseParallel(input_layouts=Replicate(), output_layouts=Replicate()))
ALL_PARALLEL_STYLES.register("colwise_rep", ColwiseParallel(input_layouts=Replicate(), output_layouts=Replicate()))
ALL_PARALLEL_STYLES.register("colwise", ColwiseParallel(input_layouts=Replicate(), output_layouts=Shard(-1)))
ALL_PARALLEL_STYLES.register("rowwise", RowwiseParallel(input_layouts=Shard(-1), output_layouts=Replicate()))
ALL_PARALLEL_STYLES.register("rowwise_split_input", RowwiseParallel(input_layouts=Replicate(), output_layouts=Replicate()))
ALL_PARALLEL_STYLES.register("rowwise_rep", RowwiseParallel(input_layouts=Replicate(), output_layouts=Replicate()))
ALL_PARALLEL_STYLES.register("packed_colwise", PackedColwiseParallel())
ALL_PARALLEL_STYLES.register("packed_rowwise", PackedRowwiseParallel())
ALL_PARALLEL_STYLES.register("sequence_parallel", SequenceParallel(use_local_output=True))
ALL_PARALLEL_STYLES.register("replicated_with_grad_allreduce", ReplicatedWithGradAllReduce())
ALL_PARALLEL_STYLES.register("mla_kv_a_proj", MlaKvAProjParallel())
ALL_PARALLEL_STYLES.register("all_reduce", AllReduceParallel())
ALL_PARALLEL_STYLES.register("moe_tp_experts", MoeExpertsParallel())
ALL_PARALLEL_STYLES.register("moe_identity_expert", MoeIdentityParallel())
ALL_PARALLEL_STYLES.register("local_params", LocalParamsParallel())Mọi chiến lược song song tensor đều là lớp con của TensorParallelLayer trong tệp đó. Các chiến lược song song chuyên gia, chẳng hạn như ep_dispatch_experts, nằm trong distributed/expert_parallel.py, kế thừa từ ExpertParallelLayer, có cùng các hook, và được đăng ký vào cùng một ALL_PARALLEL_STYLES. Hai chiến lược bạn sẽ dùng thường xuyên nhất, ColwiseParallel và RowwiseParallel, nhận các bố trí input_layouts và output_layouts, đây là cách một lớp bao phủ nhiều tên kế hoạch. colwise giữ đầu ra của nó được chia mảnh trên chiều cuối để một lớp rowwise tiếp theo tiêu thụ, trong khi colwise_gather_output thu thập tất cả (all-gather) nó trở lại thành một tensor đầy đủ.
Bảng dưới đây mô tả từng chiến lược.
| Chiến lược | Mô tả |
|---|---|
ColwiseParallel | Tách một trọng số tuyến tính theo chiều đặc trưng đầu ra (Shard(0) cho trọng số nn.Linear 2 chiều) và tách một bias 1 chiều. Phân phối lại đầu vào sang Replicate(), sau đó đặt đầu ra tại output_layouts. |
RowwiseParallel | Tách một trọng số tuyến tính theo chiều đặc trưng đầu vào (Shard(-1)) và nhân bản bias, vốn được cộng sau khi giảm. Forward của mỗi rank tạo ra một đầu ra Partial() chứa phần tổng của rank đó, và transform_output_post_forward phân phối lại phần đó sang output_layouts. Giảm về Replicate() sẽ phát sinh một all-reduce, còn giảm về Shard(1) sẽ phát sinh một reduce-scatter. |
EmbeddingParallel | Tách nn.Embedding với các token id được nhân bản. shard_dim=0 (embedding_rowwise, lựa chọn thông thường) tách bảng từ vựng và all-reduce kết quả tra cứu từng phần của mỗi rank thành đầu ra đầy đủ. shard_dim=1 (embedding_colwise) tách chiều ẩn và all-gather phần đầu ra của mỗi rank. |
PackedColwiseParallel | Một biến thể của ColwiseParallel cho các trọng số đã hợp nhất, chẳng hạn như up_proj và gate_proj được đóng gói vào gate_up_proj. Sử dụng split_factor khi một trọng số đóng gói một số lượng lớp khác với hai. |
PackedRowwiseParallel | Bản tương ứng theo hàng, dành cho các trọng số được đóng gói dọc theo chiều cuối. Nhân bản các tham số 1D. |
SequenceParallel | Nhân bản các tham số của module và chia nhỏ đầu vào của nó theo sequence_dim (mặc định là 1). Được sử dụng cho các chuẩn hóa hoạt động theo từng token, chẳng hạn như LayerNorm và RMSNorm. |
ReplicatedWithGradAllReduce | Nhân bản một tham số nhưng thực hiện all-reduce cho gradient của nó. Cần thiết cho các lớp chuẩn hóa nằm giữa một lớp theo cột và một lớp theo hàng, chuẩn hóa dọc theo một trục được chia nhỏ, trong đó mỗi rank chỉ nhìn thấy các head của riêng nó. |
AllReduceParallel | Thực hiện all-reduce trên đầu ra forward của Partial() của một module để chuyển sang Replicate(). Sử dụng nó như một điểm đồng bộ hóa cho module mà phép tính kết thúc bằng một tổng cục bộ. |
MlaKvAProjParallel | Tách đầu ra của kv_a_proj_with_mqa của cơ chế chú ý MLA kiểu DeepSeek-V2 và thực hiện all-reduce cho gradient của nửa RoPE, qua đó bỏ qua kv_b_proj và tránh việc giữ lại một phần gradient. Yêu cầu có qk_rope_head_dim trong cấu hình mô hình. |
MoeExpertsParallel | Các chuyên gia MoE song song tensor. Thực hiện all-reduce cho đầu ra của chuyên gia trong quá trình truyền thẳng và thêm các all-reduce ngược cho các trạng thái ẩn và trọng số định tuyến. |
MoeIdentityParallel | Chia trước đầu vào của một chuyên gia zero hoặc identity cho kích thước mesh, triệt tiêu phép all-reduce mà moe_tp_experts áp dụng ở phía sau. |
LocalParamsParallel | Chạy forward của một module với các tham số phân mảnh của chính nó dưới dạng tensor cục bộ. Sử dụng nó trên phần tử sở hữu một nn.Parameter trần, chẳng hạn như attention sinks, mà mục kế hoạch của chính nó phân mảnh. |
MoEParamShard | Phân mảnh trọng số chuyên gia MoE trên một vị trí đặt đã cho. Hỗ trợ tên grouped_gemm, trong đó shards_expert_dim=True cũng viết lại module.num_experts thành số lượng chuyên gia trên mỗi rank. |
EpRouterParallel | Che điểm router cho các chuyên gia không cục bộ và ánh xạ lại ID chuyên gia toàn cục thành ID cục bộ, để mỗi rank chỉ chạy các chuyên gia mà nó sở hữu. Yêu cầu num_experts phải chia hết cho kích thước mesh. |
RouterParallelMegaMoe | Biến thể Router cho DeepGEMM Mega MoE, trong đó các chuyên gia được điều phối bên trong kernel, vốn yêu cầu đầu ra của router không bị thay đổi. |
MoeTensorParalellMegaMoeExperts | Tầng chuyên gia chỉ dành cho suy luận cho DeepGEMM Mega MoE. Bỏ qua các bước đồng bộ gradient và truyền nhóm tiến trình vào module để kernel có thể thiết lập các bộ đệm chia sẻ trong lần forward đầu tiên. |
Chiến lược đóng gói
Weight packing kết hợp nhiều lớp tuyến tính thành một lớp lớn hơn duy nhất. Các chiến lược PackedColwiseParallel và PackedRowwiseParallel chia nhỏ (shard) các trọng lượng đã đóng gói một cách chính xác. Các chiến lược ColwiseParallel hoặc RowwiseParallel cơ bản sẽ chia nhỏ các trọng lượng đã đóng gói một cách không chính xác.
Ví dụ dưới đây đóng gói up_proj và gate_proj thành một module gate_up_proj duy nhất và yêu cầu chiến lược packed_rowwise để chia nhỏ gate_up_proj.
class Llama4TextExperts(nn.Module):
...
self.gate_up_proj = nn.Parameter(torch.zeros(self.num_experts, self.hidden_size, 2 * self.expert_dim))Sử dụng phép nhân ma trận theo lô trong bước forward để tính toán đầu ra của module gate_up_proj.
def forward(self, hidden_states):
...
gate_up = torch.bmm(hidden_states, self.gate_up_proj) # Compute the output of the gate_up_proj module
gate, up = gate_up.chunk(2, dim=-1) # Split the output into gate and upMột Shard thông thường chia chiều đó thành các khối liên tục, do đó một rank sẽ nhận được phần cuối của gate và phần đầu của up thay vì một lát cắt của từng phần. Các chiến lược đóng gói sử dụng _StridedShard với split_factor (mặc định là 2) để xen kẽ việc chia, đảm bảo mỗi rank nhận được một lát cắt tương ứng của cả hai nửa để chunk vẫn khớp nhau sau khi sharding.
Các chiến lược phân vùng tùy chỉnh
Kế thừa từ TensorParallelLayer trong distributed/tensor_parallel.py để tạo chiến lược phân vùng tùy chỉnh. Chỉ ghi đè các hook mà chiến lược của bạn cần, vì mỗi hook đều có mặc định không thực hiện gì (no-op).
| Hook | Mục đích |
|---|---|
validate_param | Từ chối một tham số mà chiến lược này không thể chia nhỏ, trước khi tải bất kỳ trọng số nào. |
shard_param | Thay thế một tham số bằng một placeholder DTensor để loader biết shard nào thuộc về rank này. |
transform_inputs_pre_forward | Phân phối lại các đầu vào của module theo bố cục mà hàm forward của nó mong đợi. |
context_around_forward | Bọc forward trong một trình quản lý ngữ cảnh, ví dụ để phơi bày các tensor cục bộ cho một kernel. |
transform_output_post_forward | Phân phối lại hoặc giảm đầu ra của module. |
should_use_local_tensors | Báo cáo rằng forward của module này cần các tensor thông thường thay vì DTensor. |
install_forward | Thay thế hoàn toàn module.forward. Chỉ ghi đè hàm này khi các hook ở trên không đủ, như cách ReplicatedWithGradAllReduce làm để đăng ký một backward hook. |
Ví dụ dưới đây đi qua một phiên bản rút gọn của ColwiseParallel.
Kế thừa từ
TensorParallelLayervà lưu trữ các vị trí (placements) mà chiến lược làm việc. Lớp cơ sở không định nghĩa__init__, vì vậy không có gì để gọisuper().class ColwiseParallel(TensorParallelLayer): def __init__(self, *, input_layouts=None, output_layouts=None, use_local_output: bool = True): self.input_layouts = input_layouts or Replicate() self.output_layouts = output_layouts if output_layouts is not None else Shard(-1) self.use_local_output = use_local_outputThực hiện
shard_paramđể bọc một tham số thành một placeholderDTensor. Nó chạy trên các meta tensor, do đódistribute_tensorchỉ xây dựng metadata và không di chuyển dữ liệu. Truyềnsrc_data_rank=Nonevì hiện tại chưa có tensor đầy đủ để phân tán.def shard_param(self, module, param, mesh): meta = module._parameters.get(param) if meta is None: return # Output features live on dim 0 for a 2D weight and dim -1 for a 1D bias placement = Shard(1) if isinstance(module, torch.nn.Embedding) else Shard(meta.ndim - 2) module._parameters[param] = torch.nn.Parameter( distribute_tensor(meta, mesh, [placement], src_data_rank=None), requires_grad=meta.requires_grad, )Thực hiện các phép biến đổi đầu vào và đầu ra.
install_forwardgọi chúng bao quanh hàm forward gốc của module, vì vậy chúng chỉ cần di chuyển tensor giữa các bố cục. Việc phân vùng theo cột kỳ vọng đầu vào được nhân bản và tạo ra đầu ra được chia mảnh trên chiều cuối.def transform_inputs_pre_forward(self, module, args, kwargs, mesh): x = args[0] if not isinstance(x, DTensor): x = DTensor.from_local(x, mesh, [self.input_layouts], run_check=False) if x.placements != (Replicate(),): x = x.redistribute(placements=[Replicate()]) return (x,) + args[1:], kwargs def transform_output_post_forward(self, module, output, mesh): if not isinstance(output, DTensor): output = DTensor.from_local(output, mesh, [Shard(-1)], run_check=False) if output.placements != (self.output_layouts,): output = output.redistribute(placements=[self.output_layouts]) return output.to_local() if self.use_local_output else outputColwiseParallelđược cung cấp sẵn bổ sung các đường dẫn nhanh trên cơ sở này, bỏ qua vòng lặpDTensorcho suy luậnnn.Linearthông thường và cho các module lượng tử hóa cần tensor cục bộ. Hãy đọc mã nguồn trước khi sao chép nếu chiến lược của bạn cần xử lý tương tự.Đăng ký chiến lược để
tp_plancó thể gọi tên nó. Việc đăng ký nhận một instance của chiến lược, do đó các placements bạn truyền vào__init__sẽ được lưu trữ cùng với tên. Lưu ý()bên dưới.import torch from transformers import AutoModelForCausalLM, DistributedConfig from transformers.distributed.parallel_utils import ALL_PARALLEL_STYLES ALL_PARALLEL_STYLES.register("colwise_custom", ColwiseParallel()) tp_plan = { "model.layers.*.self_attn.q_proj": "colwise_custom", ... } distributed_config = DistributedConfig(tp_size=4, tp_plan=tp_plan) model = AutoModelForCausalLM.from_pretrained( "meta-llama/Meta-Llama-3-8B-Instruct", dtype=torch.bfloat16, distributed_config=distributed_config, )
Benchmarks
Song song tensor tăng tốc độ suy luận đáng kể, đặc biệt là với kích thước lô lớn hoặc các chuỗi dài.
Biểu đồ này cho thấy tốc độ tăng tốc dự kiến cho một lần truyền tiến đơn lẻ trên Llama với độ dài chuỗi là 512.

Thiết kế triển khai
Transformers triển khai tensor parallelism theo cách độc lập với framework. Nó dựa vào DeviceMesh và DTensor từ torch.distributed để cung cấp một giao diện đơn giản, có thể mở rộng.
DeviceMesh
DeviceMesh tạo ra một lưới thiết bị nhiều chiều giao tiếp với nhau. Các chiến lược song song hóa khác nhau đòi hỏi các mẫu giao tiếp khác nhau. Hãy tạo một DeviceMesh với nhiều lưới con để xử lý các mẫu này.
import torch
from torch.distributed.device_mesh import init_device_mesh
# Create a 1D mesh of 4 accelerators
device_type = torch.accelerator.current_accelerator().type
device_mesh = init_device_mesh(device_type, (4,), mesh_dim_names=["tp"])Hầu hết các chiến lược song song hóa torch.distributed được áp dụng cho chính lưới (mesh) hoặc các lưới con của nó. Lưới tự động xử lý các mẫu giao tiếp.
DTensor
DTensor (Distributed Tensor) xử lý logic phân tán trên các thao tác tensor thông thường. Trọng số mô hình dưới tensor parallelism được lưu trữ dưới dạng DTensor, điều này cho phép một chiến lược mô tả giao tiếp như một sự thay đổi layout thay vì một tập thể hợp (collective) tường minh.
Thuộc tính placements cho PyTorch biết cách một tensor được bố trí trên các thiết bị trong một DeviceMesh. Nó chấp nhận các giá trị sau:
Shard(dimension)chia mộtDTensortrên một chiều cho trước theoDeviceMeshmà nó được xây dựng. Việc phân vùng theo cột chia chiều output-feature của trọng số và chiều duy nhất của bias.weight = DTensor.from_local(weight, device_mesh["tp"], placements=[Shard(0)]) # Shard the output features bias = DTensor.from_local(bias, device_mesh["tp"], placements=[Shard(-1)]) # Shard the ONLY dimensionPhân vùng theo hàng (row-wise partitioning) chia nhỏ chiều input-feature thay vào đó và nhân bản bias, vì bias được cộng một lần sau khi thực hiện phép giảm (reduction) chứ không phải trên mỗi rank.
weight = DTensor.from_local(weight, device_mesh["tp"], placements=[Shard(-1)]) # Shard the input features bias = DTensor.from_local(bias, device_mesh["tp"], placements=[Replicate()]) # Replicate bias across all GPUsReplicate()nhân bản mộtDTensortrênDeviceMesh, tạo ra một bản sao đầy đủ của tensor trên mỗi thiết bị.Partial()đánh dấu một tensor đang chờ giảm (reduction). Đầu ra forward của một lớp row-wise làPartial(), và việc phân phối lại nó sangReplicate()chính là thao tác phát lệnh all-reduce.
Tài nguyên
Phần Ultra-Scale Playbook về song song tensor cung cấp thêm chi tiết.
Kiểm tra hướng dẫn expert parallelism nếu bạn đang sử dụng mô hình mixture-of-experts (MoE). Các mô hình này hỗ trợ tensor parallelism và expert parallelism.
Đọc bài đăng trên blog Tensor Parallelism (TP) in Transformers: 5 Minutes to Understand để có cái nhìn tổng quan nhanh về tensor parallelism và tìm hiểu cách các cấu hình song song cột và hàng khác nhau.
Xem hướng dẫn huấn luyện Tensor parallelism để tìm hiểu cách sử dụng nó trong môi trường huấn luyện.