大家好!今天,我们将看看一些技巧使用深度学习模型来训练不平衡的数据集。在这种情况下,我们将处理一个不平衡的CIFAR-10图像分类数据集。
CIFAR-10数据集由10类60000张32x32彩色图像组成,每类6000张图像。训练图像50000张,测试图像10000张。
该数据集是均匀分布类型的数据集。我们可以手工制作自己的长尾CIFAR-10数据集,得到一个不平衡的数据集。如下所示。
现在我们有一个长尾的CIFAR-10数据集,其中1、2、4、5、8类数据量很大,0、7类数据量中等,3、6类数据量小,9类数据量非常小。为了测试数据,我们继续使用原来的数据集。
我们将尝试使用任何方法来改善由于训练数据不平衡而导致的较差性能,我们的假设是少数类别有更差的训练和测试错误。
我们可以考虑使用在ImageNet数据集上预训练的EfficientNetB3分类模型作为基准模型,并且添加BatchNormalization,Dropout和1个全连接层,网络体系结构的细节及其超参数如下所示。
测试结果对类数较多的类进行过拟合,模型泛化效果不佳。正如你所看到的,第3、5和9类的准确性较低。
从这个基线模型,我们将应用一些技巧来获得更高的测试精度和泛化能力更强的模型。
在这里,我们的损失函数是通过给少数类别分配相对较高的权重。我们可以使用scikit-learn库中的重新加权方法来估计不平衡数据集的类权重,以’ balanced '作为参数,类权重将由n_samples / (n_classes * np.bincount(y))给出。
from sklearn.utils import class_weight
class_weights = dict(zip(np.unique(y_train), class_weight.compute_class_weight('balanced',np.unique(y_train),y_train)))
print(class_weights)
正如你所看到的,在某些类中,数据越低,它得到的权重就越大,反之亦然。
在任何优化器中,固定的学习率都是默认的学习率计划。为了在训练阶段获得最佳的优化,选择合适的学习率是很困难的。通过在本例中对不同的学习速率进行实验,lr=0.001显示了相对较好的性能。这可以作为我们实验不同学习速率策略的基准。学习率策略是为了在特定阶段的训练阶段使用合适的学习率。学习率策略通过根据损失函数值的变化来决定学习率是否衰减。
常用的学习率策略如下:
class LossLearningRateScheduler(tf.keras.callbacks.History):
"""
base_lr: the starting learning rate
lookback_epochs: the number of epochs in the past to compare with the loss function at the current epoch to determine if progress is being made.
decay_threshold / decay_multiple: if loss function has not improved by a factor of decay_threshold * lookback_epochs, then decay_multiple will be applied to the learning rate.
spike_epochs: list of the epoch numbers where you want to spike the learning rate.
spike_multiple: the multiple applied to the current learning rate for a spike.
"""
def __init__(self, base_lr, lookback_epochs, spike_epochs = None, spike_multiple = 10, decay_threshold = 0.002, decay_multiple = 0.7, loss_type = 'val_loss'):
super(LossLearningRateScheduler, self).__init__()
self.base_lr = base_lr
self.lookback_epochs = lookback_epochs
self.spike_epochs = spike_epochs
self.spike_multiple = spike_multiple
self.decay_threshold = decay_threshold
self.decay_multiple = decay_multiple
self.loss_type = loss_type
def on_epoch_begin(self, epoch, logs=None):
if len(self.epoch) > self.lookback_epochs:
current_lr = tf.keras.backend.get_value(self.model.optimizer.lr)
target_loss = self.history[self.loss_type]
loss_diff = target_loss[-int(self.lookback_epochs)] - target_loss[-1]
if loss_diff <= np.abs(target_loss[-1]) * (self.decay_threshold * self.lookback_epochs):
print(' '.join(('Changing learning rate from', str(current_lr), 'to', str(current_lr * self.decay_multiple))))
tf.keras.backend.set_value(self.model.optimizer.lr, current_lr * self.decay_multiple)
current_lr = current_lr * self.decay_multiple
else:
print(' '.join(('Learning rate:', str(current_lr))))
if self.spike_epochs is not None and len(self.epoch) in self.spike_epochs:
print(' '.join(('Spiking learning rate from', str(current_lr), 'to', str(current_lr * self.spike_multiple))))
tf.keras.backend.set_value(self.model.optimizer.lr, current_lr * self.spike_multiple)
else:
print(' '.join(('Setting learning rate to', str(self.base_lr))))
tf.keras.backend.set_value(self.model.optimizer.lr, self.base_lr)
return tf.keras.backend.get_value(self.model.optimizer.lr)
callback_lr = LossLearningRateScheduler(base_lr=0.001, lookback_epochs=3)
处理不平衡数据集的基本方法之一是进行数据扩充和重采样。有两种重采样方法,如从多数类中删除数据的欠采样和向少数类中添加重复数据的过采样。
在这种方法中,我们结合了数据增强和重采样技术,通过应用数据增强来平衡数据集,通过重采样频率较低的样本来调整其数量。还通过旋转图像10度,并改变图像的亮度范围为0.2到1.0来达到数据增强的目的(我们也可以使用不同的数据增强)。
from keras.utils.data_utils import Sequence
from imblearn.over_sampling import RandomOverSampler
from imblearn.tensorflow import balanced_batch_generator
class BalancedDataGenerator(Sequence):
"""ImageDataGenerator + RandomOversampling"""
def __init__(self, x, y, datagen, batch_size=32):
self.datagen = datagen
self.batch_size = min(batch_size, x.shape[0])
datagen.fit(x)
self.gen, self.steps_per_epoch = balanced_batch_generator(x.reshape(x.shape[0], -1), y, sampler=RandomOverSampler(), batch_size=self.batch_size, keep_sparse=True)
self._shape = (self.steps_per_epoch * batch_size, *x.shape[1:])
def __len__(self):
return self.steps_per_epoch
def __getitem__(self, idx):
x_batch, y_batch = self.gen.__next__()
x_batch = x_batch.reshape(-1, *self._shape[1:])
return self.datagen.flow(x_batch, y_batch, batch_size=self.batch_size).next()
balanced_gen = BalancedDataGenerator(X_train, y_train, datagen, batch_size=64)
balanced_gen_val = BalancedDataGenerator(X_val, y_val, datagen, batch_size=64)
steps_per_epoch = balanced_gen.steps_per_epoch
解决类不平衡问题的常见损失函数之一是使用Focal loss。Focal Loss不是试图减少离群值或模型的预测与事实相去甚远的预测,而是减少它正确预测的值的权重(或影响)。损失函数是一种数学方法用来表示猜测值与数据点实际值之间的距离。我们也可以做一些研究和实验来使用或创建我们自己的损失函数,这项研究被称为度量学习。
深度学习模型可能会变得过于自信,因此,模型不能很好地泛化,特别是在训练阶段使用不平衡的数据集时。标签平滑是一种用于分类问题的正则化技术,以防止模型过于自信地预测标签。这个想法很简单,例如,我们有10个类,其中一个y的one-hot是这样的[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
。
根据上面的标签,我们知道标签是类别9。在我们用一个特定的平滑因子应用标签平滑之后,第9类的标签变成了这样。[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.9 0.1]
。
遇到包含大量类(超过100个类)的大规模数据集,通常使用这个技巧来在模型中获得更高的准确性。但是,因为我们的数据集只有10个类,所以没有必要应用这个技巧。
Baseline
loss: 0.0668
accuracy: 0.9790
val_loss: 0.7388
val_accuracy: 0.8322
-------------------------------------------------------------------------------
Re-Weighted
loss: 0.1454
accuracy: 0.9465
val_loss: 0.8069
val_accuracy: 0.8220
-------------------------------------------------------------------------------
Learning rate scheduler and Re-weighted
loss: 0.0360
accuracy: 0.9839
val_loss: 0.8414
val_accuracy: 0.8180
-------------------------------------------------------------------------------
Focal Loss - Learning rate scheduler and Re-weighted
loss: 0.2346
accuracy: 0.7935
val_loss: 0.4262
val_accuracy: 0.7358
-------------------------------------------------------------------------------
Learning rate scheduler ,Data augmentation and Random oversampling
loss: 0.0255
accuracy: 0.9919
val_loss: 1.2233
val_accuracy: 0.7730f
-------------------------------------------------------------------------------
Learning rate scheduler ,Data augmentation, Random oversampling and Re-weighted
loss: 0.0652
accuracy: 0.9699
val_loss: 1.1787
val_accuracy: 0.7686
学习率策略,权重加权,数据增强,随机过采样(Learning rate scheduler, Re-weighted, Data augmentation, and random oversampling):这是我们尝试的最后一个实验结果。幸运的是,考虑到泛化和准确性,结果是最理想的。我们可以看到,第3类的小数据和第9类的小数据得到了最好的改进,然后错误预测减少了一点。与之前的实验相比,总体准确率较高,学习率策略的训练性能稳定,权重加权、数据增广和随机过采样的结果泛化能力更好。
在本文的最后,从实验中,我们可以得出结论,处理不平衡的数据集有一些权衡,比如准确性高但容易过拟合,或者模型没有过拟合但性能较低。然而,我们的目标是,我们需要相当的精度和更低的损失,以得到一个泛化能力较强的模型。训练阶段的一些技巧可能有助于克服这个问题。
真实世界的数据通常具有长尾和开放式分布。一个实用的识别系统必须区分多数和少数类别,并从一些已知的实例中进行归纳。