PyTorch中基于Inception的双流模型损失与精度恒定问题排查
我正在训练一个二值输出的双流Inception模型:编码器采用Inception架构,解码器为自定义结构,包含卷积层、批量归一化、上采样,非线性激活试过tanh和relu均无效。模型初始化值不同但参数完全不更新,训练过程中损失与精度始终恒定(曲线呈水平直线)。已尝试SGD、Adam、RMSprop等多种优化器及参数调整,仍无收敛迹象。
模型forward函数代码:
def forward(self, inp): # Preprocessing out = self.conv3d_1a_7x7(inp) skip1 = out out = self.maxPool3d_2a_3x3(out) out = self.dropout(out) out = self.conv3d_2b_1x1(out) out = self.conv3d_2c_3x3(out) out = self.maxPool3d_3a_3x3(out) out = self.dropout(out) out = self.mixed_3b(out) skip2 = out out = self.mixed_3c(out) out = self.maxPool3d_4a_3x3(out) out = self.dropout(out) out = self.mixed_4b(out) out = self.mixed_4c(out) out = self.dropout(out) out = self.mixed_4d(out) skip3 = out out = self.dropout(out) out = self.mixed_4e(out) out = self.mixed_4f(out) out = self.maxPool3d_5a_2x2(out) out = self.dropout(out) out = self.mixed_5b(out) out = self.mixed_5c(out) out = self.dropout(out) out = self.tconv6(out, skip1,skip2,skip3) out = self.sigmoid(out) print("Before permutation", out.shape) out = out.permute(0,1,3,4,2) out_logits = out return out, out_logits
训练函数代码:
misc,out_logits[stream] = models[stream](data[stream]) out_softmax = torch.nn.functional.softmax(out_logits[stream], 1).requires_grad_() val, preds = torch.max(out_logits[stream].data, 1) preds = preds.to(device, dtype=torch.float) gt = torch.round(gt) gt_avg = torch.mean(gt) gt[gt>gt_avg] = 1 gt[gt<=gt_avg] = 0 out_logits[stream] = out_logits[stream].squeeze(1) losses[stream] = criterion(preds.cpu(), gt.cpu()).requires_grad_() if phase == 'train': optimizers[stream].zero_grad() losses[stream].backward(retain_graph=True) optimizers[stream].step() running_losses[stream] += losses[stream].item() * data[stream].shape[0] running_corrects[stream] += torch.sum(val.cpu() == gt_c.data.cpu()).item() correct_t = torch.sum(preds==gt_c).item() total_t = gt_c.shape[0]*gt_c.shape[1]*gt_c.shape[2]*gt_c.shape[3] acc_epc = 100*correct_t/total_t for scheduler in schedulers.values(): scheduler.step()
核心问题与修复方案
1. 梯度传播链被切断(最关键原因)
训练代码中,preds是从out_logits[stream].data取出的,.data会彻底切断张量的梯度传播链路,导致损失计算完全脱离模型参数的梯度追踪。后续调用backward()时,没有任何梯度会传递到模型参数,自然参数不会更新。
修复:
移除.data,直接从模型输出张量取最大值索引:
# 原代码:val, preds = torch.max(out_logits[stream].data, 1) val, preds = torch.max(out_logits[stream], 1) preds = preds.to(device, dtype=torch.float)
2. 损失计算逻辑与任务不匹配
这是二值分割任务,但当前用torch.max取类别索引作为预测结果,和模型最后用sigmoid输出单通道概率的逻辑矛盾。同时手动对gt做均值阈值处理,可能导致样本分布极端失衡,让模型陷入恒定预测的死循环。
修复:
- 直接用sigmoid后的输出(而非类别索引)计算损失,推荐使用
BCELoss或更稳定的BCEWithLogitsLoss(后者需移除模型最后的sigmoid):
# 假设用BCELoss,模型保留sigmoid输出 preds = out_logits[stream].squeeze(1) # 直接用模型输出的概率值 losses[stream] = criterion(preds, gt) # 无需转CPU,保持在设备上计算
- 取消gt的手动阈值处理,仅保留
torch.round(gt)(如果原始gt不是二值)。
3. 冗余操作干扰梯度传播
out_softmax变量未参与任何计算,属于冗余代码,直接删除;losses[stream].requires_grad_()是多余的,损失张量本身已具备梯度属性;retain_graph=True若无多损失反向传播需求,会浪费显存且可能导致梯度异常。
修复:
删除冗余代码,简化反向传播逻辑:
# 删除该行:out_softmax = torch.nn.functional.softmax(out_logits[stream], 1).requires_grad_() # 损失计算改为: losses[stream] = criterion(preds, gt) if phase == 'train': optimizers[stream].zero_grad() losses[stream].backward() # 移除retain_graph=True optimizers[stream].step()
4. 学习率调度器调用时机错误
当前每个batch后调用scheduler.step(),但多数PyTorch调度器需在每个epoch结束后调用,频繁更新会导致学习率下降过快,模型无法有效学习。
修复:
将scheduler.step()移至epoch循环的末尾,而非batch循环内部。
5. 编码器参数可能被冻结
若Inception编码器是预训练模型,默认可能冻结了参数,仅解码器更新,若解码器能力不足或初始化不当,会导致整体不收敛。
修复:
开启编码器参数的梯度更新:
# 遍历所有编码器模块,开启梯度 for module in [self.conv3d_1a_7x7, self.mixed_3b, self.mixed_3c, ...]: for param in module.parameters(): param.requires_grad = True
内容的提问来源于stack exchange,提问作者Meghna

