WeightedRandomSampler
SamplerWeightedRandomSampler(weights: Sequence[float] | Tensor, num_samples: int, replacement: bool = True, generator: Generator | int | None = None)Yield indices drawn proportionally to user-supplied weights.
Each index i is selected with probability proportional to
weights[i]. Internally the weights are normalised by their sum so
they need not form a true probability distribution on input.
Parameters
weightssequence of float or Tensori is .num_samplesintreplacementbool= TrueTrue (default), draws are independent with replacement —
the same index may appear multiple times. If False, each
index is drawn at most once: the pool shrinks as indices are
taken, so num_samples may not exceed len(weights) and is
refused at construction if it does.None (default) draws from the default generator,
so lucid.manual_seed reproduces the draws.Notes
Each index is selected with probability
so the user-supplied weights need not be normalised. The classic
use case is class-imbalance correction: set w_i = 1 / class_count[label_i] so under-represented classes are upsampled
to roughly uniform frequency. Sampling with replacement is the
default — it preserves the target marginal exactly and is the only
fully consistent option when num_samples exceeds the number of
nonzero-weight indices.
With replacement each draw inverts the cumulative weights by binary
search, per epoch for k draws. Without
replacement the indices are ranked by the exponential keys
(Efraimidis & Spirakis, 2006) and the
num_samples largest are kept, in the order one-at-a-time draws
would pick them. When fewer than num_samples weights are
positive, the draw stops after the last of them rather than handing
out an index the caller weighted out.
Examples
>>> # 3 classes, counts [900, 90, 10]; upweight rare classes
>>> import lucid
>>> from lucid.utils.data import TensorDataset, WeightedRandomSampler
>>> labels = [0] * 900 + [1] * 90 + [2] * 10
>>> my_dataset = TensorDataset(lucid.randn(1000, 4), lucid.tensor(labels))
>>> weights = [1/900]*900 + [1/90]*90 + [1/10]*10
>>> sampler = WeightedRandomSampler(weights, num_samples=1000, generator=0)
>>> for idx in sampler:
... x, y = my_dataset[idx]
>>> counts = [0, 0, 0]
>>> for idx in sampler:
... counts[labels[idx]] += 1
>>> all(250 < c < 420 for c in counts) # the classes now arrive evenly
TrueUsed by 1
Constructors
1__init__
→None__init__(weights: Sequence[float] | Tensor, num_samples: int, replacement: bool = True, generator: Generator | int | None = None)Store sampling configuration.
Parameters
weightssequence of float or Tensornum_samplesintreplacementbool= TrueRaises
ValueErrornum_samples is not positive, exceeds len(weights)
without replacement, or a weight is negative or not finite,
or — with replacement — every weight is zero.Dunder methods
2Yield num_samples indices proportional to the weights.
With replacement=True each index comes from an inverse-CDF
lookup (binary search over the running weight sums). With
replacement=False the indices with the largest exponential
keys are taken. See the class notes.
Return num_samples.