讲的是Megatron-LM中张量并行的核心原理和通信开销分析。你会学到列并行和行并行对MLP层的切分方式、Attention中QKV投影的并行切分、前向和反向传播中All-Reduce通信量的计算、不同并行度的通信效率对比,还会讲到张量并行和其他并行策略的组合。适合做大模型分布式训练的算法工程师,帮你理解张量并行的底层逻辑做对并行配置。

岗位关联

需要学习张量并行 Megatron-LM:列并行/行并行 MLP 与 Attention 切分通信量分析,标注管理本质是数据工程,需理解数据管道和质量控制。本篇涵盖数据清洗/ETL/数据仓库/特征工程,帮助你构建数据管道与洞察。

目录
  1. 可视化演示
  2. Megatron-LM张量并行核心思想与切分维度
  3. 列并行对MLP第一层权重的切分机制
  4. 行并行对MLP第二层权重的切分与AllReduce
  5. Attention层QKV投影的并行切分
  6. 前向反向AllReduce通信量分析与效率对比
  7. 张量并行与其他并行策略的组合配置
  8. AI 应用场景
  9. Python 演示
  10. 常见问答
  11. 常见误区
  12. 参考来源

可视化演示

展示MLP层列并行W1+行并行W2切分、Attention多头QKV并行切分、前向反向AllReduce通信量分析,点击按钮切换。

本节配有交互动效(进入视口后自动加载,约 3KB)。

MLP层列并行与行并行切分 W1列并行按列切分每卡持有部分列GeLU独立计算 W2行并行按行切分各卡部分输出AllReduce汇总 Y=GeLU(XW1) → split → Z=YW2 → AllReduce 列行并行组合仅需一次AllReduce通信

Megatron-LM张量并行核心思想与切分维度

张量并行(Tensor Parallelism, TP)是Megatron-LM提出的层内并行策略,其核心思想是将单个Transformer层内部的权重矩阵按行或列切分到多张GPU上,每张GPU只持有该层权重的一部分,而非像流水线并行那样按层切分。这意味着同一层的计算被分散到多卡上协同完成,每卡只负责矩阵运算的一部分。

理解张量并行的关键在于区分它与数据并行和流水线并行的本质差异。数据并行是每卡持有完整模型副本,各自处理不同的数据mini-batch,梯度同步在反向传播结束后进行。流水线并行是将不同层分配到不同卡上,数据以micro-batch的形式在各卡间流水线式传递。而张量并行是将同一层内部的权重矩阵切开,每卡只算矩阵乘法的一部分,计算过程中就需要频繁通信。

张量并行的切分方向有两种:列并行(Column Parallelism)和行并行(Row Parallelism)。列并行将权重矩阵按列方向切分,每卡持有部分列块,用完整输入乘以各自的列块后输出按隐藏维度切分。行并行将权重矩阵按行方向切分,每卡持有部分行块,用按行切分的输入乘以各自的行块后输出需要AllReduce归约。两种切分方式的通信模式不同,Megatron-LM巧妙地将它们组合使用来最小化通信次数。

通信只发生在层边界而不在矩阵乘法中间发生,这是张量并行高效的关键。在矩阵乘法Y=XW的计算过程中,每卡独立执行各自的局部矩阵乘法,不需要任何通信。通信只在层的输入输出边界发生:列并行输出需要AllGather拼接,行并行输出需要AllReduce归约。这种设计使得计算和通信可以较好地重叠,最大化GPU利用率。

python25 行
# 模拟张量并行的核心思想:将权重矩阵切分到多卡import numpy as np W = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]], dtype=float)X = np.array([[1,0,1,0]], dtype=float)Y_full = X @ Wprint(f'完整计算 Y = XW: {Y_full}') # 列并行W_col_0 = W[:, :2]W_col_1 = W[:, 2:]Y_col_0 = X @ W_col_0Y_col_1 = X @ W_col_1Y_col = np.concatenate([Y_col_0, Y_col_1], axis=1)print(f'列并行AllGather: {Y_col}')print(f'一致: {np.allclose(Y_full, Y_col)}') # 行并行W_row_0 = W[:2, :]W_row_1 = W[2:, :]Y_row_0 = X[:, :2] @ W_row_0Y_row_1 = X[:, 2:] @ W_row_1Y_row = Y_row_0 + Y_row_1print(f'行并行AllReduce: {Y_row}')print(f'一致: {np.allclose(Y_full, Y_row)}')

列并行对MLP第一层权重的切分机制

Transformer的MLP块通常由两层线性层组成:第一层 Y = GeLU(XW1),第二层 Z = YW2。在Megatron-LM中,第一层W1采用列并行切分。假设W1的形状是(hidden_size, 4*hidden_size),列并行将其按第二维度(列方向)切分为N个块,每卡持有形状为(hidden_size, 4*hidden_size/N)的列块。

列并行的计算过程非常直观:每卡用完整的输入X乘以自己持有的W1列块,得到部分输出。各卡的输出按隐藏维度自然切分,拼接起来就是完整的中间结果。这个拼接操作就是AllGather通信——每卡将自己的部分广播给其他卡,同时接收其他卡的部分。

用一个类比来理解:就像把一道大题的试卷按题号分给四个同学各做一部分,做完把各自答案拼成完整答卷上交。分题做省了每人工作量但拼卷子那次沟通就是AllGather通信。

列并行的显存优势在于每卡只存储W1的1/N列。但代价是前向传播需要一次AllGather通信来拼接输出。GeLU激活函数在AllGather之前执行,每卡对自己的部分输出做激活,不需要额外通信。

python14 行
# 模拟列并行MLP第一层import numpy as nphidden_size, ffn_size, N = 8, 32, 4np.random.seed(42)W1 = np.random.randn(hidden_size, ffn_size) * 0.1X = np.random.randn(4, hidden_size)Y_full = np.maximum(0, X @ W1)partial_outputs = []for i in range(N):    W1_col = W1[:, i*(ffn_size//N):(i+1)*(ffn_size//N)]    Y_partial = np.maximum(0, X @ W1_col)    partial_outputs.append(Y_partial)Y_gathered = np.concatenate(partial_outputs, axis=1)print(f'一致: {np.allclose(Y_full, Y_gathered)}')

行并行对MLP第二层权重的切分与AllReduce

MLP第二层W2的形状是(4*hidden_size, hidden_size),Megatron-LM对其采用行并行切分。行并行将W2按行方向切分为N个块,每卡持有形状为(4*hidden_size/N, hidden_size)的行块。行并行的输入也需要按对应维度切分——每卡只拿到中间维度的一部分。

行并行的计算过程:每卡用部分输入乘以自己持有的W2行块,得到部分加和结果。每卡输出形状与完整输出相同,但各自只包含部分贡献。需要AllReduce通信将所有卡的部分加和累加起来。

类比理解:就像四个会计各算一个季度的部分账目,最后要把四个部分账加总成全年账。加总那次对账就是AllReduce。

Megatron-LM将列并行和行并行巧妙组合:MLP第一层列并行输出的切分维度恰好是第二层行并行输入需要的切分维度。两层组合只需一次AllGather和一次AllReduce,而非四次通信。

python14 行
# 模拟行并行MLP第二层import numpy as nphidden_size, ffn_size, N = 8, 32, 4np.random.seed(42)W2 = np.random.randn(ffn_size, hidden_size) * 0.1Y_parts = [np.random.randn(4, ffn_size//N) for _ in range(N)]Y_full = np.concatenate(Y_parts, axis=1)Z_full = Y_full @ W2partial_sums = []for i in range(N):    W2_row = W2[i*(ffn_size//N):(i+1)*(ffn_size//N), :]    partial_sums.append(Y_parts[i] @ W2_row)Z_reduced = sum(partial_sums)print(f'一致: {np.allclose(Z_full, Z_reduced)}')

Attention层QKV投影的并行切分

Multi-Head Attention中QKV投影权重Wq、Wk、Wv可以按列切分实现列并行。将h个头均分到N卡上,每卡持有h/N个头的QKV权重。每卡用完整输入X乘以各自的QKV列块,得到各自负责的头对应的Q、K、V矩阵。

多头注意力天然适合列并行切分,因为不同头之间的计算是完全独立的。每个注意力头独立计算Attention(Q,K,V)=softmax(QK^T/sqrt(d_k))V,不需要与其他头通信。这就像一个合唱团分四个声部各唱各的旋律互不干扰。

在每卡独立计算完各自注意力头的输出后,需要将多头输出拼接。拼接后通过输出投影Wo进行行并行计算。Wo按行切分后每卡用各自头的输出乘以Wo行块,最后AllReduce归约得到最终Attention输出。

整个Attention层前向只需两次通信:QKV列并行后的AllGather和Wo行并行后的AllReduce。与MLP层的通信模式完全一致。

python14 行
# 模拟Attention QKV列并行import numpy as nphidden_size, num_heads, N = 16, 4, 2head_dim = hidden_size // num_headsnp.random.seed(42)X = np.random.randn(4, hidden_size)Wq = np.random.randn(hidden_size, hidden_size) * 0.1heads_per_gpu = num_heads // Nfor gpu in range(N):    start = gpu * heads_per_gpu * head_dim    end = (gpu + 1) * heads_per_gpu * head_dim    Q = X @ Wq[:, start:end]    print(f'GPU{gpu} Q形状: {Q.shape}')    # 每卡独立计算注意力无需通信

前向反向AllReduce通信量分析与效率对比

单个Transformer层前向传播:列并行需AllGather,通信量为2*batch*seq*4*hidden*(N-1)/N字节;行并行需AllReduce,通信量为2*batch*seq*hidden*2*(N-1)/N字节。关键发现:通信量与TP度N无关,主要取决于batch_size、seq_len和hidden_size。

反向传播的通信模式与前向相反:列并行反向需ReduceScatter,行并行反向需AllGather。通信量与前向相同。单个Transformer块(前向+反向)总通信量约为前向的两倍。

虽然通信量与N无关,但每卡计算量与N成反比。TP度越高通信占比越高。以GPT-3 175B为例:TP=8时单块前向通信量约140GB,NVLink 600GB/s下约233ms,通信占比已相当显著。

TP度通常等于单机GPU数(如8),因为机内NVLink带宽远高于跨机网络带宽。超过最优TP度后通信占比过高反而降低效率。

python11 行
# 通信量分析import numpy as npdef tp_analysis(bs, seq, h, tp, bw):    comm = 10*bs*seq*h*(tp-1)/tp    flops = 12*h**2*seq*bs/tp    comm_ms = comm/(bw*1e9)*1000    comp_ms = flops/(312e12)*1000    return comm/1e9, comm_ms, comp_ms, comm_ms/(comm_ms+comp_ms)for tp in [2,4,8,16]:    g,ct,cpt,r = tp_analysis(32,2048,12288,tp,600)    print(f'TP={tp}: {g:.0f}GB, 通信{ct:.0f}ms, 计算{cpt:.0f}ms, 占比{r:.1%}')

张量并行与其他并行策略的组合配置

张量并行从不单独使用,而是与数据并行DP和流水线并行PP组合形成3D并行。TP因通信频繁必须限制在机内NVLink高带宽域(600GB/s),跨机维度交给PP和DP。TP度通常等于单机GPU数。

典型3D并行配置:机内8卡TP=8,跨机PP将不同层分配到不同机器,最外层DP处理不同mini-batch。总GPU数=TP*PP*DP。例如1024卡配置TP=8,PP=16,DP=8。

序列并行是Megatron-LM v3引入的优化,将LayerNorm和Dropout也纳入序列维度切分,用AllGather和ReduceScatter替代AllReduce,使激活显存随TP度线性下降。

配置建议:TP度等于单机GPU数,PP度根据层数和跨机带宽调整,DP度用剩余GPU数。长序列训练开启序列并行,显存紧张时结合激活重计算。目标MFU将通信占比控制在20%以下。

python10 行
# 3D并行配置分析def config_analysis(gpus, params_b, seq, tp, pp, dp):    assert tp*pp*dp == gpus    h = int((params_b*1e9/24)**0.5)    mem = params_b*1e9*4/(tp*pp*1e9) + 12*h*seq*4/tp/1e9    tp_comm = 10*1*seq*h*(tp-1)/tp/1e9    return mem, tp_commfor tp,pp,dp in [(8,16,8),(8,128,1),(4,32,8)]:    m,c = config_analysis(1024,175,2048,tp,pp,dp)    print(f'TP={tp},PP={pp},DP={dp}: 显存{m:.1f}GB, TP通信{c:.1f}GB')

AI 应用场景

大模型训练张量并行切分配置优化

阶段:模型训练

解决的问题:大模型单层权重放不下单卡且层内通信开销大,通过Megatron-LM列并行行并行搭配切分MLP和Attention层,在满足显存约束的同时最小化AllReduce通信量提升训练MFU。

具体案例:训练千亿参数模型时MLP第一层用列并行第二层用行并行,这样两层的AllGather和AllReduce在块内首尾相接只需两次通信而非四次,配合Attention多头列并行消除中间通信,TP等于八填满机内NVLink域,通信占比从四成降到两成,MFU从三成五提升到五成,单步迭代时间缩短约三成。

Python 演示

python55 行
import numpy as np np.random.seed(42)hidden_size, ffn_size, num_heads = 16, 64, 4head_dim = hidden_size // num_headsseq_len, batch_size, N = 8, 2, 2tokens = batch_size * seq_lenX = np.random.randn(tokens, hidden_size) W1 = np.random.randn(hidden_size, ffn_size) * 0.01W2 = np.random.randn(ffn_size, hidden_size) * 0.01Wq = np.random.randn(hidden_size, hidden_size) * 0.01Wk = np.random.randn(hidden_size, hidden_size) * 0.01Wv = np.random.randn(hidden_size, hidden_size) * 0.01Wo = np.random.randn(hidden_size, hidden_size) * 0.01 # 完整计算mlp_out = np.maximum(0, X @ W1) @ W2Q, K, V = X @ Wq, X @ Wk, X @ WvQ = Q.reshape(tokens, num_heads, head_dim).transpose(0, 2, 1)K = K.reshape(tokens, num_heads, head_dim).transpose(0, 2, 1)V = V.reshape(tokens, num_heads, head_dim).transpose(0, 2, 1)scores = np.matmul(Q, K.transpose(0, 2, 1)) / np.sqrt(head_dim)attn = np.exp(scores) / np.exp(scores).sum(axis=-1, keepdims=True)attn_out = np.matmul(attn, V).transpose(0, 2, 1).reshape(tokens, hidden_size)layer_out = mlp_out + attn_out @ Wo # TP并行计算W1_cols = [W1[:, i*(ffn_size//N):(i+1)*(ffn_size//N)] for i in range(N)]W2_rows = [W2[i*(ffn_size//N):(i+1)*(ffn_size//N), :] for i in range(N)]mlp_parts = []for i in range(N):    y = np.maximum(0, X @ W1_cols[i])    mlp_parts.append(y @ W2_rows[i])mlp_tp = sum(mlp_parts) hpg = num_heads // NWq_cols = [Wq[:, i*hpg*head_dim:(i+1)*hpg*head_dim] for i in range(N)]Wk_cols = [Wk[:, i*hpg*head_dim:(i+1)*hpg*head_dim] for i in range(N)]Wv_cols = [Wv[:, i*hpg*head_dim:(i+1)*hpg*head_dim] for i in range(N)]Wo_rows = [Wo[i*hpg*head_dim:(i+1)*hpg*head_dim, :] for i in range(N)]attn_parts = []for i in range(N):    qi = (X @ Wq_cols[i]).reshape(tokens, hpg, head_dim).transpose(0, 2, 1)    ki = (X @ Wk_cols[i]).reshape(tokens, hpg, head_dim).transpose(0, 2, 1)    vi = (X @ Wv_cols[i]).reshape(tokens, hpg, head_dim).transpose(0, 2, 1)    si = np.matmul(qi, ki.transpose(0, 2, 1)) / np.sqrt(head_dim)    ai = np.exp(si) / np.exp(si).sum(axis=-1, keepdims=True)    oi = np.matmul(ai, vi).transpose(0, 2, 1).reshape(tokens, hpg*head_dim)    attn_parts.append(oi @ Wo_rows[i])attn_tp = sum(attn_parts) layer_tp = mlp_tp + attn_tpprint(f'完整: {layer_out.shape}, TP={N}: {layer_tp.shape}')print(f'一致: {np.allclose(layer_out, layer_tp, atol=1e-6)}')
目的:完整模拟Megatron-LM张量并行前向传播,包括MLP列并行+行并行和Attention QKV列并行+输出行并行
  1. W1_cols = [W1[:, i*(ffn_size//N):(i+1)*(ffn_size//N)] for i in range(N)]将MLP第一层权重W1按列切分为N块,每卡持有一个列块
  2. W2_rows = [W2[i*(ffn_size//N):(i+1)*(ffn_size//N), :] for i in range(N)]将MLP第二层权重W2按行切分为N块,每卡持有一个行块
  3. y = np.maximum(0, X @ W1_cols[i])列并行:每卡用完整输入X乘以自己的W1列块并执行ReLU激活
  4. mlp_parts.append(y @ W2_rows[i])行并行:每卡用激活后的部分输入乘以W2行块得到部分加和
  5. mlp_tp = sum(mlp_parts)行并行输出AllReduce归约各卡部分加和得到完整MLP输出
  6. Wq_cols = [Wq[:, i*hpg*head_dim:(i+1)*hpg*head_dim] for i in range(N)]QKV投影权重按注意力头维度列切分,每卡持有部分头
  7. si = np.matmul(qi, ki.transpose(0, 2, 1)) / np.sqrt(head_dim)每卡独立计算自己注意力头的注意力分数,无需跨卡通信
  8. attn_parts.append(oi @ Wo_rows[i])Attention输出投影Wo行并行,每卡用各自头的输出乘以Wo行块
  9. attn_tp = sum(attn_parts)Attention行并行输出AllReduce归约合并各卡多头结果
  10. print(f'一致: {np.allclose(layer_out, layer_tp, atol=1e-6)}')验证TP并行计算结果与单卡完整计算数值一致

常见问答

张量并行和流水线并行的本质区别是什么?
张量并行切的是单层内部的权重矩阵,每卡持有同一层的一部分权重,计算过程中需要频繁通信。流水线并行切的是层间维度,每卡持有完整的若干层,通信只在相邻阶段的micro-batch传递时发生。
为什么Megatron-LM要将MLP第一层和第二层分别用列并行和行并行?
这种搭配使得列并行输出的切分维度恰好是行并行输入需要的切分维度,不需要额外通信来重新分片。两层组合只需一次AllGather和一次AllReduce。
张量并行的通信量与TP度有什么关系?
通信量几乎与TP度无关,主要取决于batch_size、seq_len和hidden_size。但每卡计算量与N成反比,N越大通信占比越高。
序列并行如何与张量并行协同降低显存?
序列并行将LayerNorm和Dropout也纳入序列维度切分,每卡只处理部分token的激活,用AllGather和ReduceScatter替代AllReduce,使激活显存随TP度线性下降。
为什么Attention层的多头计算天然适合张量并行?
不同注意力头之间的计算完全独立,每个头有自己的QKV投影并独立计算注意力输出,不需要与其他头交换信息。

常见误区

误区认为张量并行就是把模型按层切开每卡放几层

正解按层切是流水线并行,张量并行切的是单层内部的权重矩阵按行或列拆分,每卡持有同一层的一部分权重。

为何常见:都叫并行容易混淆,且直觉上切模型就是切层。

误区认为列并行和行并行可以随便选,效果一样

正解列并行和行并行的通信原语不同,搭配顺序影响通信次数,Megatron-LM特意用列并行接行并行让两层共享一次AllReduce来减半通信。

为何常见:只看切分结果都是权重分到多卡,忽视了切分方向决定的通信原语差异。

误区认为张量并行度越高越好,可以无限增大切分卡数

正解TP通信量与隐藏维和序列长度相关而非TP度,但TP度越高每卡计算量越少通信占比上升,且TP跨机时带宽不足会严重拖慢。

为何常见:看到通信量公式与TP度无关就误以为可以无限切。

参考来源