PyTorch二分类模型首轮即达100%准确率问题求助
问题分析与修复方案
核心问题拆解
- 准确率计算逻辑完全错误:模型输出是经过sigmoid的单值(形状
[batch_size, 1]),但验证时用outputs.argmax(1)——对于单维度输出,argmax(1)会返回全0,而数据集99.99%是负例(target=0),所以准确率必然是100%,这是最直接的假象。 - 损失函数与维度不匹配:用MSELoss做二分类不合适,且训练时targets是一维数组(
[batch_size]),outputs是二维([batch_size,1]),维度不匹配导致损失计算异常,模型只需输出全0就能快速让损失趋近于0。 - 极端数据不平衡未处理:35个正例vs30万负例,模型直接输出全0就能获得极高的表面性能,完全不会学习正例特征。
具体修复步骤
1. 修正准确率计算逻辑
替换验证代码中的准确率计算部分,用阈值判断二分类结果(而非argmax):
# 验证循环内替换原val_acc计算代码 loan_model.eval() val_loss = 0 val_acc = 0 with torch.no_grad(): for inputs, targets in val_loader: outputs = loan_model(inputs) targets = targets.view(-1, 1) # 匹配outputs维度 val_loss += criterion(outputs, targets) # 用0.5阈值判断类别,计算正确数 preds = (outputs > 0.5).float() val_acc += (preds == targets).sum().item() val_loss /= len(val_loader) val_acc /= len(val_loader.dataset)
2. 更换损失函数并修复维度匹配
改用二分类专用损失函数,同时确保targets与outputs维度一致:
# 替换原损失函数定义 criterion = torch.nn.BCELoss() # 配合模型的sigmoid输出 # 或者用BCEWithLogitsLoss(更稳定,此时需删除模型forward中的sigmoid) # criterion = torch.nn.BCEWithLogitsLoss() optimizer = torch.optim.Adam(loan_model.parameters(), lr=0.001) # 降低学习率,0.01过大 # 训练循环内添加维度调整 for epoch in range(num_epochs): loan_model.train() for i, (inputs, targets) in enumerate(train_loader): optimizer.zero_grad() targets = targets.view(-1, 1) # 转为[batch_size,1],匹配outputs outputs = loan_model(inputs) loss = criterion(outputs, targets) loss.backward() optimizer.step()
3. 处理极端数据不平衡
针对你的数据情况,推荐以下两种方案:
方案A:加权损失
给正例设置更高的损失权重,让模型重视正例:
# 计算正例权重:负例数量/正例数量 pos_weight = torch.tensor([(len(train_df) - train_df['target'].sum()) / train_df['target'].sum()]) criterion = torch.nn.BCELoss(weight=pos_weight)
方案B:加权采样
用WeightedRandomSampler让DataLoader优先采样正例,平衡每个batch的正负样本比例:
from torch.utils.data import WeightedRandomSampler # 计算每个样本的权重 class_counts = train_df['target'].value_counts().values weights = 1. / class_counts sample_weights = weights[train_df['target'].values] # 创建带采样器的DataLoader sampler = WeightedRandomSampler(sample_weights, len(sample_weights), replacement=True) train_loader = data_utils.DataLoader(train_data, batch_size=32, sampler=sampler)
方案C:Focal Loss(进阶)
自定义Focal Loss,降低易分类负例的权重,聚焦难分类的正例:
class FocalLoss(torch.nn.Module): def __init__(self, alpha=0.8, gamma=2): super().__init__() self.alpha = alpha self.gamma = gamma def forward(self, outputs, targets): bce_loss = torch.nn.functional.binary_cross_entropy(outputs, targets, reduction='none') pt = torch.exp(-bce_loss) focal_loss = self.alpha * (1 - pt) ** self.gamma * bce_loss return torch.mean(focal_loss) criterion = FocalLoss(alpha=0.8, gamma=2)
4. 补充更有效的评估指标
不平衡数据下准确率毫无意义,需监控精确率、召回率、F1分数:
from sklearn.metrics import precision_score, recall_score, f1_score # 验证时收集所有预测结果和真实标签 all_preds = [] all_targets = [] loan_model.eval() with torch.no_grad(): for inputs, targets in val_loader: outputs = loan_model(inputs) preds = (outputs > 0.5).float() all_preds.extend(preds.cpu().numpy().flatten()) all_targets.extend(targets.cpu().numpy()) # 计算指标 precision = precision_score(all_targets, all_preds) recall = recall_score(all_targets, all_preds) f1 = f1_score(all_targets, all_preds) print(f'Val Precision: {precision:.4f}, Val Recall: {recall:.4f}, Val F1: {f1:.4f}')
内容的提问来源于stack exchange,提问作者Shourya Dwivedi
相关产品推荐
相关产品推荐

