结论
把报错文件
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_epoch
和 milestone
的值均为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
具有三个输入,分别为values
、num_updates
、milestones
,函数作用就是确保输入的格式正确,否则输出报错或者为其分配正确的值。
其中,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
中定义steps
为cfg.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=0
为 start_epoch=1
③ 修改判定条件if start_epoch >= milestone
为if start_epoch > milestone
但是,方案①为保证不出错需要进一步确认WarmupMultiStepLR学习率调度器
的工作模式,方案②可能对训练过程造成影响。
值得注意的是,在函数MultiStepParamScheduler
的末尾有 start_epoch = milestone
,
这与判定条件start_epoch ≥ milestone
相矛盾,所以采用方案③是比较合适的。
在修改时,会提示
Non-Project Files Protection
:
这意味着我们正在修改或删除与项目无关的文件,选第一个选项即可。
修改后不再报错,开始训练: