facebookresearch/detectron2 · error · ValueError

_LRMultiplier(multiplier=) must be an instance of fvcore Par

Error message

_LRMultiplier(multiplier=) must be an instance of fvcore ParamScheduler. Got {multiplier} instead.

What it means

_LRMultiplier expects its multiplier to be an fvcore ParamScheduler (e.g. WarmupParamScheduler wrapping a MultiStepParamScheduler). Passing a float, lambda, or torch scheduler triggers this check.

Source

Thrown at detectron2/solver/lr_scheduler.py:111

    # case we only need a total of one scheduler that defines the relative LR multiplier.

    def __init__(
        self,
        optimizer: torch.optim.Optimizer,
        multiplier: ParamScheduler,
        max_iter: int,
        last_iter: int = -1,
    ):
        """
        Args:
            optimizer, last_iter: See ``torch.optim.lr_scheduler.LRScheduler``.
                ``last_iter`` is the same as ``last_epoch``.
            multiplier: a fvcore ParamScheduler that defines the multiplier on
                every LR of the optimizer
            max_iter: the total number of training iterations
        """
        if not isinstance(multiplier, ParamScheduler):
            raise ValueError(
                "_LRMultiplier(multiplier=) must be an instance of fvcore "
                f"ParamScheduler. Got {multiplier} instead."
            )
        self._multiplier = multiplier
        self._max_iter = max_iter
        super().__init__(optimizer, last_epoch=last_iter)

    def state_dict(self):
        # fvcore schedulers are stateless. Only keep pytorch scheduler states
        return {"base_lrs": self.base_lrs, "last_epoch": self.last_epoch}

    def get_lr(self) -> List[float]:
        multiplier = self._multiplier(self.last_epoch / self._max_iter)
        return [base_lr * multiplier for base_lr in self.base_lrs]


"""
Content below is no longer needed!

View on GitHub (pinned to a2f4a8771a)

Solutions

  1. Wrap the schedule in fvcore ParamSchedulers, e.g. WarmupParamScheduler(MultiStepParamScheduler(...), ...), and pass that as multiplier
  2. Or keep using WarmupMultiStepLR (deprecated but functional)

Example fix

# before
from detectron2.solver import LRMultiplier
sched = LRMultiplier(optimizer, multiplier=0.1, max_iter=iters)
# after
from fvcore.common.param_scheduler import MultiStepParamScheduler
from detectron2.solver import LRMultiplier, WarmupParamScheduler
m = MultiStepParamScheduler([0.1, 1.0], milestones=[1000], num_updates=iters)
sched = LRMultiplier(optimizer, multiplier=WarmupParamScheduler(m, 0.001, 1000/iters, 'linear'), max_iter=iters)
Defensive patterns

Strategy: type-guard

Validate before calling

from fvcore.common.param_scheduler import ParamScheduler
assert isinstance(multiplier, ParamScheduler), 'multiplier must be an fvcore ParamScheduler'

Type guard

from fvcore.common.param_scheduler import ParamScheduler
def is_param_scheduler(x) -> bool:
    return isinstance(x, ParamScheduler)

Prevention

When it happens

Trigger: Constructing LRMultiplier / _LRMultiplier directly with multiplier=0.1 or multiplier=some_torch_lambda instead of a ParamScheduler instance.

Common situations: Custom training loops replacing WarmupMultiStepLR with LRMultiplier but passing the old-style gamma float or a function.

Related errors


AI-assisted analysis of facebookresearch/detectron2@a2f4a8771a (2026-08-27). Data as JSON: /api/errors/92b8920bcbd3f13a. Report an issue: GitHub.