我有一个不平衡的数据集,想对过高代表的类进行低采样,我该如何处理。我想使用weightedrandomsampler,但我也愿意接受其他建议。
到目前为止,我假设我的代码必须像下面这样构造。但是我不知道该怎么做。
trainset = datasets.ImageFolder(path_train,transform=transform)
...
sampler = data.WeightedRandomSampler(weights=..., num_samples=..., replacement=...)
...
trainloader = data.DataLoader(trainset, batchsize = batchsize, sampler=sampler)
我希望有人可以提供帮助。非常感谢
答案 0 :(得分:3)
根据我的理解,pytorch WeightedRandomSampler的“ weights”参数与numpy.random.choice的“ p”参数有些相似,这是一个样本将被随机选择的可能性。 Pytorch使用权重代替随机样本训练示例,它们在文档中指出权重不必总和为1,所以我的意思是这并不完全像numpy的随机选择。重量越重,样本就越有可能被采样。
当replacement = True时,这意味着可以多次绘制训练示例,这意味着您可以在训练集中拥有训练示例的副本,以用于训练模型。过度采样。同时,如果权重较低,则与其他训练样本权重相比,情况恰好相反,这意味着这些样本被随机抽样的机会较低;采样不足。
我不知道将num_samples参数与火车装载程序一起使用时如何工作,但是我可以警告您不要将批量大小放在那儿。今天,我尝试放入批处理大小,结果令人震惊。我的同事把班数* 100,他的成绩要好得多。我所知道的是,您不应在此处放置批处理大小。我还尝试将所有训练数据的大小设为num_samples,虽然效果更好,但是却花了很多时间进行训练。无论哪种方式,都可以尝试一下,看看哪种最适合您。我猜想,安全的选择是将训练示例的数量用于num_samples参数。
这是我看到其他人使用的示例,我也将其用于二进制分类。似乎工作正常。您将每个课程的训练示例数取反,并为每个训练示例设置相应的权重。
使用训练集对象的快速示例
labels = np.array(trainset.samples)[:,1]
#转到数组并获取所有列标签1(即标签)
labels = labels.astype(int)
#更改为int
majority_weight = 1/num_of_majority_class_training_examples
minority_weight = 1/num_of_minority_class_training_examples
sample_weights = np.array([majority_weight, minority_weight
])#假设您的少数类在标签对象中为整数1。如果没有,请切换位置,使其为dominant_weight,多数_weight。
weights = samples_weights[labels]
#这将遍历每个训练示例,并使用标签0和1作为sample_weights对象中的索引,该对象是您希望该类的权重。
sampler = WeightedRandomSampler(weights=weights, num_samples=, replacement=True)
trainloader = data.DataLoader(trainset, batchsize = batchsize, sampler=sampler)
由于pytorch文档说权重不必总和为1,我认为您也可以使用不平衡类之间的比率。例如,如果您有100个多数派培训样本和50个少数派培训样本,则比率为2:1。为了平衡这一点,我认为您可以对每个多数类培训示例使用权重1.0,对所有少数族裔培训示例使用权重2.0,因为从技术上讲,您希望选择少数族裔的可能性增加2倍,这将平衡您的随机选择期间的课程。
我希望这可以有所帮助。抱歉,我的草率写作很匆忙,没有人回答。我自己为此苦苦挣扎,也没有找到任何帮助。如果没有任何意义,请说出这些,然后我将对其进行重新编辑,以便在我有空闲时间时更加清楚。
答案 1 :(得分:0)
基于 torchdata(免责声明:我是作者)可以创建自定义欠采样器。
首先,_Equalizer
基类:
RandomSubsetSampler
(每个类一个)torch.max
或 torch.min
)将表现为 oversampler
或 undersampler
代码:
class _Equalizer(Sampler):
def __init__(self, labels: torch.tensor, function):
if len(labels.shape) > 1:
raise ValueError(
"labels can only have a single dimension (N, ), got shape: {}".format(
labels.shape
)
)
tensors = [
torch.nonzero(labels == i, as_tuple=False).flatten()
for i in torch.unique(labels)
]
self.samples_per_label = getattr(builtins, function)(map(len, tensors))
self.samplers = [
iter(
RandomSubsetSampler(
tensor,
replacement=len(tensor) < self.samples_per_label,
num_samples=self.samples_per_label
if len(tensor) < self.samples_per_label
else None,
)
)
for tensor in tensors
]
@property
def num_samples(self):
return self.samples_per_label * len(self.samplers)
def __iter__(self):
for _ in range(self.samples_per_label):
for index in torch.randperm(len(self.samplers)).tolist():
yield next(self.samplers[index])
def __len__(self):
return self.num_samples
现在,我们可以创建 undersampler
(添加了 oversampler
,因为它现在很短):
class RandomUnderSampler(_Equalizer):
def __init__(self, labels: torch.tensor):
super().__init__(labels, "min")
class RandomOverSampler(_Equalizer):
def __init__(self, labels):
super().__init__(labels, "max")
只需将您的标签传递给 __init__
(必须是 1D
,但可以有多个或二进制类),您就可以对数据进行上/下采样。</p>