PaddlePaddle/PaddleOCR · error · ValueError

Expected float between 0 and 1 pct_start, but got {}

Error message

Expected float between 0 and 1 pct_start, but got {}

What it means

Thrown by OneCycleDecay.__init__ in PaddleOCR's LR scheduler when the pct_start argument fails validation. The guard requires pct_start to be a Python float AND strictly within [0, 1]. Because the isinstance(pct_start, float) check is part of the same condition, even an in-range int (0 or 1) is rejected, as are strings like '0.2' that some config loaders deliver.

Source

Thrown at ppocr/optimizer/lr_scheduler.py:119

                },
            ]
        else:
            self._schedule_phases = [
                {
                    "end_step": float(pct_start * self.total_steps) - 1,
                    "start_lr": self.initial_lr,
                    "end_lr": self.max_lr,
                },
                {
                    "end_step": self.total_steps - 1,
                    "start_lr": self.max_lr,
                    "end_lr": self.min_lr,
                },
            ]

        # Validate pct_start
        if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float):
            raise ValueError(
                "Expected float between 0 and 1 pct_start, but got {}".format(pct_start)
            )

        # Validate anneal_strategy
        if anneal_strategy not in ["cos", "linear"]:
            raise ValueError(
                "anneal_strategy must by one of 'cos' or 'linear', instead got {}".format(
                    anneal_strategy
                )
            )
        elif anneal_strategy == "cos":
            self.anneal_func = self._annealing_cos
        elif anneal_strategy == "linear":
            self.anneal_func = self._annealing_linear

        super(OneCycleDecay, self).__init__(max_lr, last_epoch, verbose)

    def _annealing_cos(self, start, end, pct):

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Pass pct_start as an explicit float literal, e.g. pct_start=0.1 or pct_start=1.0 for the boundary values.
  2. If the value comes from a config file, unquote it (pct_start: 0.1, not pct_start: '0.1') so YAML parses it as a float.
  3. If it may be computed dynamically, coerce before constructing: pct_start = float(pct_start).

Example fix

# before
OneCycleDecay(max_lr=0.001, total_steps=10000, pct_start=1)  # int -> ValueError

# after
OneCycleDecay(max_lr=0.001, total_steps=10000, pct_start=1.0)  # float literal
Defensive patterns

Strategy: type-guard

Validate before calling

def valid_pct_start(v) -> float:
    v = float(v)  # raises early with a clear message if non-numeric
    if not (0.0 <= v <= 1.0):
        raise ValueError(f"pct_start must be within [0,1], got {v}")
    return v

pct = valid_pct_start(cfg['Optimizer']['scheduler'].get('pct_start', 0.1))

Type guard

def is_valid_pct_start(v) -> bool:
    return isinstance(v, float) and 0.0 <= v <= 1.0

Prevention

When it happens

Trigger: Constructing OneCycleDecay (directly or via a training config with scheduler.name: OneCycleDecay) with pct_start that is out of range, an int (e.g. pct_start=1), or a non-numeric type such as a string loaded from a config file.

Common situations: YAML/JSON config where pct_start is quoted ('0.3') so it arrives as a string; passing whole-number boundaries 0 or 1 as int; hand-written training scripts passing div results or ints from argparse (argparse type=int).

Related errors


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