解决detectron2中ValueError: Milestone must be smaller than start epoch: start_epoch=0, milestone=0


结论

把报错文件
home/user_name/miniconda3/envs/envs_name/lib/python3.8/site-packages/fvcore/common/param_scheduler.py
中的
if start_epoch ≥ milestone:
修改为
if start_epoch > milestone:

由于这一报错在网上没找到太多讨论,该结论是我个人debug过程中认为较为合理的解决方案,毕竟需要修改非项目文件,如果有不妥欢迎留言讨论~



1 问题描述

在训练mask-rcnn开始前时出现如下报错,可以看到是依赖库detectron2在调用依赖库fvcore时出现了报错。

在这里插入图片描述


定位到最终的报错位置,可以看到,如果 start_epoch ≥ milestone 的话,就会出现当前报错。而我们现在 start_epochmilestone 的值均为0。
在这里插入图片描述



2 解决过程

2.1 debug

首先要弄明白变量的物理含义和所在函数的作用。

报错代码位于 fvcore 库的 commom/param_scheduler.py 中:


class MultiStepParamScheduler(ParamScheduler):
    """
    Takes a predefined schedule for a param value, and a list of epochs or steps
    which stand for the upper boundary (excluded) of each range.

    Example:

        .. code-block:: python

          MultiStepParamScheduler(
            values=[0.1, 0.01, 0.001, 0.0001],
            milestones=[30, 60, 80, 120]
          )

    Then the param value will be 0.1 for epochs 0-29, 0.01 for
    epochs 30-59, 0.001 for epochs 60-79, 0.0001 for epochs 80-120.
    Note that the length of values must be equal to the length of milestones
    plus one.
    """

    def __init__(
        self,
        values: List[float],
        num_updates: Optional[int] = None,
        milestones: Optional[List[int]] = None,
    ) -> None:
        """
        Args:
            values: param value in each range
            num_updates: the end of the last range. If None, will use ``milestones[-1]``
            milestones: the boundary of each range. If None, will evenly split ``num_updates``

        For example, all the following combinations define the same scheduler:

        * num_updates=90, milestones=[30, 60], values=[1, 0.1, 0.01]
        * num_updates=90, values=[1, 0.1, 0.01]
        * milestones=[30, 60, 90], values=[1, 0.1, 0.01]
        * milestones=[3, 6, 9], values=[1, 0.1, 0.01]  (ParamScheduler is scale-invariant)
        """
        if num_updates is None and milestones is None:
            raise ValueError("num_updates and milestones cannot both be None")
        if milestones is None:
            # Default equispaced drop_epochs behavior
            milestones = []
            step_width = math.ceil(num_updates / float(len(values)))
            for idx in range(len(values) - 1):
                milestones.append(step_width * (idx + 1))
        else:
            if not (
                isinstance(milestones, Sequence)
                and len(milestones) == len(values) - int(num_updates is not None)
            ):
                raise ValueError(
                    "MultiStep scheduler requires a list of %d miletones"
                    % (len(values) - int(num_updates is not None))
                )

        if num_updates is None:
            num_updates, milestones = milestones[-1], milestones[:-1]
        if num_updates < len(values):
            raise ValueError(
                "Total num_updates must be greater than length of param schedule"
            )

        self._param_schedule = values
        self._num_updates = num_updates
        self._milestones: List[int] = milestones

        start_epoch = 0
        for milestone in self._milestones:
            # Do not exceed the total number of epochs
            if milestone >= self._num_updates:
                raise ValueError(
                    "Milestone must be smaller than total number of updates: "
                    "num_updates=%d, milestone=%d" % (self._num_updates, milestone)
                )
            # Must be in ascending order
            if start_epoch >= milestone:
                raise ValueError(
                    "Milestone must be smaller than start epoch: start_epoch=%d, milestone=%d"
                    % (start_epoch, milestone)
                )
            start_epoch = milestone

根据描述,函数MultiStepParamScheduler具有三个输入,分别为valuesnum_updatesmilestones,函数作用就是确保输入的格式正确,否则输出报错或者为其分配正确的值。


其中,milestones 是一个 list,而 milestone 是该 list 中的元素。
start_epoch 是函数内定义的变量,取值为0。

在这里插入图片描述

我们进入Traceback中报错位置的上一级代码,查看函数 MultiStepParamScheduler 的调用情况:

在这里插入图片描述
在这里插入图片描述可以看到milestones = steps,而steps定义为cfg.SOLVER.STEPS中小于等于cfg.SOLVER.MAX_ITER的部分,cfg为函数build_lr_scheduler的输入。


我们依次进入Traceback中的上一级代码,查看cfg的定义:

在这里插入图片描述在这里插入图片描述终于找到了定义cfg的地方。
可以看到,cfg取值于配置文件./mask_rcnn_R_50_train_defin_new.yaml


我们打开配置文件,找到SOLVER.STEPS如下:

在这里插入图片描述
在上述配置文件中,STEPS: [0, 12000, 16000] 是指学习率调度器(LR scheduler)的阶段(milestones)。这些阶段指定了在训练过程中学习率应该在何时进行调整。具体来说:

     ① 第一个milestone是:从迭代次数为0开始的初始阶段(采用初始学习率)。

     ② 第二个milestone是:在迭代次数达到12000时进行学习率的调整。

     ③ 第三个milestone是:在迭代次数达到16000时进行学习率的调整。

这些milestones用于控制训练过程中学习率的变化,可以根据具体情况调整这些milestones的数值,以满足训练模型的需求。


这里我们也看到:
LR_SCHEDULER_NAME: "WarmupMultiStepLR",意味着指定使用WarmupMultiStepLR学习率调度器
MAX_ITER: 26000,意味着训练的最大迭代次数。
函数build_lr_scheduler中定义stepscfg.SOLVER.STEPS中小于等于cfg.SOLVER.MAX_ITER的部分,也就是指最大迭代次数内需要对学习率进行调整的迭代次数。


到这里,我们知道,报错处的milestones的含义是 在第几次迭代时需要对学习率进行调整。

我们在报错处设置断点进行调试:

在这里插入图片描述
可以看到,milestones是值为 [0, 12000, 16000] 的 list,
milestone取其中第一个元素时,取值为0。



2.2 解决方案

由此,我们可以考虑以下三个解决方案:

    ① 修改 STEPS: [0, 12000, 16000]STEPS: [1, 12000, 16000]

    ② 修改 start_epoch=0start_epoch=1

    ③ 修改判定条件if start_epoch >= milestoneif start_epoch > milestone

但是,方案①为保证不出错需要进一步确认WarmupMultiStepLR学习率调度器的工作模式,方案②可能对训练过程造成影响。

值得注意的是,在函数MultiStepParamScheduler的末尾有 start_epoch = milestone
这与判定条件start_epoch ≥ milestone相矛盾,所以采用方案③是比较合适的。



在修改时,会提示Non-Project Files Protection

在这里插入图片描述这意味着我们正在修改或删除与项目无关的文件,选第一个选项即可。


修改后不再报错,开始训练:

在这里插入图片描述



  • 45
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值