先把结论说清楚:如果你的报错是前向推理阶段抛出的 CUDA out of memory,通常先降分辨率(或帧数)比先降批大小更快看到效果;如果日志显示峰值出现在一次并行处理多条样本时,批大小才是主因。判断依据不是哪个参数更好调,而是报错落在加载、前向还是写盘哪一段——先定位阶段,再用单项变量法一次只改一个参数重跑,才能知道是谁撑不住了。
建议顺序:先记录完整报错和发生阶段,再单项重跑。推理阶段显存吃紧,按分辨率→帧数→批大小的顺序试;加载阶段报错优先看精度与是否一次性搬入显存;写盘阶段报错多半是待编码帧在内存或显存里堆积。每次只改一个参数,同时记下占用读数与输出变化。分辨率下调通常最直接,但会损失画面细节;帧数下调影响时序连续性;批大小降到 1 只影响吞吐,一般不影响单条输出质量。
把完整报错原文和发生阶段记下来
不同阶段撑不住,改的参数完全不一样。先把这几样抄到一处:完整报错原文、堆栈里出现的关键词、对应的代码位置(文件与函数名)、当时的输入参数。PyTorch 常见的报错形态是 RuntimeError: CUDA out of memory. Tried to allocate ...,后面还会跟一段 GPU 0 has a total capacity of ... of which ... is free,以你实际输出为准,不要凭印象记。
| 堆栈关键词 | 所在阶段 | 常见代码位置 | 当时要记的输入参数 |
|---|---|---|---|
torch.load / load_state_dict / _load_from_state_dict | 加载权重 | 模型初始化、权重加载 | 精度 fp16/fp32、是否 device_map 全量入卡 |
forward / scaled_dot_product_attention / conv3d | 前向推理 | 扩散/注意力/VAE 解码 | 分辨率、帧数、批大小、精度 |
VideoWriter / encode / write / 编码库栈帧 | 写盘与编码 | 结果保存、视频封装 | 帧数、分辨率、是否把整段帧留在显存 |
拿到阶段之后再动手。判断方式很直接:报错堆栈里最后一个属于你项目代码的帧,基本就指向出问题的模块。
只降分辨率重跑一次
保持帧数、批大小、精度不动,只把分辨率降一档。例如短边从 720 调到 512,宽按原比例同步缩放,避免拉伸。改动前后各记一次峰值占用,推荐在代码里读:
torch.cuda.reset_peak_memory_stats()
# ... 只跑一次推理 ...
print(torch.cuda.max_memory_allocated() / 1024**3, "GiB")
外部观测可以用 nvidia-smi `--query-gpu`=memory.used `--format`=csv -l 1 定时刷新,看的是整卡占用,包含别的进程,所以要和进程内读数对照着看。
输出画面变化上,分辨率下调最先损失的是小字、细边缘纹理和远处小物体,快速运动画面的拖影也会更明显。如果这批任务对细节不敏感(例如只做动作预览),这一档降得比较划算。要留意分辨率不能低于模型或 VAE 能接受的下限,具体数值需要结合你用的配置确认。
只降帧数重跑一次
把分辨率、批大小、精度恢复原样,只动帧数。常见档位可以按 32→24→16 逐级往下试,每次只降一档。帧数和分辨率在注意力计算里都会转成序列长度,对占用的影响方向类似,但帧数影响的是时序。要同时记:帧数档位、峰值占用读数、输出片段是否还能看清动作起止。
- 帧数降太多,动作会出现跳变、首尾不连贯,语义上像是被截断。
- 如果输出时长是固定要求,帧数下降会连带改变 fps 或让时长变短,这一点需要在配置里对齐,别只看显存。
- 如果只是把帧数从高到低降到一半左右仍报错,说明瓶颈可能不在序列长度,回到批大小去查。
只降批大小或改成逐条推理
把分辨率和帧数恢复原样,只改批大小:例如 4→2→1。改成逐条推理就是把 batch 拆成循环,写法大致如下,替换模型调用为你自己的接口即可:
for item in items:
with torch.inference_mode():
out = model.generate(item)
save(out)
del out
torch.cuda.empty_cache()
逐条推理的总耗时通常会上升,因为同样的总条数要跑更多次前向,单条延迟反而更稳定、更容易预测。这里要分清:empty_cache() 只是把不用的缓存还给驱动,属于清理动作,不要当成性能优化。如果改成批大小 1 后显存仍然不够,说明单条样本本身就超了,问题回到分辨率和帧数上。
排出一条降配顺序
下次再报显存不足,按这个顺序试,不要同时改两个参数:
- 先看报错阶段。加载阶段报错,先确认精度和是否把全部权重塞进了同一张卡;写盘阶段报错,先看是不是整段帧都留在显存里等编码。
- 前向阶段报错,先降分辨率一档。下限是模型与 VAE 能接受的最小尺寸,具体值结合配置确认,不要低于它。
- 仍然报错,只降帧数一档。下限是画面动作还能读出来的最少帧数,再低输出语义就断了。
- 仍然报错,把批大小减半,直到改成逐条推理。下限就是 1,没有更小。
- 批大小为 1 仍报错,再去查:是否有别的进程占卡(
nvidia-smi查看)、推理是否忘了inference_mode或no_grad、精度是否还是 fp32、VAE 解码和主模型是否峰值叠加。
每一步都只改一个参数,并记录改动值、峰值占用读数和输出变化三项。连续两步都没让占用明显下降,就说明这个方向不是主因,换下一个参数,而不是继续在同一项上加码。