深度神经网络训练突发OOM:loss张量保留引发内存泄漏问题咨询
问题解答
1. 是否可以手动删除loss张量后继续训练
可以,但是要注意操作细节:
- 正常情况下PyTorch调用
backward()且没有设置retain_graph=True时,对应的计算图会自动释放,不需要手动删除loss张量。但如果存在其他引用持有了loss张量(比如全局日志列表存储了CUDA侧的loss张量),手动删除可以释放对应显存。 - 删除操作需要覆盖所有中间loss变量,不止最终的
loss_gen_all:在scaler.update()执行完成后新增如下代码即可:
del loss_dur, loss_mel, loss_kl, loss_fm, loss_gen, losses_gen, loss_gen_all torch.cuda.empty_cache()
- 注意不要每步都调用
torch.cuda.empty_cache(),会显著降低训练速度,建议每100~500步调用一次即可。
2. 如何验证是否存在loss张量持续存储的问题
可以通过以下几个可落地的方法排查:
- 首先监控迭代级的显存变化:每轮迭代结束后打印
torch.cuda.memory_allocated()的返回值,如果数值随迭代次数稳定上涨(每次涨几十KB到几MB不等),就可以确定存在显存泄漏。 - 统计CUDA侧loss类张量的数量:在每轮迭代结束、清理完缓存后运行以下代码,观察计数是否随迭代持续增加:
import gc gc.collect() torch.cuda.empty_cache() loss_tensor_count = 0 for obj in gc.get_objects(): try: if torch.is_tensor(obj) and obj.is_cuda and 'loss' in str(type(obj)): loss_tensor_count += 1 except: pass print(f"当前CUDA上留存的loss类张量数量:{loss_tensor_count}")
- 排查全局loss存储逻辑:90%以上的这类问题都是因为记录loss时直接把CUDA侧的张量存入了全局列表,正确的写法是用
.item()转成Python原生标量再存储,比如loss_log.append(loss_gen_all.item())而非loss_log.append(loss_gen_all)。同理检查判别器侧的loss存储逻辑,避免出现同样的问题。
3. 是否可以回退一个epoch并停止存储loss张量
如果有上一个epoch的完整checkpoint(包含模型权重、优化器状态、scaler状态),可以直接加载该checkpoint重新训练,修复loss存储的逻辑后就不会再出现持续泄漏的问题。
如果没有保存上一个epoch的checkpoint,无法直接回退,可以先执行手动删除loss张量、清理显存的操作,尽可能复用当前训练状态继续训练,建议后续训练时调高checkpoint的保存频率,避免意外丢失训练进度。
另外补充你提到的.detach()放置问题:你当前的代码里计算loss用到的fmap_r是判别器对真实样本的输出,不需要参与生成器的梯度计算,建议在拿到fmap_r后立即执行fmap_r = [x.detach() for x in fmap_r],可以避免不必要的计算图留存,也能减少显存占用。
内容的提问来源于stack exchange,提问作者John G.
相关产品推荐
相关产品推荐

