优先用torch.no_grad()减显存,因其禁用整个上下文的梯度追踪、不建计算图、不存grad_fn;detach()仅切断单个Tensor梯度流,原计算图和中间变量仍占显存。

PyTorch中detach()和torch.no_grad()到底该用哪个?
看报错或显存暴涨时才想起来加no_grad,结果发现模型推理还是卡顿、显存没降——大概率是混用了detach()和torch.no_grad(),且没理解它们作用的层级。前者切断梯度流但保留计算图节点,后者直接禁用整个上下文中的梯度追踪。训练中想省显存,优先关上下文;推理或中间特征复用时,才考虑detach()。
为什么torch.no_grad()比detach()更有效减显存?
torch.no_grad()让所有新创建的Tensor默认requires_grad=False,且不构建计算图,连grad_fn都不存——显存里没图节点、没反向缓存,自然轻。而detach()只是把一个已有Tensor“摘”出来,它本身不参与反向,但原图还在,中间变量(比如hidden、attn_weights)若还在作用域里,GC又没及时回收,显存照样涨。
-
torch.no_grad():适合整段推理、验证、特征提取逻辑 -
detach():适合从训练循环里临时取个中间值做监控(如记录loss曲线)、或传给非PyTorch模块(如NumPy绘图) - 别在
torch.no_grad()里再调detach()——冗余,还可能干扰GC
detach()后仍OOM?检查这三处常见泄漏点
显存没下来,不是detach()失效,而是你“以为 detach 了”,其实没真正断开引用链。尤其在循环中反复赋值、日志打印、或放进列表缓存时极易踩坑。
- 写了
x_detached = x.detach(),但老变量x还在循环里被持续引用 → 显存不释放 - 把
detach()后的张量存进list.append(x.detach()),却在整个epoch结束后才清空列表 → 所有历史副本全留在内存 - 用
print(x.detach().cpu().numpy())调试,.cpu()会触发拷贝,.numpy()又可能隐式共享内存,大张量反复调用等于多占一份
验证显存是否真降:别只看nvidia-smi
nvidia-smi显示的是GPU总分配量,PyTorch的缓存机制会让它“只增不减”——哪怕你删了变量,缓存池也不还给系统。真正要看的是PyTorch内部统计:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
import torch print(torch.cuda.memory_allocated() / 1024**3, "GB") # 当前实际占用 print(torch.cuda.memory_reserved() / 1024**3, "GB") # 缓存池大小
加了torch.no_grad()后,memory_allocated应明显下降;如果只降一点,大概率是某处漏了del或没退出上下文;如果memory_reserved持续涨,说明缓存池被反复扩容,需检查是否在循环中新建大量小张量(比如逐token生成时没合并)。
动态图的显存压力从来不在“要不要算梯度”,而在“谁还拿着那些中间结果”。盯住memory_allocated,比猜函数名有用得多。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










