torch.cuda.empty_cache()仅释放未被引用的显存,不释放模型、优化器或梯度占用的显存;常见误用是在backward后立即调用,此时基本无效。

为什么torch.cuda.empty_cache()经常没用
调用torch.cuda.empty_cache()只是释放缓存中未被张量引用的显存,它**不释放正在被模型、优化器或梯度占用的显存**。如果你刚跑完一次loss.backward(),而没清空计算图或没del掉中间变量,缓存里其实没多少可清的。常见错误是把它当成“重启GPU”,结果反复调用后显存占用纹丝不动。
实操建议:
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 先确认是否真有泄漏:在训练循环开头加
print(torch.cuda.memory_allocated()/1024**3)和print(torch.cuda.memory_reserved()/1024**3),看allocated是否持续上涨 - 只在确定无活跃张量时调用
empty_cache(),比如验证完模型后、加载新数据前 - 别在
backward()之后立刻调用——此时grad还在,empty_cache()基本不生效
batch_size不是唯一瓶颈:model.train()和model.eval()切换影响显存
训练模式下,BatchNorm和Dropout层会保留中间激活用于反向传播;而eval()模式不仅禁用这些操作,还会让部分层跳过缓存激活,显著降低峰值显存。很多人在验证时忘了切模式,导致val阶段显存比train还高。
实操建议:
- 每次进入验证/推理前,必须显式调用
model.eval();训练开始前再调model.train() - 验证循环内避免任何
loss.backward()或optimizer.step(),否则即使no_grad也可能因autograd上下文残留激活 - 用
with torch.no_grad():包裹整个验证块,不只是forward那一行
显存爆炸常来自torch.cat()或torch.stack()拼接操作
这两个函数会在GPU上新建一块连续显存来存放拼接结果,如果拼接几十个[1, 3, 224, 224]张量,显存占用不是线性增长,而是产生一个巨大的临时张量。更隐蔽的是,如果拼接发生在循环内且没del中间结果,显存会阶梯式上升。
实操建议:
- 优先用
torch.stack()代替torch.cat()做堆叠(如把list of tensors变[N, C, H, W]),它更省内存 - 避免在训练循环里动态拼接——改用预分配
torch.empty()张量,再用索引赋值 - 对返回多个输出的模型(如检测头),不要直接
torch.cat([x for x in outputs]),改用torch.cat(outputs, dim=0)并确保outputs是tuple/list of tensors,而非含None或标量的混合结构
GradScaler + autocast不是银弹:FP16下inf/nan会悄悄吃光显存
启用torch.cuda.amp.autocast()后,如果某次前向出现inf或nan,梯度缩放器可能无法及时检测并跳过backward(),导致后续optimizer.step()失败、计算图卡住、显存无法释放。现象是显存占用突增后不再下降,nvidia-smi显示GPU-Util为0但显存占满。
实操建议:
- 在
scaler.step(optimizer)后立刻加scaler.update(),缺一不可 - 每轮训练后检查
scaler.get_scale()是否异常下降(如loss或输入数据 - 调试时临时关闭
autocast和GradScaler,确认问题是否消失——若消失,大概率是数值不稳定引发的隐式泄漏
model.eval()漏切和autocast下inf/nan的静默堆积——这两点不排查,调batch_size只是把问题推迟几轮。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










