如何处理不平衡的多标签数据集?

问题描述 投票:0回答:1

我目前正在尝试使用 Pytorch dendnet121 训练具有 4 个标签(A、B、C、D)的图像分类模型。我有 224000 张图像,每张图像都以

[1, 0, 0, 1]
的形式标记(标签 A 和 D 出现在图像中)。我已经替换了densenet121的最后一个密集层。该模型使用 Adam 优化器进行训练,LR 为 0.0001(每个 epoch 衰减系数为 10),并训练 4 个 epoch。当我确信班级不平衡问题得到解决后,我会尝试更多的时期。

估计的正类数量分别为

[19000, 65000, 38000, 105000]
。当我在没有类别平衡和权重(使用 BCELoss)的情况下训练模型时,我对标签 A 和 C 的召回率非常低(事实上,真阳性 TP 和假阳性 FP 小于 20)

在 Google 和 Stackoverflow 上进行广泛搜索后,我尝试了 3 种方法来解决类不平衡问题。

方法 1:类别权重 我尝试通过使用负样本与正样本的比率来实现类别权重。

y = train_df[CLASSES];
pos_weight = (y==0).sum()/(y==1).sum()

pos_weight = torch.Tensor(pos_weight)
if torch.cuda.is_available():
    pos_weight = pos_weight.cuda()
criterion = nn.BCEWithLogitsLoss(pos_weight=pos_weight)

最终的班级权重为

[10.79, 2.45, 4.90, 1.13]
。我得到了相反的效果;正面预测太多,导致精度低。

方法 2:更改类别权重的逻辑

我还尝试通过获取数据集中正样本的比例并获取逆来获得类别权重。最终的班级权重为

[11.95, 3.49, 5.97, 2.16]
。我仍然得到太多积极的预测。

class_dist = y.apply(pd.Series.value_counts)
class_dist_norm = class_dist.loc[1.0]/class_dist.loc[1.0].sum()
pos_weight = 1/class_dist_norm

方法 3:焦点丢失

我还尝试了以下实现的焦点损失(但仍然得到太多积极的预测)。我已使用

alpha
参数的类别权重。这是从https://gist.github.com/f1recracker/0f564fd48f15a58f4b92b3eb3879149b引用的,但我做了一些修改以更好地适合我的用例。

class FocalLoss(nn.CrossEntropyLoss):
    ''' Focal loss for classification tasks on imbalanced datasets '''

    def __init__(self, alpha=None, gamma=1.5, ignore_index=-100, reduction='mean', epsilon=1e-6):
        super().__init__(weight=alpha, ignore_index=ignore_index, reduction='mean')
        self.reduction = reduction
        self.gamma = gamma
        self.epsilon = epsilon
        self.alpha = alpha

    def forward(self, input_, target):
        # cross_entropy = super().forward(input_, target)
        # Temporarily mask out ignore index to '0' for valid gather-indices input.
        # This won't contribute final loss as the cross_entropy contribution
        # for these would be zero.
        target = target * (target != self.ignore_index).long()

        # p_t = p if target = 1, p_t = (1-p) if target = 0, where p is the probability of predicting target = 1

        p_t = input_ * target + (1 - input_) * (1 - target)

        # Loss = -(alpha)( 1 - p_t)^gamma log(p_t), where -log(p_t) is cross entropy => loss = (alpha)(1-p_t)^gamma * cross_entropy (Epsilon added to prevent error with log(0) when class probability is 0)
        if self.alpha != None:
            loss = -1 * self.alpha * torch.pow(1 - p_t, self.gamma) * torch.log(p_t + self.epsilon)
        else:
            loss = -1 * torch.pow(1 - p_t, self.gamma) * torch.log(p_t + self.epsilon)


        if self.reduction == 'mean':
            return torch.mean(loss)
        elif self.reduction == 'sum':
            return torch.sum(loss)
        else:
            return loss

需要注意的一点是,在第一个 epoch 之后,损失使用停滞,但不同 epoch 的指标有所不同。

我考虑过欠采样和过采样,但我不确定如何继续,因为每个图像可以有超过 1 个标签。一种可能的方法是通过复制仅具有 1 个标签的图像来进行过采样。但我担心该模型只能在具有 1 个标签的图像上进行泛化,但在具有多个标签的图像上表现不佳。

所以想问一下有没有什么方法可以尝试,或者我的方法有没有错误。

任何建议将不胜感激。

谢谢!

machine-learning pytorch multilabel-classification imbalanced-data
1个回答
0
投票

尝试加权随机采样器。它为每个样本添加权重并在批次级别执行数据平衡。

© www.soinside.com 2019 - 2024. All rights reserved.