我们经常听到训练大模型需要多少张显卡、推理需要多少张显卡。那么,一张显卡真的不行吗?答案是:对于超大规模模型来说,确实不行!这其中的主要原因在于,大模型"大"的本质——它对显存的需求非常庞大。
以DeepSeek-V3 671B为例,这是一个拥有6710亿参数的超大规模模型。其完整版本在推理时至少需要8张配备80GB显存的显卡(如A100或H100)。如果只有一张显卡,根本无法将整个模型加载到显存中运行。
注意:对于有一些教程里说的24GB即可部署DeepSeek 671B,都是所谓的残血版本,实际是利用了DeepSeek的MoE技术,显存中只会放激活的专家,所以显存占用会显著降低,但推理速度也会显著变慢。
模型的显存占用分析
目前所有开源的大语言模型(LLM)都基于Transformer架构,并且采用了Decode-only的设计,即只使用了Transformer的解码器部分。以下是解码器的主要组成部分及其显存占用情况:
Transformer架构示意图
解码器的主要层结构
-
1
Embedding层
将输入的token转化为向量表示
显存占用:约883.75 MB
-
2
Transformer核心层
在DeepSeek-V3 671B中,有61层
-
MLA(多头注意力层):
负责捕捉输入序列中的全局依赖关系
显存占用:每层约250.46 MB,61层总计约14.91 GB
-
归一化层与残差连接:
对激活值进行归一化处理并加入残差连接
显存占用:可以忽略不计
-
FFN(前馈神经网络):
由普通MLP(多层感知机)和MoE(混合专家)结合实现
显存占用:
- 前3层为普通MLP,每层约378 MB,总计1.1 GB
- 后58层为MoE,每层约10.54 GB,总计约611.32 GB
-
MLA(多头注意力层):
-
3
输出投影层
将最终的隐藏状态映射回词汇表大小的输出空间
显存占用:约883.75 MB
显存占用分布
顶级显卡(A100/H100)单卡显存仅为80 GB
如何用多张显卡解决问题?
既然单张显卡无法加载完整的模型,那么如何利用多张显卡协同工作呢?以下是几种常见的分布式并行技术。
流水线并行
流水线并行的核心思想是将模型按层分割,分配到不同的设备上。例如,DeepSeek-V3的MoE层单层大小约为10.54 GB,而一张24 GB显存的显卡可以容纳两层MoE。
优缺点
- 实现简单直观
- 适合层数较多的模型
- GPU利用率不均衡
- 通信开销较大
模型并行
模型并行的核心思想是将模型的不同部分分配到不同设备上,而不是像流水线并行那样按层划分。每个设备仅负责词汇表的一部分,显著减少单设备显存占用。
self.part_vocab_size = (vocab_size // world_size)
self.vocab_start_idx = rank * self.part_vocab_size
self.vocab_end_idx = self.vocab_start_idx + self.part_vocab_size
self.weight = nn.Parameter(torch.empty(self.part_vocab_size, self.dim))
优缺点
- 显著减少单设备显存占用
- 适合大词汇表模型
- 需要频繁同步结果
张量并行
张量并行进一步细分为列并行和行并行,旨在将单个张量的计算任务分配到多个设备上。这种方法能够有效地分解大型矩阵运算,提高计算效率。
列并行 (Column Parallelism)
将输出维度分片到不同设备,每个设备计算部分输出。
示例:如果有8个GPU,原本形状为(7168×18432)的权重矩阵被分割为8个(7168×2304)的子矩阵。
每个设备上的输入数据与本地子矩阵相乘,得到(batch_size×2304)的局部输出。
行并行 (Row Parallelism)
将输入维度分片到不同设备,通过all-reduce操作合并结果。
示例:如果有8个GPU,输入维度被分割为(input_dim÷8)大小的子矩阵。
各设备计算完成后,通过all-reduce操作将局部结果汇总,得到完整输出。
class MLP(nn.Module):
def __init__(self, dim: int, inter_dim: int):
super().__init__()
self.w1 = ColumnParallelLinear(dim, inter_dim) # 列并行
self.w2 = RowParallelLinear(inter_dim, dim) # 行并行
self.w3 = ColumnParallelLinear(dim, inter_dim) # 列并行
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.w2(F.silu(self.w1(x)) * self.w3(x))
# 列并行实现
class ColumnParallelLinear(Linear):
def __init__(self, in_features: int, out_features: int, bias: bool = False, dtype=None):
self.part_out_features = out_features // world_size
super().__init__(in_features, self.part_out_features, bias, dtype)
# 行并行实现
class RowParallelLinear(Linear):
def forward(self, x: torch.Tensor) -> torch.Tensor:
y = linear(x, self.weight)
if world_size > 1:
dist.all_reduce(y) # 合并各设备结果
return y
优缺点
- 高效处理大型矩阵运算
- 计算负载均衡
- 实现复杂
- 需要精细的通信协调
MLP计算过程详解
MLP(多层感知机)是Transformer架构中的关键组件,也是显存占用的主要来源。以下是其计算过程的详细说明:
计算步骤
-
1
第一阶段:矩阵乘法
输入 x 分别与权重矩阵 w1 和 w3 进行矩阵乘法运算,得到两个形状为 (batch_size×18432) 的中间结果。
-
2
第二阶段:逐元素乘法
将上述两个中间结果进行逐元素相乘(element-wise multiplication),生成一个新的形状为 (batch_size×18432) 的输出。
-
3
第三阶段:最终矩阵乘法
将上一步的结果与权重矩阵 w2 再次进行矩阵乘法运算,最终得到一个形状为 (batch_size×7168) 的输出。
单GPU计算流程
多GPU并行计算流程
张量并行如何优化
列并行:将 w1 和 w3 按列分割到不同GPU上,各自计算部分结果
行并行:将 w2 按行分割到不同GPU上,各自计算部分最终输出
通过 all-reduce 操作合并各GPU的计算结果
MoE技术:显存与性能的平衡
混合专家模型(Mixture of Experts,MoE)是一种特殊的神经网络架构,它通过"专家"机制大幅提升模型容量,同时保持计算效率。DeepSeek-V3 671B模型中的MoE层是显存占用的主要来源。
MoE的工作原理
MoE层包含多个"专家"(Expert)网络,每个专家都是一个完整的神经网络。但在推理过程中,只有部分专家会被激活,这就是所谓的"稀疏激活"机制。
MoE与显存优化
-
稀疏激活:每次推理只激活部分专家,而非全部专家
-
显存优化:可以只将激活的专家加载到显存中,大幅降低显存需求
-
性能权衡:虽然显存占用降低,但需要频繁加载不同专家,导致推理速度变慢
-
"残血版"原理:24GB显卡可以运行DeepSeek 671B的原因就是利用了这种机制,但推理速度会显著降低
专家知识:DeepSeek-V3 671B模型中的MoE层包含8个专家,但每次只激活1-2个专家进行计算,这使得模型在保持巨大参数量的同时,实际计算量可以控制在合理范围内。
总结与延伸阅读
关键结论
-
大模型需要多张显卡的根本原因是显存需求:以DeepSeek-V3 671B为例,完整模型需要约630GB显存,远超单卡能力
-
MoE层是显存占用的主要来源:在DeepSeek-V3中,MoE层占用了超过97%的显存
-
多种并行技术协同工作:流水线并行、模型并行和张量并行各有优缺点,实际应用中常常结合使用
-
MoE技术提供了显存与性能的平衡点:通过稀疏激活机制,可以在有限显存下运行超大模型,但会牺牲推理速度