Skip to content
SR-Forge

Dataset Statistics

Statistics computed once from a dataset, at config build time.

A loss that needs the class balance should receive it as a constant, not iterate the dataset itself: the balance of a fixed training set never changes, and a loss that walks the data on construction couples itself to one dataset layout. Compute the numbers here and hand them over::

class_weights:
  _target: srforge.dataset.stats.get_class_weights
  params:
    dataset: ${ref:dataset.training}
    field: label
    num_classes: 2

criterion:
  _target: srforge.metrics.classification.CrossEntropy
  params:
    class_weights: ${ref:class_weights}

class_counts(dataset, field: str = 'target', num_classes: int = 2, *, ignore_index: Optional[int] = None, mask_field: Optional[str] = None) -> torch.Tensor

How many elements of each class field holds across dataset.

Reads every entry once, with the dataset's transforms applied — so a transform that turns the raw label into class indices (thresholding a depth map, say) runs before counting.

Parameters:

Name Type Description Default
dataset

Anything with len() and integer indexing that yields entries — an SR-Forge :class:~srforge.dataset.Dataset.

required
field str

The entry key holding the labels: class indices, integer or integer-valued floats (a 0/1 float mask counts as two classes). The same key the loss's y is bound to.

'target'
num_classes int

Number of classes; indices must lie in [0, num_classes).

2
ignore_index Optional[int]

A label value left out of the counts (e.g. 255 for "unlabelled").

None
mask_field Optional[str]

Optional entry key of a validity mask; elements where it is 0 are left out, as y_mask leaves them out of the loss.

None

Returns:

Type Description
Tensor

A float tensor of num_classes counts.

Raises:

Type Description
KeyError

An entry has no field (or mask_field).

ValueError

A label is not an integer, or lies outside the classes.

get_class_weights(dataset, field: str = 'target', num_classes: int = 2, ignore_index: Optional[int] = None, scheme: str = 'inverse_freq', *, mask_field: Optional[str] = None) -> List[float]

Class-balancing weights for CrossEntropy(class_weights=...).

Counts the classes once (see :func:class_counts) and turns the counts into one weight per class, in class order:

================== =============================== ==================== scheme weight of class c effect ================== =============================== ==================== inverse_freq n / (num_classes · n_c) rare classes weigh (default) more; balanced data gives all 1.0 proportional num_classes · n_c / n frequent classes weigh more ================== =============================== ====================

Both give 1.0 to every class on perfectly balanced data. For a yes/no map the result is [negative, positive], which is also what BinaryCrossEntropy(class_weights=...) takes.

A class that never occurs is counted as occurring once, with a warning: its weight never multiplies anything in training, but an infinite one would still poison the arithmetic.

Parameters:

Name Type Description Default
dataset

The dataset to count — usually the training set, as ${ref:dataset.training} in a config.

required
field str

The entry key holding the labels.

'target'
num_classes int

Number of classes.

2
ignore_index Optional[int]

A label value left out of the counts.

None
scheme str

"inverse_freq" or "proportional".

'inverse_freq'
mask_field Optional[str]

Optional entry key of a validity mask.

None

Returns:

Type Description
List[float]

num_classes floats — a plain list, ready for a config value.