在 Pytorch 中使用 Resnet 101 主干训练更快的 R-CNN RPN 时遇到问题

数据挖掘 深度学习 美国有线电视新闻网 训练 计算机视觉 火炬
2022-01-19 11:28:01

RPN 的训练问题

我正在尝试为区域提议训练一个网络,就像在Pascal VOC 2012 训练数据上的Faster R-CNN中的锚框概念一样。

我正在使用一个预训练的Resnet 101主干,三层弹出。弹出的图层是conv5_x layer, average pooling layersoftmax layer

因此,对于大小为 600*600 的图像,我输入到 RPN 头的卷积特征图的空间分辨率为 37 x 37,具有 1024 个通道。

我已将仅块 conv4_x 的梯度设置为可训练的。从那里我使用 torchvision.models.detection rpn 代码来使用 rpn.AnchorGenerator、rpn.RPNHead 和最终 rpn.RegionProposalNetwork 类。调用 forward 会返回两个损失,即对象损失和回归损失。

我遇到的问题是我的模型训练非常非常缓慢(因为损失的改善非常缓慢)。在 Girschick 的原始论文中,他说他训练了超过 80K 小批量(自 Pascal VOC 2012 数据集有大约 11000 张图像以来大约 8 个时期),其中每个小批量是具有 256 个锚框的单个图像,但我的网络从一个时期到另一个时期改善了它的损失非常缓慢,我正在训练 30 多个 epoch。

下面是我的网络课程代码。

class ResnetRegionProposalNetwork(torch.nn.Module):
    def __init__(self):
        super(ResnetRegionProposalNetwork, self).__init__()
        self.resnet_backbone = torch.nn.Sequential(*list(models.resnet101(pretrained=True).children())[:-3])
        non_trainable_backbone_layers = 5
        counter = 0
        for child in self.resnet_backbone:
            if counter < non_trainable_backbone_layers:
                for param in child.parameters():
                    param.requires_grad = False
                counter += 1
            else:
                break

        anchor_sizes = ((32,), (64,), (128,), (256,), (512,))
        aspect_ratios = ((0.5, 1.0, 2.0),) * len(anchor_sizes)
        self.rpn_anchor_generator = rpn.AnchorGenerator(
            anchor_sizes, aspect_ratios
        )
        out_channels = 1024
        self.rpn_head = rpn.RPNHead(
            out_channels, self.rpn_anchor_generator.num_anchors_per_location()[0]
        )

        rpn_pre_nms_top_n = {"training": 2000, "testing": 1000}
        rpn_post_nms_top_n = {"training": 2000, "testing": 1000}
        rpn_nms_thresh = 0.7
        rpn_fg_iou_thresh = 0.7
        rpn_bg_iou_thresh = 0.2
        rpn_batch_size_per_image = 256
        rpn_positive_fraction = 0.5

        self.rpn = rpn.RegionProposalNetwork(
            self.rpn_anchor_generator, self.rpn_head,
            rpn_fg_iou_thresh, rpn_bg_iou_thresh,
            rpn_batch_size_per_image, rpn_positive_fraction,
            rpn_pre_nms_top_n, rpn_post_nms_top_n, rpn_nms_thresh)

    def forward(self,
                images,       # type: ImageList
                targets=None  # type: Optional[List[Dict[str, Tensor]]]
                ):
        feature_maps = self.resnet_backbone(images)
        features = {"0": feature_maps}
        image_sizes = getImageSizes(images)
        image_list = il.ImageList(images, image_sizes)
        return self.rpn(image_list, features, targets)

我正在使用具有以下参数的亚当优化器: optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, ResnetRPN.parameters()), lr=0.01, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)

我的训练循环在这里:

for epoch_num in range(epochs): # will train epoch number of times per execution of this program
        loss_per_epoch = 0.0
        dl_iterator = iter(P.getPascalVOC2012DataLoader())
        current_epoch = epoch + epoch_num
        saveModelDuringTraining(current_epoch, ResnetRPN, optimizer, running_loss)
        batch_number = 0
        for image_batch, ground_truth_box_batch in dl_iterator:
            #print(batch_number)
            optimizer.zero_grad()
            boxes, losses = ResnetRPN(image_batch, ground_truth_box_batch)
            losses = losses["loss_objectness"] + losses["loss_rpn_box_reg"]
            losses.backward()
            optimizer.step()
            running_loss += float(losses)
            batch_number += 1
            if batch_number % 100 == 0:  # print the loss on every batch of 100 images
                print('[%d, %5d] loss: %.3f' %
                      (current_epoch + 1, batch_number + 1, running_loss))
                string_to_print = "\n epoch number:" + str(epoch + 1) + ", batch number:" \
                                  + str(batch_number + 1) + ", running loss: " + str(running_loss)
                printToFile(string_to_print)
                loss_per_epoch += running_loss
                running_loss = 0.0
        print("finished Epoch with epoch loss " + str(loss_per_epoch))
        printToFile("Finished Epoch: " + str(epoch + 1) + " with epoch loss: " + str(loss_per_epoch))
        loss_per_epoch = 0.0

我正在考虑尝试以下想法来非常缓慢地修复网络训练:

  • 尝试各种学习率(尽管我已经尝试过 0.01、0.001、0.003 的结果相似
  • 各种批次大小(到目前为止,最好的结果是 4 个批次(4 张图像 * 每张图像 256 个锚点)
  • 冻结更多/更少的 Resnet-101 主干层
  • 完全使用不同的优化器
  • 损失函数的不同权重

非常感谢我的方法有任何明显错误的提示或事情。我很乐意向任何可以提供帮助的人提供更多信息。

编辑:我的网络在快速 GPU 上进行训练,图像和边界框作为火炬张量。

2个回答

在这里,我们假设您的数据中有一个模式。我的问题是:

  • 你确定在你的数据中有一个共同的模式可以检测吗?
  • 标签是否分配得当?有时,即使数据很好,您在创建标签时也会遇到一些错误,从而浪费数小时的工作时间。

对于您的其余代码,它似乎没问题。为了正确调试它,您可以尝试:

  • 找到一个非常相似的问题,你就知道这个模型会成功。一些玩具数据集应该可以做到。然后训练相同的架构,看看损失是否下降。这样您将检查您在 pytorch 中所做的是否正确。
  • 使用另一种算法或其他一些实现。可能是 ResNet101 不适合这个问题。

希望能帮助到你 :)

到目前为止,我已经尝试了一些很有帮助的事情:

  • 首先,令人尴尬的是,我将 BGR 格式的图像输入到以 RGB 格式训练的网络中。
  • 其次,尝试优化器:
optimizer = torch.optim.SGD(filter(lambda p: p.requires_grad, ResnetRPN.parameters()), lr=0.001, momentum=0.9, weight_decay=0.0005)

也许 Adam 优化器不适合卷积神经网络??

与原始论文一样,除了一个学习率调度程序,它在 24 个 epoch 后将学习率降低到 0.0001。

至于要冻结哪些层,我将尝试几乎所有方法,包括:

  • 只训练 RPN 头
  • 冻结 1 层,不再移除任何 Resnet101 顺序块
  • 在没有预先训练的权重的情况下从头开始训练整个事物
  • 使用预先训练的权重从头开始训练整个事物

此外,输入图像的归一化针对 Imagenet 数据集进行了调整,该数据集与 Pascal VOC 2012 数据集具有不同的通道均值和标准差。

此外,为了测试 RPN,我编写了一类 4 个比较 RPNS,它们生成随机框:

  • 图像中任意宽度、高度、中心位置的随机框
  • 来自数组中随机宽度和高度的四个图像象限中的每一个的随机框dimensions = [4, 16, 32, 64, 128, 256, 512]
  • 没有学习位移的随机锚框,如 Faster RCNN 中使用的锚框
  • 在 Pascal VOC 2012 训练集中找到边界框的 x_min、y_min 和宽度和高度的平均值和(标准差),并从每个这些值的正态分布中随机抽样(并使用 math.floor、math.ceil使它们成为有效框)

我的网络至少优于这些比较 RPN 执行的 ROIS,我通过计算每个图像的每个框的最大 IOU 来衡量,RPNS 为每个图像生成 300 个 ROIS。

我还将在 MS COCO 2014 train_val 数据上训练我的网络。我希望这些信息对某人有所帮助。