简介:围绕 DeepSeek 各版本硬件配置与优化展开的实践型文档,面向大模型研究者、算法工程师及需要做训练/推理硬件选型的技术开发者,帮助读者按 1B/3B、7B/13B、33B/70B 及 100B+ 等规模确定 CPU、GPU、显存、内存与存储方案。内容按精简版、基础版、大型版和分布式训练扩展编排,分别说明模型适用场景、训练与推理阶段的硬件门槛,并给出量化、FlashAttention-2、DeepSpeed-Inference、vLLM、ONNX Runtime/TensorRT 等优化思路;超大规模部分还涉及 H100 节点、NVLink、InfiniBand 与分布式文件系统等集群配置,末尾附关键注意事项和总结建议。包内共 1 个 docx 文件,约 34KB,篇幅紧凑、目录清晰,便于按参数规模快速检索对应配置。已有 2408 人学习,适合在项目立项、设备采购和部署调优前做参考对照。
1. 先定版本再配硬件:DeepSeek 硬件要求里最容易被搞反的那一步
上周有个做客服系统的朋友发来一张配置单,i9 加 128G 内存加一张 4090,问我能不能直接上 70B。我让他先算笔账:70B 参数按 FP16 落盘就是 140GB 权重,单张 24GB 卡连权重都装不下,更别提 KV Cache。这不是配置高低的问题,是数量级错位。DeepSeek 这类模型的硬件要求,本质上由参数量、精度、并行策略三个变量决定,跟机器贵不贵关系不大。这份资料按 1B/3B、7B/13B、33B/70B、100B+ 四档,把训练和推理分开列了 GPU 型号、显存下限、内存和存储规格,也给了量化、切分、蒸馏几条优化路径。它适合正在做选型的技术负责人,也适合手里只有一张消费级卡、想把吞吐榨到极限的开发者。
2. 参数量到显存的换算链路:7B/13B 基础版本的训练与推理配置
2.1 1.2 这个系数是怎么来的,以及它管不了训练
原资料给了一个公式:FP16 显存 ≈ 参数量 × 2 字节 × 1.2,举例 7B 模型算出 16.8GB,结论是要 24GB 卡。这个 1.2 的系数常被误读成"训练开销",实际上它覆盖的是推理阶段的 KV Cache、CUDA Context 和框架本身的碎片开销,一般取 1.1 到 1.3 之间,序列越长越往高取。拿它去估算训练显存,7B 算出来 16.8GB 就敢上单卡,跑起来第一步 backward 就 OOM,这是我在项目里见过最频繁的翻车点。
真正训练时的显存账要按混合精度 Adam 逐项拆。每参数占用的字节数如下表,这个表比任何口诀都好用,记住了基本不会算错量级。
| 占用项 | 精度 | 每参数字节 |
|---|---|---|
| 模型权重 | FP16/BF16 | 2 |
| 梯度 | FP16/BF16 | 2 |
| 主权重副本 | FP32 | 4 |
| Adam 一阶动量 m | FP32 | 4 |
| Adam 二阶动量 v | FP32 | 4 |
| 合计 | — | 16 |
按 16 字节/参数算,7B 全参数微调的静态开销约 112GB,13B 约 208GB。这还没算激活值,激活值跟 batch size、序列长度、是否开启梯度检查点强相关,长序列场景下能再翻一倍。所以原资料说"单卡 A100 40GB 训练 7B 需开启梯度检查点",这句话是对的,但前提是把优化器状态切片出去,也就是 ZeRO 系列要上。
我用一段脚本把这两套账都算出来,选型阶段直接跑一下,比翻手册快:
# mem_estimate.py # 用途:分别估算推理与训练的显存下限,避免拿推理公式去套训练 def infer_vram(params_b, bytes_per_param=2, overhead=1.2): """推理显存:权重 + 框架与 KV Cache 的粗放系数""" weight = params_b * 1e9 * bytes_per_param / (1024 ** 3) return weight * overhead def train_vram(params_b, seq_len=2048, batch=1, hidden=4096, layers=32, checkpointing=True): """训练显存:16 字节/参数 + 激活值估算""" static = params_b * 1e9 * 16 / (1024 ** 3) # 激活值粗略项:层数 × batch × 序列长 × 隐藏维 × 2 字节 × 系数 act = layers * batch * seq_len * hidden * 2 / (1024 ** 3) if checkpointing: act = act * 0.3 # 梯度检查点把激活压到约三成 return static + act if __name__ == "__main__": for p in (7, 13, 33, 70): print(f"{p}B 推理FP16≈{infer_vram(p):.1f}GB " f"训练(含检查点)≈{train_vram(p):.1f}GB")脚本里overhead默认 1.2 对应原资料的系数,序列长度超过 8K 时建议手动调到 1.3;checkpointing关掉的话激活值那一项会放大三倍多,这就是为什么很多人关了梯度检查点想提速,结果直接从能跑变成不能跑。跑出来的数字如果训练显存超过单卡容量,别硬扛,直接进 2.2 的 ZeRO 配置。
2.2 单卡与四卡训练的差异化配置
原资料对 7B/13B 训练给的方案是:单卡 A100 40GB 开梯度检查点,多卡推荐 4×A100 80GB 配 ZeRO-3。这里的分界线其实在 13B:7B 用 ZeRO-2 加梯度检查点,单张 40GB 能扛住;13B 必须上 ZeRO-3,因为优化器状态和参数切片的需求已经超过单卡余量。内存方面 64GB DDR4 是底线,卡越多、CPU offload 开得越多,内存越要往上加,我一般按"每张卡配 32GB 内存"这个比例留余量。
下面这份 ZeRO-3 配置是我在 13B 场景下用了很久的版本,改几个数字就能套到 7B 上:
{ "train_batch_size": 16, "gradient_accumulation_steps": 4, "fp16": { "enabled": true, "loss_scale": 0, "initial_scale_power": 12 }, "zero_optimization": { "stage": 3, "offload_optimizer": { "device": "cpu", "pin_memory": true }, "overlap_comm": true, "contiguous_gradients": true, "stage3_gather_16bit_weights_on_model_save": true }, "gradient_checkpointing": { "enabled": true }, "steps_per_print": 20 }stage是切片粒度,3 表示参数、梯度、优化器状态全切;offload_optimizer把优化器状态扔到 CPU 内存,能省下一大块显存但要吃内存带宽,内存低于 128GB 时别开;overlap_comm让通信和计算重叠,多卡之间只要有 NVLink 或高速 PCIe 就该开;stage3_gather_16bit_weights_on_model_save必须为 true,否则存出来的 checkpoint 是分片的,后续推理加载会报 key 不匹配。
启动命令对应:
# 4 卡训练 13B,注意 --num_gpus 与实际可见卡数一致 deepspeed --num_gpus=4 train.py \ --model_name_or_path ./deepseek-13b-base \ --deepspeed ds_zero3.json \ --per_device_train_batch_size 2 \ --gradient_accumulation_steps 4 \ --max_seq_length 2048 \ --learning_rate 2e-5 \ --bf16 False \ --output_dir ./ckpt-13bper_device_train_batch_size乘卡数再乘梯度累积步数,要等于配置里的train_batch_size,对不上就会在初始化阶段直接抛错;max_seq_length每翻一倍,激活值大约翻一倍,显存紧张时优先降它而不是降 batch。启动后第一件事是看日志里的"ZeRO stage 3 initialized"和每步耗时,如果 step time 抖动超过 20%,多半是 CPU offload 的内存带宽打满了。
2.3 推理侧量化选型:FP16、INT8、INT4 的边界
推理的要求比训练宽松得多。原资料的结论是 7B 在 FP16 下需约 10GB 显存,13B 用 INT4 量化后约 16GB,对应的卡就是 3090/4090 这类 24GB 消费级,或者 T4 走 INT8。这个判断我认同,但要补一句:10GB 是权重加载完的瞬时值,实际跑起来序列一长,KV Cache 会继续吃显存,24GB 卡跑 7B FP16 在 8K 上下文下其实已经接近上限。
量化方案上,GPTQ 和 AWQ 是两条主流路线。GPTQ 生态更成熟、工具链顺手,AWQ 对激活值里的显著通道做保护,低比特下掉点通常更小。我的习惯是 13B 及以上先试 AWQ INT4,7B 这种小模型 INT8 就够,量化过度反而得不偿失。加载代码大致长这样:
# infer_quant.py 用 AWQ INT4 加载 13B,单卡 24GB 可跑 from awq import AutoAWQForCausalLM from transformers import AutoTokenizer import torch model_path = "./deepseek-13b-awq-int4" model = AutoAWQForCausalLM.from_quantized( model_path, fuse_layers=True, # 融合 QKV 与 MLP,省显存也提速 trust_remote_code=True, safetensors=True, device_map="cuda:0", ) tok = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) inputs = tok("显存不够时应该优先降哪个参数?", return_tensors="pt").to("cuda:0") out = model.generate(**inputs, max_new_tokens=256, do_sample=False) print(tok.decode(out[0], skip_special_tokens=True))fuse_layers=True会把注意力和 MLP 里的相邻算子合并,单次前向能少几次 kernel launch,显存能省一两百 MB,对边缘卡很值;device_map在多卡场景下可以写成"auto"让 accelerate 自己切,但量化模型跨卡切分容易出精度问题,能单卡就单卡。
至于 FlashAttention-2,原资料把它列在优化建议里,实际收益主要体现在长序列:2K 以内提升不明显,8K 以上延迟能降两三成。装的时候注意版本要和 CUDA、PyTorch 对齐,版本错配的报错通常是"undefined symbol",这种玄学问题八成是编译环境不一致导致的,血泪经验是直接用官方预编译 wheel,别自己源码编译。
3. 33B/70B 大版本落地:并行切分与推理框架的实战参数
3.1 张量并行和流水线并行的分界线在哪
原资料对 33B/70B 训练给的方案是"必须多卡并行,推荐 8×A100 80GB,结合张量并行加流水线并行",推理侧是 33B 用 2×A100 40GB、70B 用 4×A100 80GB。这套配置背后的逻辑是:模型放不进单卡时,只有两条路——把单层切开(张量并行,TP),或者把层分组放到不同卡上(流水线并行,PP)。
分界线我一般这么定:卡间有 NVLink 且卡数在 8 以内,优先 TP,因为 TP 每层都要做 all-reduce,通信量大,NVLink 的高带宽能扛住;跨节点必须走 PP,因为 PP 只在层边界传激活,通信量小,普通以太网也能凑合。TP 度超过 8 之后通信开销增长很快,收益急剧下降,这也是为什么大规模集群最终都收敛到 3D 并行。
内存和存储这块原资料写得比较硬:33B/70B 训练要 256GB DDR4 ECC 起,存储 1TB NVMe。我的经验是内存按"参数量的 4 倍 GB"估,70B 就是 280GB 往上,ECC 不是可选项,长时间训练的 bit flip 是真的会让 loss 曲线莫名抖动。存储方面 1TB 只够中小数据集,涉及多轮清洗的语料建议直接上 2TB,NVMe 的随机读性能对 dataloader 影响很大,机械盘会直接把 GPU 饿死。
3.2 vLLM 与 DeepSpeed-Inference 的启动参数
推理框架选型上,原资料提到 DeepSpeed-Inference 做跨卡加载、vLLM 提吞吐。这两个的定位不太一样:DeepSpeed-Inference 强在能把模型切到多卡、支持比较灵活的分片策略;vLLM 强在 PagedAttention 带来的批处理吞吐,服务化场景基本是首选。我现在的默认做法是——在线服务用 vLLM,离线大批量跑用 DeepSpeed-Inference。
vLLM 起 70B INT4 双卡的命令:
# 2×24GB 消费卡跑 70B AWQ INT4,Tensor 并行度设为 2 python -m vllm.entrypoints.openai.api_server \ --model ./deepseek-70b-awq-int4 \ --tensor-parallel-size 2 \ --dtype half \ --max-model-len 4096 \ --gpu-memory-utilization 0.92 \ --swap-space 16 \ --port 8000tensor-parallel-size必须整除注意力头数,70B 一般是 64 头,取 2、4、8 都行,取 3 会直接报错;gpu-memory-utilization是预分配的显存比例,0.92 是我在消费卡上试出来的平衡点,调到 0.95 容易在长请求时被系统进程挤爆;max-model-len决定 KV Cache 的预留量,设 4096 而不是模型上限的 32K,能省出一大块显存给并发;swap-space是 CPU 侧的交换空间,请求排队严重时可以给到 16GB 以上。
DeepSpeed-Inference 那边则是靠一个 JSON 描述切分策略:
{ "train_micro_batch_size_per_gpu": 1, "fp16": { "enabled": true }, "injection_policy": { "DeepSeekBlock": "{'self_attn.o_proj': 'tensor_parallel'}" }, "tensor_parallel": { "tp_size": 4 }, "enable_cuda_graph": true }injection_policy指定哪些层做张量切分,一般是把注意力的输出投影和 MLP 的上下投影切开,这两处的矩阵最大、切分收益最高;enable_cuda_graph用 CUDA Graph 把整个前向固化下来,减少 kernel launch 开销,但要求输入 shape 固定,动态 batch 场景别开。
3.3 2×4090 跑 70B INT4 到底能不能用
原资料给了一个很具体的结论:70B 模型 INT4 量化后可以在 2×RTX 4090 上运行。这句话技术上成立,但"能运行"和"能提供服务"是两件事,中间隔着吞吐这道坎。我实测过的量级是:双卡 4090 跑 70B AWQ INT4,单请求生成速度大约在每秒十几个 token,并发上到 4 之后延迟明显上升,P99 会飙到秒级。
瓶颈不在算力,在显存带宽。4090 的显存带宽约 1TB/s,70B INT4 权重约 35GB,每生成一个 token 要把权重扫一遍,理论上限就是每秒二十多个 token,跟实测对得上。A100 80GB 的带宽是 2TB/s 级别,同样的模型能翻倍。所以这个方案适合的场景是:内部工具、低频调用、对延迟不敏感的批处理,不适合直接对外提供实时对话服务。
如果非要提吞吐,能做的是减小单次扫描的数据量——比如把 INT4 的 group size 调小(精度换速度的权衡)、开连续批处理把多请求拼成一次前向。连续批处理在 vLLM 里是默认开的,但序列长度差异大时会有 padding 浪费,可以在请求侧做长度分桶。
4. 1B/3B 精简版与端侧部署:从蒸馏到 TensorRT 的完整链路
4.1 蒸馏和剪枝谁先谁后
原资料对 1B/3B 的定位是嵌入式设备、边缘计算、实时响应场景,优化建议是 ONNX Runtime 或 TensorRT 部署、知识蒸馏压缩尺寸。这里有个顺序问题经常被搞反:先剪枝还是先蒸馏。
我的做法是先蒸馏、后剪枝、最后量化。理由是蒸馏需要一个大模型当老师,在原始结构上做蒸馏效果最好;剪枝会破坏结构,剪完再蒸馏,老师的输出分布和学生的结构已经对不齐了,效果打折。剪枝之后再走量化,是因为剪枝留下的稀疏结构在低比特量化下容易放大误差,量化放最后能一次性把前两步的冗余都吃掉。
蒸馏阶段的关键参数是温度 T 和软标签权重 α。T 一般取 2 到 4,太小软标签接近 one-hot 失去意义,太大分布太平学生学不到重点;α 取 0.5 到 0.7 之间,偏向软标签多一点。这套参数没有普适值,得看任务,我一般先跑一组 T=3、α=0.6 的基线,再单变量微调。
4.2 ONNX 导出与 TensorRT 引擎构建
端侧部署的路径基本是 PyTorch → ONNX → TensorRT。第一步导出:
# export_onnx.py 导出 3B 模型为 ONNX,供 TensorRT 构建引擎 import torch from transformers import AutoModelForCausalLM, AutoTokenizer model_id = "./deepseek-3b" model = AutoModelForCausalLM.from_pretrained( model_id, torch_dtype=torch.float16, trust_remote_code=True ).eval().cuda() tok = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True) dummy = tok("hello", return_tensors="pt").to("cuda") torch.onnx.export( model, (dummy["input_ids"], dummy["attention_mask"]), "deepseek-3b.onnx", input_names=["input_ids", "attention_mask"], output_names=["logits"], dynamic_axes={ "input_ids": {0: "batch", 1: "seq"}, "attention_mask": {0: "batch", 1: "seq"}, "logits": {0: "batch", 1: "seq"}, }, opset_version=17, do_constant_folding=True, )opset_version建议 17 及以上,低版本对 attention 相关算子的支持不全,导出后常见的问题是 GELU 被拆成一堆小算子,推理时慢得离谱;dynamic_axes把 batch 和 seq 都标成动态,否则引擎只能跑固定 shape,实际业务里基本没法用;do_constant_folding打开能提前折叠常量,减小图体积。
第二步构建 TensorRT 引擎:
# 3B 模型在 Jetson 类设备上构建 FP16 引擎,注意 workspace 别给太大 trtexec --onnx=deepseek-3b.onnx \ --saveEngine=deepseek-3b-fp16.engine \ --fp16 \ --minShapes=input_ids:1x1,attention_mask:1x1 \ --optShapes=input_ids:1x128,attention_mask:1x128 \ --maxShapes=input_ids:4x512,attention_mask:4x512 \ --workspace=2048optShapes是最关键的参数,TensorRT 会针对这组 shape 做 kernel 选择,设成业务里最常见的请求长度,性能最好;maxShapes决定显存/内存的预留上限,设太大引擎体积暴涨,设太小超长请求直接失败;workspace单位是 MB,边缘设备上给 2048 就够,给太多反而挤占系统内存。
4.3 边缘设备的功耗和散热预算
原资料提到精简版可以用集成显卡或者 Jetson Nano 这类设备,需要 INT8 量化,CPU 方案是 4 核加 16GB 内存。这里补一个容易被忽略的维度:功耗。
多卡训练那边原资料说单张 A100 功耗 300W 以上,要注意电源和散热。边缘侧同理,只是量级不同。一块 Jetson 类模组的整板功耗在 10 到 30W 之间,看着不高,但塞进密闭外壳里连续跑,结温很快就上去了,然后就是降频,然后就是延迟抖动。我在一个项目里遇到过推理延迟周期性从 80ms 跳到 300ms 的情况,查了半天是散热片的导热硅脂没涂匀,这种问题看日志完全看不出来,只能上温度监控。
预算上的经验值:整机功耗留 30% 余量;被动散热方案的持续负载不要超过额定功耗的六成;CPU 推理方案里,内存带宽往往比核心数更影响吞吐,DDR5 比 DDR4 在 3B 模型上能快两成左右。
5. 避坑清单:硬件配置里最容易翻车的五件事
5.1 用推理公式算训练显存,第一次 backward 就 OOM
现象:按 7B×2×1.2 算出 16.8GB,买了 24GB 卡,模型能加载、能前向,一执行 backward 就 CUDA out of memory。
原因:这个公式只覆盖推理时的权重和框架开销,训练还要额外承担梯度、FP32 主权重、两份额外的优化器动量,合计 16 字节/参数,7B 就是 112GB 的静态开销,差了一个数量级。
解决:训练前先用 2.1 的脚本把两套账分开算。显存不够就按顺序上手段:先开梯度检查点(省激活值,代价是重算、慢约三成),再上 ZeRO-2(切优化器状态),还不行上 ZeRO-3(连参数一起切),最后才是 CPU offload。别一上来就 offload,内存带宽会成为新瓶颈。
5.2 张量并行度随手设成 3 或 5,启动直接报维度错
现象:vLLM 或 DeepSpeed 启动时报 hidden size 不能被整除,或者 attention head 数量不匹配。
原因:张量并行是把注意力的头、MLP 的中间维按份切开,并行度必须整除头数和中间维。常见的模型头数是 32、40、64,取 3、5、6 这种都会失败。
解决:并行度只能在 1、2、4、8 里选,且要满足整除关系。跨节点时并行度不要超过单节点的卡数,否则 all-reduce 会走网络,延迟直接翻几倍。拿不准就先看模型配置里的num_attention_heads和num_key_value_heads,取两者的公约数。
5.3 量化后精度掉得莫名其妙,问题出在校准集
现象:INT4 量化后模型在通用测试集上看着还行,一上业务数据就开始胡言乱语,或者重复输出。
原因:GPTQ/AWQ 都需要校准数据来估计激活分布,很多人直接拿通用语料当校准集,跟业务分布完全对不上,量化误差在业务输入上被放大。另一个常见原因是 group size 设得太大,粗粒度分组让离群值影响了整组。
解决:校准集从真实业务数据里抽,几百条就够,但要覆盖长尾输入。group size 从 128 起调,掉点明显就降到 64,代价是模型体积略增。量化完必须跑一遍业务侧的评估集,别只看通用 benchmark。
5.4 多卡训练的电源和散热没算够,跑到半夜掉卡
现象:训练跑几小时后 loss 突然异常,日志里出现 GPU 掉线、Xid 错误,重启又能跑。
原因:单张高功耗卡的瞬时功耗会超过标称值,多卡同时冲峰值时电源余量不够就会触发保护。散热不足导致降频是软故障,不报错但速度掉一半,很容易被当成"训练变慢了"而忽略。
解决:电源按"单卡峰值功耗 × 卡数 × 1.3"配,别按 TDP 算。机箱风道要保证进风温度低于 35 度,多卡机器建议用涡轮卡而不是轴流卡。监控上nvidia-smi --query-gpu=power.draw,temperature.gpu,clocks_throttle_reasons.active --format=csv -l 5这条命令能同时看到功耗、温度和降频原因,挂到后台跑,出问题回头翻日志就有据可查。
5.5 存储按模型大小买,数据集一上来就爆盘
现象:算好 70B 模型 140GB,买了 1TB NVMe,结果训练到一半磁盘满,checkpoint 存不下。
原因:只算了模型权重,没算 checkpoint 轮转、优化器状态、数据集缓存和日志。ZeRO-3 保存时要先 gather 出完整权重,瞬时占用可能是模型的两倍;每个 checkpoint 还要带优化器状态,体积通常是权重的三到四倍。
解决:存储按"模型体积 × 4 × 保留的 checkpoint 数 + 数据集体积 × 1.5"估。用save_total_limit限制保留数量,开启save_only_model只存权重不存优化器状态(代价是没法断点续训)。数据集预处理后的缓存目录记得单独挂盘。
6. 100B+ 集群的配置核查:一份能提前发现问题的压测清单
超过百亿参数往上,配置单上写的"64×H100 加 NVLink 加 InfiniBand 200Gbps 加分布式文件系统",光看是看不出问题的,必须在上量之前做一轮通信和 IO 压测。原资料里 3D 并行那句"数据并行加流水线并行加张量并行"只有一行字,实际落地时最容易出问题的是并行维度的组合方式和进程到设备的映射。
我的习惯是先跑三个小测试,任何一个不过就别急着起训练。第一个是卡间带宽:用 NCCL 自带的 all-reduce 测试,8 卡 NVLink 场景下 bus bandwidth 应该接近硬件标称,明显偏低说明拓扑识别错了,可能是 PCIe 交换机把 NVLink 组切开了。第二个是节点间带宽,跨节点跑 all-reduce,200Gbps 的 IB 实测能到 20GB/s 上下,掉到个位数就检查网卡绑定和交换机端口。第三个是存储吞吐,多进程并发读同一批数据,看聚合带宽能不能喂饱 dataloader,Lustre 这类并行文件系统在小文件场景下性能会断崖式下跌,语料最好提前打包成 webdataset 之类的分片格式。
3D 并行的维度分配有个粗略的比例:TP 不超过单节点卡数,PP 尽量让每个 stage 的层数均衡,DP 填剩下的。比如 64 卡、单节点 8 卡,可以取 TP=8、PP=8、DP=1,也可以 TP=8、PP=4、DP=2,后者要求模型层数能被 4 整除且每段负载接近。PP 有个绕不开的问题是流水线气泡,stage 越多气泡越大,常见的缓解是 interleaved schedule,把每个 stage 拆成多个小块交替执行,能显著降低空转率。
验证阶段还有个技巧:先用小模型把整条流水线跑通,再换大模型。并行配置、数据加载、checkpoint 保存这些逻辑跟模型大小无关,用小模型验证能省下大量排队等卡的时间。等小模型跑通、loss 曲线正常下降、checkpoint 能存能恢复,再切到大模型上,出问题的概率会低很多。
从那以后我每次上多卡集群,不管配置单写得多漂亮,都强制先跑一遍这三项压测,宁可多花半天,也不想在训练跑到第三天的时候发现是网卡没绑对。希望帮到你。
本文还有配套的精品资源,点击获取