Long-tail distribution problem in image datasets.

ImageNetCOCO等常用视觉数据集中,由于经过人工预筛选,图像中的不同目标类别的数量是接近的。而在实际的视觉应用中,数据集大多服从长尾分布(long-tail distribution),即少数类别(称为head class)占据绝大多数样本,多数类别(称为tail class)仅有少量样本。一个典型的长尾分布数据集(Open Brands商标数据集)如下图所示。

定义数据集的不平衡率(imbalance ratio)为类别的最大数量和最小数量之比。目前常用的一些长尾分布数据集如下:

本文介绍一些解决图像数据集中长尾分布问题的方法:

  1. 重采样 Re-sampling:通过对head class进行欠采样或对tail class进行过采样,人为地构造类别均衡的数据集。包括Random under/over-sampling, Class-balanced sampling, Meta Sampler等。
  2. 重加权 Re-weighting:在损失函数中对不同类别样本的损失设置不同的权重,通常是对tail class对应的损失设置更大的权重。其中在$\log$运算之外调整损失函数的本质是在调节样本权重或者类别权重(如Inverse Class Frequency Weighting, Cost-Sensitive Cross-Entropy Loss, Focal Loss, Class-Balanced Loss)。在$\log$运算之内调整损失函数的本质是调整logits得分$z$,从而缓解对tail类别的负梯度(如Equalization Loss, Equalization Loss v2, Logit Adjustment Loss, Balanced Softmax Loss, Seesaw Loss)。
  3. 其他方法:一些方法将长尾分布问题解耦为特征的表示学习和特征的分类。一些方法按照不同类别的样本数量级对类别进行分组(如BAGS)。

1. 重采样 Re-sampling

重采样(re-sampling)的思想是通过对head class进行欠采样或对tail class进行过采样,人为地让模型学习时接触到的训练样本是类别均衡的,从而一定程度上减少对head class的过拟合。不过由于tail class的少量数据往往被反复学习,缺少足够多的样本从而容易过拟合;而head class又往往得不到充分学习。

常用的重采样方法包括:

\[p_j^{CB} = \frac{1}{C}\] \[p_j^{PB}(t) = (1-\frac{t}{T})\frac{N_j}{N} +\frac{t}{T}\frac{1}{C}\] \[p_j^{Re} = \frac{w_j}{\sum_{j=1}^{C}w_j}, \quad w_j=\frac{n_{max}}{n_j}\] \[p_j \gets p_j - \nabla_{p_j}L_{D_{meta}}(\theta)\]

pytorch中,可以为DataLoader传入采样器sample,从而实现不同类别的重采样(本质是调整样本权重)。下面给出一个实现Class-balanced采样的例子:

class_label = dataset.targets
class_count = np.array([len(np.where(class_label==c)[0]) for c in np.unique(class_label)])
weight = 1./class_count
samples_weight = np.array([weight[i] for i in class_label])
samples_weight = torch.from_numpy(samples_weight)
samples_weight = samples_weight.double()
sampler = torch.utils.data.WeightedRandomSampler(weights=samples_weight,
                                                 num_samples=len(samples_weight),
                                                 replacement=True) # 有放回采样

train_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, sampler=sampler)

2. 重加权 Re-weighting

重加权(re-weighting)的思想是在损失函数中对不同类别样本的损失设置不同的权重,通常是对tail class对应的损失设置更大的权重。但是这类方法需要针对不同的数据集和模型等条件设置不同的超参数,泛化性较差。

假设共有$C$个类别,类别$c$共有$n_c$个样本,总样本数为$n$;模型的输出logits(softmax前的输出)为$z=[z_1,z_2,…,z_C]^T$,则属于类别$c$的样本$x$的交叉熵损失函数计算为:

\[\mathcal{L}(x,c) = -\log(\frac{\exp(z_c)}{\sum_{i=1}^{C} \exp(z_i)})\]

根据对损失函数进行调整的位置是在$\log$运算之外还是在$\log$运算之内,重加权方法又可以粗略地分为两种情况。

(1) log外调整:class-level re-weighting

在$\log$运算之外调整损失函数的本质是在调节样本权重或者类别权重。

\[\mathcal{L}_{\text{ICFW}}(x,c) = -\frac{n}{n_c}\log(\frac{\exp(z_c)}{\sum_{i=1}^{C} \exp(z_i)})\] \[\mathcal{L}_{\text{CS}}(x,c) = -\frac{n_{min}}{n_c}\log(\frac{\exp(z_c)}{\sum_{i=1}^{C} \exp(z_i)})\] \[\mathcal{L}_{\text{FO}}(x,c) = -(1-\frac{\exp(z_c)}{\sum_{i=1}^{C} \exp(z_i)})^{\gamma}\log(\frac{\exp(z_c)}{\sum_{i=1}^{C} \exp(z_i)})\] \[\mathcal{L}_{\text{CB}}(x,c) = -\frac{1}{E_{n_c}} \log(\frac{\exp(z_c)}{\sum_{i=1}^{C} \exp(z_i)}) = -\frac{1-\beta}{1-\beta^{n_c}} \log(\frac{\exp(z_c)}{\sum_{i=1}^{C} \exp(z_i)})\]

(2) log内调整:class-level re-margining

注意到交叉熵损失也可以表示为:

\[-\log(\frac{\exp(z_c)}{\sum_{i=1}^{C} \exp(z_i)}) = -\log(\frac{1}{\sum_{i=1}^{C} \exp(z_i-z_c)}) = \log(\sum_{i=1}^{C} \exp(z_i-z_c))\]

注意到logsumexp函数是最大值函数的光滑近似,则交叉熵损失实际上相当于:

\[\mathcal{L}(x,c) = \max(z_1-z_c,z_2-z_c,...,z_c-z_c,...,z_C-z_c)\]

最小化上述损失函数,即使得所有非目标类的logits得分$z_{i\ne c}$均小于目标类的logits得分$z_c$。上述优化过程会对所有非目标类产生负样本梯度,抑制这些类别的预测过程,这个现象称为负梯度过抑制(negative gradient over-suppression)tail类样本作为非目标类样本的频率更高,因此抑制程度更强。

在$\log$运算之内调整损失函数的本质是调整logits得分$z$,从而缓解对tail类别的负梯度。一种可行的方法是为每个类别引入margin $m_c>0$,则logits得分调整为$z_c-m_c$,样本数量越少的类别具有越大的margin。此时损失函数的形式为:

\[-\log(\frac{\exp(z_c-m_c)}{\sum_{i=1}^{C} \exp(z_i-m_i)}) = -\log(\frac{\exp(-m_c)\exp(z_c)}{\sum_{i=1}^{C} \exp(-m_i)\exp(z_i)})\] \[\mathcal{L}_{\text{EQ}}(x,c) = -\log(\frac{\exp(z_c)}{\sum_{i=1}^{C} \tilde{w}_i \exp(z_i)}), \tilde{w}_i=1-\beta \Bbb{I}(f_i<\lambda)(1-y_i)\] \[\nabla_{z_j}^{pos'}(\mathcal{L}^{(t)}) = 1+\alpha(1-\frac{1}{1+e^{-\gamma(g_j^{(t)}-\mu)}}) \nabla_{z_j}^{pos}(\mathcal{L}^{(t)})\] \[\nabla_{z_j}^{neg'}(\mathcal{L}^{(t)}) = \frac{1}{1+e^{-\gamma(g_j^{(t)}-\mu)}} \nabla_{z_j}^{neg}(\mathcal{L}^{(t)})\] \[\mathcal{L}_{\text{LA}}(x,c) = -\log (\frac{\exp(z_c+ \log p_c)}{\sum_{i=1}^{C}\exp(z_i+ \log p_i)})\] \[\mathcal{L}_{\text{BS}}(x,c) = -\log(\frac{z_c\exp(z_c)}{\sum_{i=1}^{C} z_i\exp(z_i)})\] \[\mathcal{L}_{\text{SS}}(x,c) = -\log(\frac{\exp(z_c)}{\sum_{j≠c}^{C} \mathcal{S}_{ij} \exp(z_j) + \exp(z_c)}), \mathcal{S}_{ij} = \mathcal{M}_{ij} \cdot \mathcal{C}_{ij}\]

3. 其他方法 Others

(1) 解耦特征表示与分类

(2) 类别分组分类

⚪ 参考文献