如何在BERT序列分类模型中对特定类别预测加重惩罚?
解决BERT二分类模型类别偏向问题的方法
首先纠正你当前代码里的关键错误:nn.CrossEntropyLoss()的输入应该是模型输出的logits,而非经过softmax后的结果。因为CrossEntropyLoss内部已经集成了log_softmax和NLLLoss的计算逻辑,提前做softmax会导致数值不稳定,还会损失梯度信息。所以第一步要把损失计算改成:
loss = loss_fun(out_logits, labels_tensor.long()) # 注意标签要转成整数类型,CrossEntropyLoss要求标签为int64
针对你遇到的类别不平衡(仅1个样本属于类别1)导致的模型偏向问题,最直接的解决方式是给类别1的样本设置更高的损失权重,具体实现有两种方式:
方法一:使用带权重的CrossEntropyLoss
初始化损失函数时传入类别权重参数,根据样本不平衡比例调整权重数值(比如这里类别0和1的样本数是3:1,可给类别1设3倍权重):
# 定义类别权重:类别0权重1,类别1权重3,需和模型同设备 class_weights = torch.tensor([1.0, 3.0], device='cuda:0') loss_fun = nn.CrossEntropyLoss(weight=class_weights) # 用logits计算损失 loss = loss_fun(out_logits, labels_tensor.long())
这样模型计算类别1样本的损失时会自动乘以设置的权重,相当于加重了对该类样本预测错误的惩罚。
方法二:手动计算样本级加权损失
如果需要更灵活的控制(比如针对单个样本单独调整权重),可以手动计算每个样本的损失权重:
# 给标签为1的样本设置3倍权重,其他样本权重为1 sample_weights = torch.where(labels_tensor == 1, 3.0, 1.0).to('cuda:0') # 先计算每个样本的交叉熵损失(reduction设为none保留单样本损失) per_sample_loss = nn.CrossEntropyLoss(reduction='none')(out_logits, labels_tensor.long()) # 乘以样本权重后取平均得到总损失 loss = (per_sample_loss * sample_weights).mean()
这种方式适合样本不平衡程度差异较大、需要精细化调整单样本权重的场景。
额外优化建议
- 数据层面:如果条件允许,补充更多类别1的样本,从根源上解决不平衡问题,这是最有效的方式。
- 模型训练技巧:可以尝试使用Focal Loss,它会自动降低易分类样本的权重,聚焦难分类的样本(比如你的类别1样本),核心逻辑是给错误预测的难样本赋予更高的损失权重,你可以基于PyTorch手动实现该损失函数。
内容的提问来源于stack exchange,提问作者Penguin
相关产品推荐
相关产品推荐

