PyTorch版CNN回归器训练失效:TensorFlow迁移的VGG16姿态回归不收敛
训练失效的核心原因及修正方案
1. 损失函数计算链路断裂,梯度无法回传(最核心问题)
你在PoseLoss.forward方法中对模型输出的x、q重新封装了Variable并手动设置requires_grad=True,该操作会切断原模型输出对应的计算图,梯度回传时无法传递到上游的网络参数,相当于模型全程没有收到梯度更新信号,参数一直保持初始化状态,所以预测值始终接近0。
同时损失计算时存在两处错误:
- 主动将张量迁移到CPU计算,无必要且增加设备调度开销
- 索引逻辑错误:
GT_q[:, 3:]是错误写法,GT_q本身已经是poseGT[:,3:]得到的4维四元数张量,再次取[:,3:]会仅剩最后1维,损失计算完全错误
修正后的PoseLoss代码:
class PoseLoss(nn.Module): def __init__(self, beta, device = 'cuda'): super(PoseLoss, self).__init__() self.beta = beta self.t_loss_fn = nn.MSELoss() def forward(self, x, q, poseGT): GT_x = poseGT[:, 0:3].to(x.device) GT_q = poseGT[:, 3:].to(x.device) # 四元数归一化,符合四元数模长为1的约束 q = nn.functional.normalize(q, p=2, dim=1) loss = self.t_loss_fn(x, GT_x) + self.beta * self.t_loss_fn(q, GT_q) return loss
注意:原损失额外加了torch.sqrt变成RMSE,如果你要严格对齐PoseNet的L2平方损失,不需要开根号,会导致梯度过小拖慢收敛速度
2. 网络结构定义错误,VGG16最后一层修改逻辑失效
你修改VGG16分类头的代码nn.ReLU(nn.Linear(4096, 4096))是完全错误的写法:nn.ReLU的构造参数仅接受inplace布尔值,你传入Linear层不会被作为网络层执行,相当于直接把VGG16最后一层替换成了无参数的ReLU激活,后续全连接层的输入没有经过可训练的线性变换,特征提取链路失效。
修正后的Net类代码:
class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.backbone = models.vgg16(pretrained=True) # 正确替换最后一层:先Linear再ReLU self.backbone.classifier[6] = nn.Sequential( nn.Linear(4096, 4096), nn.ReLU() ) self.fcl = nn.Sequential(nn.Linear(4096, 4096), nn.ReLU(), nn.Linear(4096, 2048), nn.ReLU()) self.xyz = nn.Linear(2048, 3) self.q = nn.Linear(2048, 4) def forward(self, x): x1 = self.backbone(x) x2 = self.fcl(x1) xyz = self.xyz(x2) q = self.q(x2) return xyz, q
3. 其他可优化点
- 单样本训练时可以把
num_workers设为0,避免多进程加载的额外开销 - 学习率1e-4对于单样本训练来说偏小,可以尝试调到1e-3加速收敛
- 训练时建议冻结VGG16的特征层(backbone.features部分)参数,仅训练分类头和新增的全连接层,避免预训练特征被破坏
内容的提问来源于stack exchange,提问作者Wooni
相关产品推荐
相关产品推荐

