You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyTorch中基于Inception的双流模型损失与精度恒定问题排查

二值输出双流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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 05:54:09