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

BCELoss趋近于零但训练集准确率未达100%的原因探究

问题描述

我在PyTorch中运行一个前馈分类器神经网络,作为评估复杂技术的简单测试基线,但对输出存在疑惑:BCELoss趋近于零,而训练集准确率却远未达到100%。这并非过拟合问题,我仅关注训练集。

虽然损失与准确率是两个不同的函数,通常不会完全一致,但由于训练数据标签始终为0或1,每个错误预测必然至少偏离目标0.5,因此损失应显著高于零。

以下是损失与准确率的样本数据:

0.758351    0.500000
1.817934    0.500134
0.767420    0.510689
0.609878    0.516836
0.160672    0.586317
0.162658    0.617451
0.146049    0.618653
0.000277    0.583378
0.000025    0.760289
0.000000    0.872394
0.000000    0.840861
0.000001    0.870657
0.000000    0.869722

换言之,当损失趋近于零(保留六位小数)时,准确率仍低于90%,但两者数值单独看均合理。

以下是相关代码片段,我怀疑是否忽略了API的基础用法:

hidden_size = 100
epochs = 1000


class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.layers = nn.Sequential(
            nn.Linear(size * alphabet_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, hidden_size),
            nn.Tanh(),
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, 1),
            nn.Sigmoid(),
        )

    def forward(self, x):
        return self.layers(x)


device = torch.device("cpu")
model = Net().to(device)
criterion = nn.BCELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)


def accuracy(model, ds):
    n = 0
    for x, y in ds:
        y = y[0]
        with torch.no_grad():
            z = model(x)[0]
        if (y and z > 0.5) or (not y and z <= 0.5):
            n += 1
    return n / len(ds)


# train the network
for epoch in range(epochs):
    for bi, (x, y) in enumerate(train_dl):
        x = x.to(device)
        y = y.to(device)

        loss = criterion(model(x), y)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if epoch % (epochs / 20) == 0 and not bi:
            print("    %f    %f" % (loss, accuracy(model, train_ds)))
问题根源与解决办法
  • 核心问题:打印的是单批次损失而非训练集整体损失
    你当前代码中打印的loss是训练时当前批次的平均损失,而准确率是整个训练集的准确率。当模型刚好在某个批次拟合得很好时,这个批次的损失会趋近于0,但其他批次还没训练到位,就会出现“损失接近0但整体准确率不高”的矛盾现象。

  • 验证与修正方式

    1. 修改代码,在每个epoch结束后计算并打印整个训练集的平均损失,而不是单个批次的损失:
      def compute_avg_loss(model, dataloader, criterion, device):
          total_loss = 0.0
          total_samples = 0
          with torch.no_grad():
              for x, y in dataloader:
                  x = x.to(device)
                  y = y.to(device)
                  outputs = model(x)
                  loss = criterion(outputs, y)
                  total_loss += loss.item() * x.size(0)
                  total_samples += x.size(0)
          return total_loss / total_samples
      
      # 修改训练循环的打印逻辑
      for epoch in range(epochs):
          for bi, (x, y) in enumerate(train_dl):
              x = x.to(device)
              y = y.to(device)
      
              optimizer.zero_grad()
              outputs = model(x)
              loss = criterion(outputs, y)
              loss.backward()
              optimizer.step()
      
          # 每个epoch结束后计算并打印整体损失和准确率
          if epoch % (epochs // 20) == 0:
              avg_loss = compute_avg_loss(model, train_dl, criterion, device)
              acc = accuracy(model, train_ds)
              print(f"    {avg_loss:.6f}    {acc:.6f}")
      
    2. 检查标签y的形状:确保y的形状和模型输出outputs一致(都是(batch_size, 1)),避免广播机制导致的损失计算偏差。如果y是(batch_size,)格式,可添加y = y.unsqueeze(1)调整形状。
    3. 调整学习率:SGD的初始学习率0.1可能偏大,训练后期容易出现震荡,可尝试降低到0.01,或使用学习率调度器动态调整。

内容的提问来源于stack exchange,提问作者rwallace

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 19:25:31