PyTorch视频帧处理中内存不释放的根源是CUDA缓存机制和计算图残留,必须在每帧处理后立即调用torch.cuda.empty_cache(),配合model.eval()和torch.no_grad()禁用梯度,del仅解除引用而empty_cache才真正释放显存。
PyTorch视频帧处理中内存不释放的典型表现
加载一帧就
一次,循环几十帧后 OOM;用
读帧 +
转换后没显式释放,
显示 GPU 内存持续上涨;即使函数退出、变量被重赋值,
仍不回落。
根本原因不是“没删变量”,而是 PyTorch 的 CUDA 缓存机制默认复用显存块,且 autograd 计算图残留(哪怕没
)也可能持引用。单纯靠 Python 垃圾回收器(
)无法触发 CUDA 显存释放。
必须显式调用
这不是可选项,是视频批量帧处理的强制操作点:
不会清空模型参数或当前活跃 tensor,只释放未被任何 tensor 引用的缓存显存
它应在每帧处理完、且确定后续不再访问该帧对应 tensor 后立即调用(尤其在
块内做完推理后)
不要等到循环结束才调用——缓存会在多帧间不断累积
注意:
本身不释放显存,只是移除 Python 引用;
才真正归还给 CUDA 驱动。
立即学习
“
Python免费学习笔记(深入)
”;
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
下载
避免隐式计算图构建导致的 Tensor 持有
只要 tensor 的
(或由它派生),PyTorch 就会构建计算图并持有输入 tensor 引用,即使你没调
。视频处理中绝大多数场景不需要梯度:
用
+
双保险,禁止 grad 开关和计算图追踪
输入 tensor 显式设为
(虽然
下默认如此,但加一层更稳)
切勿在推理循环里写类似
—— 这是训练逻辑,混入推理会快速拖垮内存
gc.collect() 在 CUDA 场景下作用有限但仍有用处
对 CPU 内存中的 tensor 对象(如 numpy array、
python
list of tensors)回收有效,但它
完全不影响 CUDA 显存分配状态
:
如果你用
积累帧数据,忘了清空 list,
可帮回收 list 本身和其中的 CPU-side tensor 元信息
但如果 tensor 已转移到 GPU,
不会让
下降
实操建议:仅在确认有大量 CPU 端中间对象(如预处理后的 PIL.Image、临时 dict)堆积时,配合
使用;别指望它解决显存泄漏
真正容易被忽略的是:PyTorch 的 CUDA 缓存行为在不同版本中略有差异,1.12+ 默认启用
中的“allocated vs reserved”分离机制,所以看
低不代表显存真空闲——得同时监控
,并在关键节点调
才算闭环。
torch.tensor()cv2.VideoCapturetorch.from_numpy()nvidia-smitorch.cuda.memory_allocated().backward()gc.collect()torch.cuda.empty_cache()torch.cuda.empty_cache()with torch.no_grad():
for i in range(frame_count):
ret, frame = cap.read()
if not ret: break
tensor = torch.from_numpy(frame).permute(2, 0, 1).float().unsqueeze(0).to('cuda')
out = model(tensor)
del tensor, out # 先解引用
torch.cuda.empty_cache() # 立即释放缓存
delempty_cache()requires_grad=True.backward()model.eval()torch.no_grad()requires_grad=Falseno_gradloss = criterion(pred, target); loss.backward()gc.collect()list.append(tensor)gc.collect()gc.collect()torch.cuda.memory_allocated()deltorch.cuda.memory_stats()memory_allocated()memory_reserved()empty_cache()