为什么大模型需要多张显卡?

一张显卡真的不行吗?探索大模型背后的硬件需求与技术挑战

DeepSeek-V3 671B

6710亿参数的超大规模模型

推理所需显存 约630 GB
单张A100/H100显卡 80 GB
至少需要8张80GB显卡协同工作

我们经常听到训练大模型需要多少张显卡、推理需要多少张显卡。那么,一张显卡真的不行吗?答案是:对于超大规模模型来说,确实不行!这其中的主要原因在于,大模型"大"的本质——它对显存的需求非常庞大。

以DeepSeek-V3 671B为例,这是一个拥有6710亿参数的超大规模模型。其完整版本在推理时至少需要8张配备80GB显存的显卡(如A100或H100)。如果只有一张显卡,根本无法将整个模型加载到显存中运行。

注意:对于有一些教程里说的24GB即可部署DeepSeek 671B,都是所谓的残血版本,实际是利用了DeepSeek的MoE技术,显存中只会放激活的专家,所以显存占用会显著降低,但推理速度也会显著变慢。

模型的显存占用分析

目前所有开源的大语言模型(LLM)都基于Transformer架构,并且采用了Decode-only的设计,即只使用了Transformer的解码器部分。以下是解码器的主要组成部分及其显存占用情况:

Transformer架构示意图

graph TD subgraph "Transformer解码器" A[输入Tokens] --> B[Embedding层] B --> C[Transformer层 x 61] C --> D[输出投影层] D --> E[输出Tokens] subgraph "单个Transformer层" F[多头注意力层\nMLA] --> G[归一化层] G --> H[前馈神经网络\nFFN/MoE] H --> I[归一化层] end end

解码器的主要层结构

  • 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
  • 3

    输出投影层

    将最终的隐藏状态映射回词汇表大小的输出空间

    显存占用:约883.75 MB

显存占用分布

Embedding层 883.75 MB
MLA层 (61层) 14.91 GB
普通MLP层 (3层) 1.1 GB
MoE层 (58层) 611.32 GB
输出投影层 883.75 MB
总显存需求 约630 GB

顶级显卡(A100/H100)单卡显存仅为80 GB

如何用多张显卡解决问题?

既然单张显卡无法加载完整的模型,那么如何利用多张显卡协同工作呢?以下是几种常见的分布式并行技术。

流水线并行

流水线并行的核心思想是将模型按层分割,分配到不同的设备上。例如,DeepSeek-V3的MoE层单层大小约为10.54 GB,而一张24 GB显存的显卡可以容纳两层MoE。

graph LR A[GPU 1] --> B[GPU 2] --> C[GPU 3] --> D[GPU 4] A[GPU 1] -->|"层1-15"| A B[GPU 2] -->|"层16-30"| B C[GPU 3] -->|"层31-45"| C D[GPU 4] -->|"层46-61"| D

优缺点

  • 实现简单直观
  • 适合层数较多的模型
  • 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))
graph TD A[完整模型] --> B[GPU 1: 词汇表部分1] A --> C[GPU 2: 词汇表部分2] A --> D[GPU 3: 词汇表部分3] A --> E[GPU 4: 词汇表部分4] B & C & D & E --> F[结果同步]

优缺点

  • 显著减少单设备显存占用
  • 适合大词汇表模型
  • 需要频繁同步结果

张量并行

张量并行进一步细分为列并行和行并行,旨在将单个张量的计算任务分配到多个设备上。这种方法能够有效地分解大型矩阵运算,提高计算效率。

列并行 (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
graph TD A[输入矩阵] --> B[列并行: 按列分割权重矩阵] B --> C[GPU 1: 计算部分1] B --> D[GPU 2: 计算部分2] C & D --> E[行并行: 结果合并] E --> F[输出矩阵]

优缺点

  • 高效处理大型矩阵运算
  • 计算负载均衡
  • 实现复杂
  • 需要精细的通信协调

MLP计算过程详解

MLP(多层感知机)是Transformer架构中的关键组件,也是显存占用的主要来源。以下是其计算过程的详细说明:

计算步骤

  1. 1
    第一阶段:矩阵乘法

    输入 x 分别与权重矩阵 w1 和 w3 进行矩阵乘法运算,得到两个形状为 (batch_size×18432) 的中间结果。

  2. 2
    第二阶段:逐元素乘法

    将上述两个中间结果进行逐元素相乘(element-wise multiplication),生成一个新的形状为 (batch_size×18432) 的输出。

  3. 3
    第三阶段:最终矩阵乘法

    将上一步的结果与权重矩阵 w2 再次进行矩阵乘法运算,最终得到一个形状为 (batch_size×7168) 的输出。

单GPU计算流程
graph TD A[输入 x: batch_size×7168] --> B[w1: 7168×18432] A --> C[w3: 7168×18432] B --> D[中间结果1: batch_size×18432] C --> E[中间结果2: batch_size×18432] D --> F{逐元素乘法} E --> F F --> G[中间结果3: batch_size×18432] G --> H[w2: 18432×7168] H --> I[输出: batch_size×7168]
多GPU并行计算流程
graph TD subgraph "GPU 1" A1[输入 x: batch_size×7168] --> B1[w1部分1: 7168×9216] A1 --> C1[w3部分1: 7168×9216] B1 --> D1[中间结果1部分: batch_size×9216] C1 --> E1[中间结果2部分: batch_size×9216] D1 --> F1{逐元素乘法} E1 --> F1 F1 --> G1[中间结果3部分: batch_size×9216] end subgraph "GPU 2" A2[输入 x: batch_size×7168] --> B2[w1部分2: 7168×9216] A2 --> C2[w3部分2: 7168×9216] B2 --> D2[中间结果1部分: batch_size×9216] C2 --> E2[中间结果2部分: batch_size×9216] D2 --> F2{逐元素乘法} E2 --> F2 F2 --> G2[中间结果3部分: batch_size×9216] end G1 --> H1[w2部分1: 9216×7168] G2 --> H2[w2部分2: 9216×7168] H1 --> I1[输出部分1: batch_size×7168] H2 --> I2[输出部分2: batch_size×7168] I1 & I2 --> J[All-Reduce合并] J --> K[最终输出: batch_size×7168]
张量并行如何优化

列并行:将 w1 和 w3 按列分割到不同GPU上,各自计算部分结果

行并行:将 w2 按行分割到不同GPU上,各自计算部分最终输出

通过 all-reduce 操作合并各GPU的计算结果

MoE技术:显存与性能的平衡

混合专家模型(Mixture of Experts,MoE)是一种特殊的神经网络架构,它通过"专家"机制大幅提升模型容量,同时保持计算效率。DeepSeek-V3 671B模型中的MoE层是显存占用的主要来源。

MoE的工作原理

MoE层包含多个"专家"(Expert)网络,每个专家都是一个完整的神经网络。但在推理过程中,只有部分专家会被激活,这就是所谓的"稀疏激活"机制。

graph TD A[输入] --> B[路由器] B --> C[专家1] B --> D[专家2] B --> E[专家3] B --> F[专家4] B --> G[...] B --> H[专家N] C & D & E & F & G & H --> I[输出]

MoE与显存优化

  • 稀疏激活:每次推理只激活部分专家,而非全部专家
  • 显存优化:可以只将激活的专家加载到显存中,大幅降低显存需求
  • 性能权衡:虽然显存占用降低,但需要频繁加载不同专家,导致推理速度变慢
  • "残血版"原理:24GB显卡可以运行DeepSeek 671B的原因就是利用了这种机制,但推理速度会显著降低

专家知识:DeepSeek-V3 671B模型中的MoE层包含8个专家,但每次只激活1-2个专家进行计算,这使得模型在保持巨大参数量的同时,实际计算量可以控制在合理范围内。

总结与延伸阅读

关键结论

  • 大模型需要多张显卡的根本原因是显存需求:以DeepSeek-V3 671B为例,完整模型需要约630GB显存,远超单卡能力
  • MoE层是显存占用的主要来源:在DeepSeek-V3中,MoE层占用了超过97%的显存
  • 多种并行技术协同工作:流水线并行、模型并行和张量并行各有优缺点,实际应用中常常结合使用
  • MoE技术提供了显存与性能的平衡点:通过稀疏激活机制,可以在有限显存下运行超大模型,但会牺牲推理速度