不平衡学习是一种机器学习范例,其中分类器必须从具有倾斜的类分布的数据集中学习。不平衡的数据集可能对分类器的性能产生不利影响。
重新平衡数据集是处理类不平衡的一种方法。这可以通过以下方式完成:
PyTorch提供了一些用于重新平衡数据集的实用程序,但它们仅限于已知长度的批处理数据集(即,它们要求数据集具有__len__方法)。诸如ufoym / imbalanced-dataset-sampler之类的社区贡献很可爱,但它们也仅适用于批处理数据集(在PyTorch行话中也称为地图样式数据集)。 pytorch / pytorch存储库上还存在一个GitHub问题,但它似乎不太活跃。
因此,该存储库实现了包装IterableDataset的数据重采样器。在此拉取请求中,后者已添加到PyTorch。特别是,提供的方法不需要您必须事先知道数据集的大小。每种方法都适用于二进制和多类分类。
$ pip install pytorch_resample
作为一个正在运行的示例,我们将定义一个IterableDataset,它对scikit-learn的make_classification函数的输出进行迭代。
>>> from sklearn import datasets>>> import torch>>> class MakeClassificationStream(torch.utils.data.IterableDataset):...... def __init__(self, *args, **kwargs):... self.X, self.y = datasets.make_classification(*args, **kwargs)...... def __iter__(self):... yield from iter(zip(self.X, self.y))
可以将以上数据集提供给DataLoader,以迭代Tensor批次。 为了举例说明,我们将生成10.000个样本,其中50%的0s,40%的1s和10%的2s。 我们可以使用collections.Counter来衡量有效的类分布。
>>> import collections>>> dataset = MakeClassificationStream(... n_samples=10_000,... n_classes=3,... n_informative=6,... weights=[.5, .4, .1],... random_state=42... )>>> y_dist = collections.Counter()>>> batches = torch.utils.data.DataLoader(dataset, batch_size=16)>>> for xb, yb in batches:... y_dist.update(yb.numpy())>>> for label in sorted(y_dist):... print(f'• {label}: {y_dist[label] / sum(y_dist.values()):.2%} ({y_dist[label]})')• 0: 49.95% (4995)• 1: 39.88% (3988)• 2: 10.17% (1017)
可以使用pytorch_resample.UnderSampler类对数据流进行欠采样。 后者是包装器,必须提供IterableDataset和所需的类分发。 它继承自IterableDataset,因此可以代替包装的数据集使用。 举个例子,让它被平等地表示。
如图所示,观察到的类分布接近指定的分布。 实际上,比上面少的0和1。 请注意,desired_dist参数的值不需要总和为1,因为这是自动完成的。
您可以使用pytorch_resample.OverSampler来对数据进行过度采样。 它具有与pytorch_resample.UnderSampler相同的签名,因此可以完全相同的方式使用。
>>> sample = pytorch_resample.OverSampler(... dataset=dataset,... desired_dist={0: .33, 1: .33, 2: .33},... seed=42... )>>> y_dist = collections.Counter()>>> batches = torch.utils.data.DataLoader(sample, batch_size=16)>>> for xb, yb in batches:... y_dist.update(yb.numpy())>>> for label in sorted(y_dist):... print(f'• {label}: {y_dist[label] / sum(y_dist.values()):.2%} ({y_dist[label]})')• 0: 33.21% (4995)• 1: 33.01% (4965)• 2: 33.78% (5080)
在这种情况下,1和2已过采样。
pytorch_resample.HybridSampler类可用于在欠采样和过采样之间折衷。 它接受一个名为sample_rate的额外参数,该参数确定要使用的数据百分比。 这样可以控制用于训练的数据量,同时确保班级分配遵循所需的分配。
>>> sample = pytorch_resample.HybridSampler(... dataset=dataset,... desired_dist={0: .33, 1: .33, 2: .33},... sampling_rate=.5, # use 50% of the dataset... seed=42... )>>> y_dist = collections.Counter()>>> batches = torch.utils.data.DataLoader(sample, batch_size=16)>>> for xb, yb in batches:... y_dist.update(yb.numpy())>>> for label in sorted(y_dist):... print(f'• {label}: {y_dist[label] / sum(y_dist.values()):.2%} ({y_dist[label]})')• 0: 33.01% (1672)• 1: 32.91% (1667)• 2: 34.08% (1726)
可以看出,流样本的数量接近5000,是数据集大小的一半。
只要知道数据的类别分布,就可以预先确定每个重采样器将流回的确切样本数。
>>> n = 10_000>>> desired = {'cat': 1 / 3, 'mouse': 1 / 3, 'dog': 1 / 3}>>> actual = {'cat': .5, 'mouse': .4, 'dog': .1}>>> pytorch_resample.UnderSampler.expected_size(n, desired, actual)3000>>> pytorch_resample.OverSampler.expected_size(n, desired, actual)15000>>> pytorch_resample.HybridSampler.expected_size(n, .5)5000
根据设计,UnderSampler和HybridSampler可以一个接一个地重复采样。 这可能不是理想的,因为通常需要在每个批次中使样品多样化。 因此,我们建议您使用改组缓冲区,例如此处建议的ShuffleDataset类。
据我所知,该软件包中实现的方法在文献中并不存在。 我首先偶然发现了欠采样方法,该方法等效于拒绝采样。 然后,我为过采样和混合方法制定了必要的公式。 后者都基于从Poisson分布中抽样的想法,我是从Nikunj Oza和Stuart Russell的Online Bagging and Boosting论文中选取的。 创新之处在于确定满足所需类别分布的比率。
$ python -m venv .env$ source .env/bin/activate$ pip install poetry$ poetry install$ pytest