PaddlePaddle/PaddleOCR · error · ValueError

Expected positive integer epochs, but got {}

Error message

Expected positive integer epochs, but got {}

What it means

The one-cycle-style LR scheduler (CosineAnnealing/OneCycle port in lr_scheduler.py) derives total_steps = epochs * steps_per_epoch and requires both to be positive ints. If epochs <= 0 or is not an int (e.g. 0 from an unset config, or a float like 10.0), it raises this ValueError at scheduler construction.

Source

Thrown at ppocr/optimizer/lr_scheduler.py:70

    Code referred in https://pytorch.org/docs/stable/_modules/torch/optim/lr_scheduler.html#OneCycleLR
    """

    def __init__(
        self,
        max_lr,
        epochs=None,
        steps_per_epoch=None,
        pct_start=0.3,
        anneal_strategy="cos",
        div_factor=25.0,
        final_div_factor=1e4,
        three_phase=False,
        last_epoch=-1,
        verbose=False,
    ):
        # Validate total_steps
        if epochs <= 0 or not isinstance(epochs, int):
            raise ValueError(
                "Expected positive integer epochs, but got {}".format(epochs)
            )
        if steps_per_epoch <= 0 or not isinstance(steps_per_epoch, int):
            raise ValueError(
                "Expected positive integer steps_per_epoch, but got {}".format(
                    steps_per_epoch
                )
            )
        self.total_steps = epochs * steps_per_epoch

        self.max_lr = max_lr
        self.initial_lr = self.max_lr / div_factor
        self.min_lr = self.initial_lr / final_div_factor

        if three_phase:
            self._schedule_phases = [
                {
                    "end_step": float(pct_start * self.total_steps) - 1,

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Set a positive integer epochs in the optimizer/scheduler config (e.g. epochs: 200)
  2. Coerce before constructing: epochs = int(epochs) and validate epochs > 0
  3. If epochs come from a computed value, wrap with max(1, int(round(epochs)))

Example fix

# before
lr = decay(..., epochs=len(dataset)/batch_size)  # float
# after
epochs = max(1, int(round(len(dataset)/batch_size)))
lr = decay(..., epochs=epochs)
Defensive patterns

Strategy: validation

Validate before calling

epochs = int(epochs)
assert isinstance(epochs, int) and epochs > 0, f'epochs must be a positive int, got {epochs!r}'

Type guard

def valid_epochs(e) -> bool:
    return isinstance(e, int) and not isinstance(e, bool) and e > 0

Prevention

When it happens

Trigger: Building the optimizer with lr_scheduler where the epochs field is 0, negative, a float, or a string parsed from YAML/CLI that was never converted to int.

Common situations: Global configs (PaddleOCR train yamls) missing epoch num so it defaults to 0; computing epochs as len(train_samples)/batch (float division); passing epochs: 100.0 from a hyperparameter search tool.

Related errors


AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14). Data as JSON: /api/errors/e2d0a42eb19bfad3. Report an issue: GitHub.