Mask-RCNN 的哪些参数控制掩码召回?

Which parameters of Mask-RCNN control mask recall?

我对微调我用于实例分割的 Mask-RCNN 模型很感兴趣。目前我已经训练了6个epoch的模型,各种Mask-RCNN损失如下:

我停止的原因是 COCO 评估指标似乎在上一个时期有所下降:

我知道这是一个影响深远的问题,但我希望获得一些直觉,了解如何理解哪些参数对改进评估指标的影响最大。我明白要考虑三个地方:

  1. 我是否应该查看批量大小、学习率和动量,这使用学习率为 1e-4 且批量大小为 2 的 SGD 优化器?
  2. 我是否应该考虑使用更多的训练数据或添加增强(我目前没有使用任何数据)并且我的数据集当前是相当大的 40K 图像?
  3. 我应该查看具体的 MaskRCNN 参数吗?

我想我可能会被问到更具体的问题,我想改进什么,所以让我说我想提高个别口罩的召回率。该模型表现良好,但并未完全捕捉到我想要的全部内容。我也遗漏了具体学习问题的细节,因为我想获得一般如何处理这个问题的直觉。

一些注意事项:

  • 即使您使用预先训练的网络,6 个时期对于网络来说也是一个收敛的方式。尤其是resnet50这么大的。我认为你至少需要 50 个纪元。在预训练的 resnet18 上,我在 30 个时期后开始获得良好的结果,resnet34 需要 +10-20 个时期,而你的 resnet50 + 40k 训练集图像 - 肯定需要比 6 个更多的时期;
  • 绝对使用预训练网络;
  • 根据我的经验,我未能通过 SGD 获得我喜欢的结果。我开始使用 AdamW + ReduceLROnPlateau 调度程序。网络收敛得非常快,比如 50-60% 的 AP 在第 7-8 个时期,但在 50-60 个时期之后它达到 80-85,使用从一个时期到另一个时期的非常小的改进,前提是 LR 足够小。您必须熟悉梯度下降概念。我曾经认为它好像你有更多的增强,你的“山丘”覆盖着你必须能够绕过的“巨石”,这只有在你控制 LR 时才有可能。此外,AdamW 有助于解决过度拟合问题。

我就是这样做的。对于具有更高输入分辨率的网络(您输入的图像由网络本身在输入时缩放),我使用更高的 lr。

init_lr = 0.00005
weight_decay = init_lr * 100
optimizer = torch.optim.AdamW(params, lr=init_lr, weight_decay=weight_decay)
scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, verbose=True, patience=3, factor=0.75)

for epoch in range(epochs):
    # train for one epoch, printing every 10 iterations
    metric_logger = train_one_epoch(model, optimizer, train_loader, scaler, device,
                                    epoch, print_freq=10)
    
    scheduler.step(metric_logger.loss.global_avg)
    optimizer.param_groups[0]["weight_decay"] = optimizer.param_groups[0]["lr"] * 100

    # scheduler.step()

    # evaluate on the test dataset
    evaluate(model, test_loader, device=device)

    print("[INFO] serializing model to '{}' ...".format(args["model"]))
    save_and_print_size_of_model(model, args["model"], script=False)

找到这样的 lr 和权重衰减,使训练在训练结束时将 lr 耗尽到一个非常小的值,例如初始 lr 的 1/10。如果您的平台太频繁,调度程序会迅速将其调至非常小的值,并且网络将在所有剩余的 epoch 中学习不到任何东西。

您的绘图表明您的 LR 在训练的某个时刻过高,网络停止训练,然后 AP 下降。您需要不断改进,即使是很小的改进。网络训练得越多,它了解的有关您领域的细微细节就越多,学习率就越小。恕我直言,常量 LR 不允许正确执行此操作。

  • 锚生成器设置。这是我初始化网络的方式。

     def get_maskrcnn_resnet_model(name, num_classes, pretrained, res='normal'):
          print('Using maskrcnn with {} backbone...'.format(name))
    
    
          backbone = resnet_fpn_backbone(name, pretrained=pretrained, trainable_layers=5)
    
    
          sizes = ((4,), (8,), (16,), (32,), (64,))
          aspect_ratios = ((0.25, 0.5, 1.0, 2.0, 4.0),) * len(sizes)
          anchor_generator = AnchorGenerator(
              sizes=sizes, aspect_ratios=aspect_ratios
          )
    
          roi_pooler = torchvision.ops.MultiScaleRoIAlign(featmap_names=['0', '1', '2', '3'],
                                                          output_size=7, sampling_ratio=2)
    
          default_min_size = 800
          default_max_size = 1333
    
          if res == 'low':
              min_size = int(default_min_size / 1.25)
              max_size = int(default_max_size / 1.25)
          elif res == 'normal':
              min_size = default_min_size
              max_size = default_max_size
          elif res == 'high':
              min_size = int(default_min_size * 1.25)
              max_size = int(default_max_size * 1.25)
          else:
              raise ValueError('Invalid res={} param'.format(res))
    
          model = MaskRCNN(backbone, min_size=min_size, max_size=max_size, num_classes=num_classes,
                           rpn_anchor_generator=anchor_generator, box_roi_pool=roi_pooler)
    
          model.roi_heads.detections_per_img = 512
          return model
    

我需要在这里找到小对象,为什么我使用这样的锚参数。

  • 类 平衡问题。如果你只有你的对象和 bg - 没问题。如果您有更多 类,那么请确保您的训练拆分(80% 用于训练,20% 用于测试)或多或少精确地应用于您特定训练中使用的所有 类。

祝你好运!