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但整体准确率不高”的矛盾现象。验证与修正方式
- 修改代码,在每个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}") - 检查标签
y的形状:确保y的形状和模型输出outputs一致(都是(batch_size, 1)),避免广播机制导致的损失计算偏差。如果y是(batch_size,)格式,可添加y = y.unsqueeze(1)调整形状。 - 调整学习率:SGD的初始学习率0.1可能偏大,训练后期容易出现震荡,可尝试降低到0.01,或使用学习率调度器动态调整。
- 修改代码,在每个epoch结束后计算并打印整个训练集的平均损失,而不是单个批次的损失:
内容的提问来源于stack exchange,提问作者rwallace
相关产品推荐
相关产品推荐

