Samplers¶
Samplers decide which dataset indices form each batch. MPerClassSampler,
HierarchicalSampler, and FixedSetOfPairs yield whole batches; pass them as
DataLoader(..., batch_sampler=...) and omit batch_size and shuffle.
FixedSetOfTriplets yields one dataset index at a time; pass it to
DataLoader(..., sampler=...).
Labels describe the whole dataset: labels[i] is the label of dataset item i.
MPerClassSampler, FixedSetOfPairs, and FixedSetOfTriplets accept a
one-dimensional tensor, a NumPy array, or any iterable of hashable labels, such
as a list of strings
(types.DatasetLabels).
The examples on this page use this dataset of 64 items in 16 classes:
import torch
from pytorch_metric_learning import samplers
labels = torch.arange(64) // 4
dataset = torch.utils.data.TensorDataset(torch.randn(64, 8), labels)
Randomness and coverage¶
Every sampler takes a keyword-only generator: torch.Generator | None. With
None, sampling uses PyTorch's global random number generator, so
torch.manual_seed makes it repeatable; a dedicated generator isolates sampler
randomness from model initialization and dropout. Each new iterator draws fresh
random batches, and two samplers given generators with the same seed produce the
same sequence of epochs. Samplers run in the main process, so DataLoader
worker seeding does not affect them.
Samplers balance labels within batches, and balance has priority over visiting
every example equally often: when class sizes differ, equal class counts
per batch must repeat small classes. MPerClassSampler documents its
coverage guarantees. HierarchicalSampler draws classes
and examples independently for each batch, with replacement across batches, so
its iterators are not epochs. A class with fewer members than requested is
sampled with replacement within its batch.
MPerClassSampler¶
Yields batches of batch_size // m different classes with m indices per
class, grouped by class. For example, batch_size=100 and m=5 gives 20
classes with 5 samples each in every batch.
sampler = samplers.MPerClassSampler(labels, m=4, batch_size=32)
loader = torch.utils.data.DataLoader(dataset, batch_sampler=sampler)
assert len(loader) == len(sampler) == 2
samplers.MPerClassSampler(
labels: Tensor | np.ndarray | Iterable[Hashable],
m: int,
batch_size: int,
num_batches: int | None = None,
*,
generator: torch.Generator | None = None,
)
Parameters:
- labels: The dataset labels.
- m: The number of samples per class in each batch.
- batch_size: The number of indices per batch. Construction raises
ValueErrorunlessbatch_sizeis a multiple ofmandm * (number of unique labels) >= batch_size. - num_batches: The number of batches one iterator yields, which is
len(sampler). It must be positive. WithNone, the sampler uses the smallest length with the default coverage guarantee below:ceil(ceil(largest_class_size / m) * num_classes / (batch_size // m)). - generator: Optional
torch.Generator; see randomness and coverage.
Coverage guarantees¶
Classes rotate through shuffled passes, and so do the examples of each class. In every iterator, at every batch:
- Every batch has exactly
batch_size // mdifferent classes andmindices for each. - A class appears at most once more than any other class.
- An example appears at most once more than any other example of its class.
- A batch repeats an example only when its class has fewer than
mexamples.
With the default num_batches, every example appears at least once per
iterator. Each example appears exactly once when all classes have the same size,
m divides that size, and batch_size // m divides the number of classes.
With unequal class sizes, exactly-once coverage is impossible: every class
appears equally often, so the default length is set by the largest class, and
smaller classes repeat. On long-tailed data that length can be large. Pass a
smaller num_batches to shorten an iterator; the guarantees that hold at every
batch still apply, but examples of large classes may be missed.
HierarchicalSampler¶
Implementation of the sampler used in Deep Metric Learning to Rank.
Each batch is built as follows:
- Select X super classes.
- For each super class, select Y samples from each of Z classes, such that Y * Z equals the batch size divided by X.
X is super_classes_per_batch and Y is samples_per_class. The sampler yields
lists of dataset indices, so pass it as the batch_sampler argument:
hierarchical_labels = torch.stack([labels, labels // 4], dim=1)
sampler = samplers.HierarchicalSampler(
hierarchical_labels, batch_size=32, samples_per_class=4
)
loader = torch.utils.data.DataLoader(dataset, batch_sampler=sampler)
assert all(len(batch) == 32 for batch in sampler)
samplers.HierarchicalSampler(
labels: Tensor | np.ndarray | Sequence[Sequence[int]],
batch_size: int,
samples_per_class: int | Literal["all"],
batches_per_super_tuple: int = 4,
super_classes_per_batch: int = 2,
inner_label: int = 0,
outer_label: int = 1,
*,
generator: torch.Generator | None = None,
)
Parameters:
- labels: A two-dimensional array of integer labels with one row per dataset item and one column per hierarchy level.
- batch_size: The number of indices per batch. It must be positive and a
multiple of
super_classes_per_batch, andbatch_size // super_classes_per_batchmust be a multiple ofsamples_per_class; otherwise construction raisesValueError. - samples_per_class: The number of samples per class in a batch, Y in the
explanation above, or
"all"to take every sample of each chosen class."all"suits datasets with few samples per class. - batches_per_super_tuple: The number of batches created for each combination of super classes.
- super_classes_per_batch: The number of super classes per batch, X in the explanation above.
- inner_label: The column of
labelsholding classes. - outer_label: The column of
labelsholding super classes. - generator: Optional
torch.Generator; see randomness and coverage.
len(sampler) is batches_per_super_tuple times the number of combinations of
super_classes_per_batch super classes. Each super class fills
batch_size // super_classes_per_batch positions with whole classes in random
order, skipping a class that would overflow its share. A batch is shorter than
batch_size when a super class lacks enough classes to fill its share.
Mining-weighted sampling¶
To sample examples in proportion to how often a miner selects them, pass
utils.indexing.convert_to_weights output to PyTorch's WeightedRandomSampler.
The executable recipe
shows embedding extraction, subset-index mapping, and seeded sampling.
FixedSetOfPairs¶
Creates a fixed set of positive and negative pairs at construction. Use it when training or evaluating on the same pairs every epoch.
samplers.FixedSetOfPairs(
labels: Tensor | np.ndarray | Iterable[Hashable],
num_pairs: int,
batch_size: int,
*,
generator: torch.Generator | None = None,
)
Parameters:
- labels: The dataset labels.
- num_pairs: The number of pairs to create. It must be positive. Half are positive and half negative, with the extra pair positive when the number is odd.
- batch_size: The number of indices per batch, so
batch_size // 2pairs. It must be positive and even. - generator: Optional
torch.Generatorused for creation and ordering.
A positive pair draws a class with at least two examples, then two different
examples of it. A negative pair draws two different classes, then one example of
each. Pairs are drawn independently, so a pair can repeat. Construction raises
ValueError for an invalid num_pairs or batch_size, for fewer than two
classes, and when no class has two examples.
The pairs are drawn once, in sampler.fixed_set_of_pairs, a (num_pairs, 2)
index tensor. Each iterator yields them in a new random order. Each batch is a
list [first_0, second_0, first_1, second_1, ...], so a pair is never split, and
batches can hold any mix of positive and negative pairs. The last batch has
fewer pairs when batch_size // 2 does not divide num_pairs.
len(sampler) is ceil(num_pairs / (batch_size // 2)).
Recover PairIndices from each embedding batch, using the labels to tell
positive pairs from negative ones:
from pytorch_metric_learning import losses
sampler = samplers.FixedSetOfPairs(labels, num_pairs=30, batch_size=8)
loader = torch.utils.data.DataLoader(dataset, batch_sampler=sampler)
model = torch.nn.Linear(8, 4)
loss_func = losses.ContrastiveLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
for data, batch_labels in loader:
embeddings = model(data)
rows = torch.arange(len(embeddings), device=embeddings.device)
first, second = rows.reshape(-1, 2).unbind(1)
positive = batch_labels[first] == batch_labels[second]
indices_tuple = (
first[positive],
second[positive],
first[~positive],
second[~positive],
)
loss = loss_func(embeddings, batch_labels, indices_tuple=indices_tuple)
optimizer.zero_grad()
loss.backward()
optimizer.step()
assert len(loader) == 8
FixedSetOfTriplets¶
Creates a fixed set of triplets at construction. Use it when the only ground truth for evaluating an algorithm is a set of triplets.
samplers.FixedSetOfTriplets(
labels: Tensor | np.ndarray | Iterable[Hashable],
num_triplets: int,
*,
generator: torch.Generator | None = None,
)
Each triplet draws two different classes, then an anchor and a positive from the
first class and a negative from the second. A class with one sample yields an
anchor equal to its positive. Construction raises ValueError when
num_triplets is not positive or labels has fewer than two classes.
The triplets are drawn once, when the sampler is created; each iterator yields
them in a new random order as consecutive (anchor, positive, negative) index
triples. Use a DataLoader batch size divisible by 3 and recover the tuple
indices directly from each embedding batch:
from pytorch_metric_learning import losses
sampler = samplers.FixedSetOfTriplets(labels, num_triplets=30)
loader = torch.utils.data.DataLoader(dataset, batch_size=9, sampler=sampler)
model = torch.nn.Linear(8, 4)
loss_func = losses.TripletMarginLoss()
data, batch_labels = next(iter(loader))
embeddings = model(data)
indices_tuple = (
torch.arange(len(embeddings), device=embeddings.device).reshape(-1, 3).unbind(1)
)
loss = loss_func(embeddings, batch_labels, indices_tuple=indices_tuple)
Parameters:
- labels: The dataset labels.
- num_triplets: The number of triplets to create.
- generator: Optional
torch.Generatorused for creation and ordering.