部署架构
1. 分布式训练与推理架构总览
训练与部署全链路
数据准备 → 分布式训练 → 模型导出 → 量化/压缩 → 推理部署
↑ ↑
并行策略选择 服务架构设计
QUESTION 面试高频:大模型训练和推理分别用什么并行策略?
- 训练阶段:DP + TP + PP(3D 并行),目标是最大化吞吐量
- 推理阶段:TP(张量并行)为主,PP 和 EP(专家并行)为辅,目标是降低延迟
2. 推理部署架构
部署架构的核心挑战
LLM 推理部署面临三大挑战:
- 显存容量:模型权重 + KV Cache + 激活值需要大量显存
- 推理延迟:用户要求低 TTFT 和低 TPOT
- 系统吞吐:在线服务需要高并发、高吞吐
部署选型因素
| 因素 | 考量 |
|---|---|
| 模型大小 | 7B/13B 可单卡,70B+ 需要多卡 |
| 序列长度 | 短文本 vs 长上下文(128K+) |
| 并发量 | 低并发延迟优先 vs 高并发吞吐优先 |
| 硬件预算 | 消费级 GPU vs A100/H100 |
| 延迟要求 | 实时交互 vs 批量处理 |
QUESTION 面试题:部署一个 70B 模型的推理服务,需要考虑哪些因素?
- 硬件:70B FP16 需要 ~140 GB 显存,至少 2×A100-80G 或 4×A100-40G
- 并行策略:张量并行(TP)切分模型到多卡,或量化(INT4 降至 ~35 GB)
- 框架:vLLM / TensorRT-LLM / TGI
- KV Cache 管理:GQA + PagedAttention
- 批处理:Continuous Batching 提高吞吐
- 量化:AWQ 4-bit 可将显存降至 ~35 GB,配合 1-2 张 GPU
主流推理框架对比
| 特性 | vLLM | TGI | TensorRT-LLM | SGLang | llama.cpp |
|---|---|---|---|---|---|
| 开发者 | UC Berkeley | HuggingFace | NVIDIA | LMSYS | 社区 |
| PagedAttention | 原生 | 类似机制 | 有 | RadixAttention | 无 |
| Continuous Batching | 支持 | 支持 | 支持 | 支持 | 有限 |
| Prefix Caching | 支持 | 支持 | 支持 | 支持(更高效) | 无 |
| 投机解码 | 支持 | 支持 | 支持 | 支持 | 有限 |
| 量化支持 | GPTQ/AWQ/FP8 | GPTQ/AWQ | 全格式 | GPTQ/AWQ/FP8 | GGUF |
| 硬件支持 | NVIDIA/AMD | NVIDIA/AMD | NVIDIA | NVIDIA/AMD | CPU/GPU/Apple |
| 易用性 | 高 | 高 | 中 | 高 | 高 |
| 适用场景 | 通用在线服务 | HF 生态 | 极致性能 | 高吞吐研究 | 消费级/本地 |
框架选型决策
部署需求分析
│
├─ 本地/消费级硬件 ──→ llama.cpp (GGUF)
│
├─ 在线服务 ──────────┐
│ ├─ 追求极致性能 ──→ TensorRT-LLM
│ ├─ HF 生态集成 ──→ TGI
│ └─ 通用/灵活 ──→ vLLM
│
└─ 研究实验 ──────────→ SGLang
3. 硬件配置基础
GPU 显存需求估算
| 模型 | 参数量 | FP16 | INT8 | INT4 |
|---|---|---|---|---|
| 7B | 7B | 14 GB | 7 GB | 3.5 GB |
| 13B | 13B | 26 GB | 13 GB | 6.5 GB |
| 34B | 34B | 68 GB | 34 GB | 17 GB |
| 70B | 70B | 140 GB | 70 GB | 35 GB |
| 405B | 405B | 810 GB | 405 GB | 203 GB |
总显存需求
以 LLaMA-3-8B (FP16, Batch=32, seq_len=4096) 为例:
| 组件 | 显存 |
|---|---|
| 模型权重 | 16 GB |
| KV Cache | ~32 GB(GQA, 8 KV heads) |
| 激活值 | 2-4 GB |
| 框架开销 | 1-2 GB |
| 总计 | ~52 GB |
主流 GPU 规格
| GPU | 显存 | 带宽 | 算力 (FP16) | 适用模型 |
|---|---|---|---|---|
| RTX 4090 | 24 GB | 1 TB/s | 83 TFLOPS | 7B FP16 / 13B INT4 |
| A100 40GB | 40 GB | 1.6 TB/s | 312 TFLOPS | 13B FP16 / 70B INT4 (2卡) |
| A100 80GB | 80 GB | 2 TB/s | 312 TFLOPS | 34B FP16 / 70B INT4 |
| H100 80GB | 80 GB | 3.4 TB/s | 990 TFLOPS | 70B FP16 (2卡) / 405B INT4 |
| H200 141GB | 141 GB | 4.8 TB/s | 990 TFLOPS | 70B FP16 单卡 |
QUESTION 面试题:如何估算部署一个模型的 GPU 显存需求? 总显存 = 模型权重 + KV Cache + 激活值 + 框架开销:
- 模型权重:参数量 x dtype 字节数(7B FP16 = 14 GB)
- KV Cache:
- 激活值:通常 2-4 GB
- 框架开销:1-2 GB 经验法则:推理服务总显存约等于模型权重 x 1.5~3(取决于并发量和序列长度)
Token 与模型参数
Token 的概念
Token 是模型处理文本的基本单位。文本经 Tokenizer 编码为 Token ID。
| 语言 | 大约比例 | 说明 |
|---|---|---|
| 英文 | ~1 Token = 4 字符 | 常见词完整编码 |
| 中文 | ~1 Token = 1-2 字 | BPE 对中文效率较低 |
| 代码 | ~1 Token = 3-4 字符 | 取决于代码语言 |
模型参数分布
以 Transformer 模型为例:
| 组件 | 参数占比 | 说明 |
|---|---|---|
| Embedding | ~5-10% | 词嵌入矩阵 |
| Attention (QKV + O) | ~30-40% | |
| FFN (Up + Gate + Down) | ~50-60% | (LLaMA 风格) |
| LayerNorm / RMSNorm | <1% | 可忽略 |
QUESTION 面试题:7B 模型的参数量是怎么算出来的? 以 LLaMA-2-7B 为例(32 层, hidden_dim=4096, intermediate_dim=11008, vocab_size=32000):
- Embedding:
- 每层 Attention: (Q/K/V/O 投影)
- 每层 FFN: (Up/Gate/Down)
- 32 层合计:
- LM Head:
- 总计约 6.7B(约 7B)
4. 推理并行策略
Tensor Parallelism (TP, 张量并行)
将模型权重在多张 GPU 上切分:
模型权重矩阵 W (4096 x 4096)
┌─────────────┬─────────────┐
│ GPU 0 │ GPU 1 │
│ W[:, :2048] │ W[:, 2048:] │
└─────────────┴─────────────┘
前向传播:
1. 每个 GPU 计算部分结果
2. All-Reduce 通信合并结果
Pipeline Parallelism (PP, 流水线并行)
将模型按层切分到不同 GPU:
GPU 0: Layer 0-15 → GPU 1: Layer 16-31 → GPU 2: Layer 32-47 → GPU 3: Layer 48-63
并行策略对比
| 维度 | 张量并行 (TP) | 流水线并行 (PP) |
|---|---|---|
| 切分方式 | 权重矩阵按列/行切分 | 模型按层切分 |
| 通信模式 | All-Reduce | 点对点 (P2P) |
| 通信频率 | 每层 2 次 | 每 stage 1 次 |
| 适用 | 单机内(NVLink) | 跨机(网络) |
| 问题 | 通信频繁 | 流水线气泡 |
并行策略选择
| 模型大小 | 推荐 | 配置 |
|---|---|---|
| 7B FP16 | 单卡 | A100 40GB+ |
| 13B FP16 | TP=2 或单卡 | 2xA100-40G 或 INT4 单卡 |
| 70B FP16 | TP=4 或 INT4 TP=2 | 4xA100-80G 或 2xA100-80G(INT4) |
| 405B FP16 | TP=8 或 TP=4+PP=2 | 8xH100-80G |
5. 数据并行方案详解
5.1 DataParallel (DP)
GPU 0 (Master)
/ | \
GPU 1 GPU 2 GPU 3
(复制模型) (复制模型) (复制模型)
↓ ↓ ↓
前向计算 前向计算 前向计算
↓ ↓ ↓
梯度汇总到 GPU 0 (AllReduce)
↓
GPU 0 更新参数 → 广播给所有 GPU
5.2 DistributedDataParallel (DDP)
GPU 0 GPU 1 GPU 2
(完整模型副本) (完整模型副本) (完整模型副本)
↓ ↓ ↓
└─────── Ring AllReduce ───────┘
↓
所有 GPU 梯度同步后各自更新
QUESTION 面试高频:DDP 和 DP 的核心区别是什么?
维度 DP DDP 架构 Master-Worker 去中心化 通信方式 Gather 到 Master Ring AllReduce 负载均衡 不均衡(GPU 0 最重) 均衡 扩展性 单机 多机多卡 通信效率 低(瓶颈在 Master) 高(Ring 无瓶颈)
Ring AllReduce 原理
阶段 1: Reduce-Scatter(每个 GPU 获得梯度的一个分片)
GPU 0: [A0] [B0] [C0] → [A0+A1+A2] [ ] [ ]
GPU 1: [A1] [B1] [C1] → [ ] [B0+B1+B2] [ ]
GPU 2: [A2] [B2] [C2] → [ ] [ ] [C0+C1+C2]
阶段 2: All-Gather(每个 GPU 获得完整梯度)
所有 GPU 最终都有 [Sum_A] [Sum_B] [Sum_C]
通信量:( = 梯度大小, = GPU 数),远优于 DP 的 。
6. DeepSpeed ZeRO 详解
ZeRO (Zero Redundancy Optimizer) 三个阶段
| 阶段 | 分片内容 | 显存节省 | 通信开销 |
|---|---|---|---|
| ZeRO-1 | 优化器状态 | ~4x | 低 |
| ZeRO-2 | 优化器状态 + 梯度 | ~8x | 中 |
| ZeRO-3 | 优化器状态 + 梯度 + 参数 | ~Nx (N=GPU数) | 高 |
QUESTION 面试高频:ZeRO 三个阶段分别解决了什么问题? 标准 DDP 中每个 GPU 保存完整的三份冗余数据:参数、梯度、优化器状态。
- ZeRO-1:将优化器状态(占显存最大,Adam 约 2x 模型参数)分片到各 GPU
- ZeRO-2:在 ZeRO-1 基础上进一步分片梯度
- ZeRO-3:将参数也分片,前向/反向时通过 All-Gather 临时获取所需参数
工作原理
ZeRO-1(优化器状态分片)
GPU 0: 完整模型参数 + 完整梯度 + optimizer_states[shard_0]
GPU 1: 完整模型参数 + 完整梯度 + optimizer_states[shard_1]
...
各 GPU 独立更新自己负责的参数分片,然后 All-Gather 同步
ZeRO-3(完全分片)
GPU 0: params[shard_0], grads[shard_0], optimizer_states[shard_0]
GPU 1: params[shard_1], grads[shard_1], optimizer_states[shard_1]
...
前向/反向时通过 All-Gather 临时获取需要的参数,计算完后立即释放
ZeRO-Offload
将优化器状态和/或参数卸载到 CPU 内存或 NVMe 磁盘:
GPU 内存: 存放当前计算需要的参数分片
CPU 内存: 存放优化器状态 (momentum, variance)
NVMe 磁盘: 存放不活跃的参数分片(ZeRO-Infinity)
7. FSDP 详解
核心概念
FSDP 是 PyTorch 原生的完全分片数据并行方案,功能类似 ZeRO-3。
分片策略
from torch.distributed.fsdp import ShardingStrategy
# 完全分片 (类似 ZeRO-3)
sharding_strategy=ShardingStrategy.FULL_SHARD
# 仅分片梯度 (类似 ZeRO-2)
sharding_strategy=ShardingStrategy.SHARD_GRAD_OP
# 不分片 (标准 DDP)
sharding_strategy=ShardingStrategy.NO_SHARD
模型包装
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
model = FSDP(
model,
sharding_strategy=ShardingStrategy.FULL_SHARD,
mixed_precision=MixedPrecision(
param_dtype=torch.bfloat16,
reduce_dtype=torch.bfloat16,
buffer_dtype=torch.bfloat16
),
device_id=rank
)
8. AMP 混合精度训练
QUESTION 面试高频:什么是混合精度训练?为什么能加速训练并节省显存? 混合精度训练同时使用 FP16(低精度)和 FP32(高精度):
- FP16 计算前向和反向:速度快、显存省(带宽减半)
- FP32 维护主权重:保持训练稳定性(防止精度丢失)
- Loss Scaling:放大 loss 防止小梯度在 FP16 下下溢为零
AMP 工作流程
1. FP32 主权重 → 转换为 FP16 副权重
2. FP16 副权重 → 前向计算(FP16)
3. 计算输出 → Loss Scaling(放大 S 倍)
4. Scaled Loss → 反向传播(FP16 梯度)
5. 检查梯度是否溢出
├── 溢出 → 跳过此步,减小 S
└── 未溢出 → Unscale 梯度(除以 S)
6. FP32 主权重更新(用 unscaled 后的梯度)
BF16 vs FP16
| 维度 | FP16 | BF16 |
|---|---|---|
| 指数位 | 5 bits | 8 bits |
| 尾数位 | 10 bits | 7 bits |
| 动态范围 | 小 | 大(同 FP32) |
| Loss Scaling | 需要 | 通常不需要 |
| 硬件要求 | Volta+ | Ampere+ (A100) |
| 2025 趋势 | 逐渐淘汰 | 默认选择 |
9. 分布式训练框架对比
| 维度 | DeepSpeed | FSDP | Accelerate |
|---|---|---|---|
| 最大模型规模 | 最大(NVMe Offload) | 大 | 取决于后端 |
| 易用性 | 中等 | 中高 | 最易用 |
| PyTorch 原生 | 否(第三方库) | 是 | 是(封装层) |
| CPU 卸载 | CPU + NVMe | 仅 CPU | 取决于后端 |
| 混合精度 | 支持 | 支持 | 支持 |
| 张量并行 | 支持 | 有限 | 有限 |
| 调试难度 | 较难 | 较易 | 容易 |
选型指南
| 场景 | 推荐 |
|---|---|
| SFT/LoRA 微调(单机多卡) | Accelerate + FSDP |
| 全量微调 7B-13B(多卡) | FSDP |
| 全量微调 70B+(多节点) | DeepSpeed ZeRO-3 |
| 预训练大模型 | DeepSpeed 或 FSDP + Megatron |
| 快速实验、多种后端切换 | Accelerate |
| 消费级硬件、极限节省显存 | DeepSpeed ZeRO-3 + CPU Offload |
10. 典型部署方案
方案 1:7B 模型在线服务
硬件: 1x A100-40GB 或 1x RTX 4090 (24GB, INT4)
框架: vLLM
配置:
--gpu-memory-utilization 0.9
--max-model-len 4096
--enable-prefix-caching
性能: ~50-65 tokens/s (单请求), ~2000 tokens/s (64并发)
方案 2:70B 模型在线服务
硬件: 4x A100-80GB (TP=4) 或 2x A100-80GB (INT4, TP=2)
框架: vLLM / TensorRT-LLM
配置:
--tensor-parallel-size 4
--gpu-memory-utilization 0.9
--max-model-len 8192
--quantization awq # 如果用 INT4
性能: ~20-33 tokens/s (单请求), ~1000 tokens/s (64并发)
方案 3:本地/边缘部署
硬件: MacBook M2/M3 Pro (36GB+) 或 RTX 4090
框架: llama.cpp
模型: GGUF Q4_K_M 格式
配置:
-n 512 # 最大生成 Token 数
-c 4096 # 上下文长度
-ngl 99 # GPU 层数
性能: ~20-40 tokens/s (MacBook), ~50-80 tokens/s (RTX 4090)