PyTorch添加自定义损失后autograd失效、训练损失无变化排查
PyTorch混合损失训练时损失值固定问题排查
核心错误点
你的代码一共两处破坏autograd梯度链路的问题,直接导致模型参数无法更新,损失全程不变:
- 自定义损失完全脱离计算图
my_custom_loss中调用.detach().cpu().numpy()将PyTorch张量转为NumPy数组,后续所有正弦、求和计算都是NumPy操作,完全脱离PyTorch的自动微分追踪范围,返回的square_sum是和模型参数毫无关联的普通浮点数,反向传播时这部分不会产生任何梯度。从你的打印日志也能验证:正常的loss1带grad_fn=<MseLossBackward0>标记,而loss2直接输出无梯度标记的浮点数34.6672。 - 多余的Variable操作彻底切断梯度链
损失求和后执行的loss = torch.autograd.Variable(loss, requires_grad=True)是完全错误的操作:PyTorch 0.4版本后Variable已和Tensor合并,这种写法会新建一个独立的叶子张量,彻底断开和前序计算图、模型参数的关联,反向传播时梯度根本无法回传到模型参数,参数自然全程不更新。
修复代码
首先重写自定义损失,全程使用PyTorch原生算子,保留计算图,同时修正原实现中角度未转弧度的逻辑错误(三角函数默认输入为弧度值,原NumPy实现直接传入角度值计算结果是错的):
import torch def my_custom_loss(preds, label): angle_diff = preds - label half_angle_diff = angle_diff / 2 # 如果你的标签角度是角度制,保留下面这行转弧度;如果是弧度制则删除 half_angle_diff = torch.deg2rad(half_angle_diff) sine_diff = torch.sin(half_angle_diff) square_sum = torch.nansum(sine_diff ** 2) return square_sum
然后删除多余的Variable包装,修正损失计算逻辑:
mse_loss = torch.nn.MSELoss() # 前向传播得到preds、label后 loss1 = mse_loss(preds[:, (0,1,3)], label[:, (0,1,3)]) loss2 = my_custom_loss(preds[:, 2], label[:, 2]) # 直接求和即可,不需要额外包装 loss = loss1 + loss2 # 正常执行反向传播 loss.backward() # 后续执行优化器step、梯度清零等常规操作即可
修复后可以通过打印验证:loss2会成为带grad_fn标记的可微分张量,最终的loss也会保留完整的计算图链路,模型参数可以正常接收梯度更新。
内容的提问来源于stack exchange,提问作者fariadantes
相关产品推荐
相关产品推荐

