Cross-domain Object Detection through Coarse-to-Fine Feature Adaptation-CVPR2020代码阅读

继论文之后,开始读代码

本文主要集中于两个模块的代码

推荐先阅读论文,否则这篇文章理解起来可能有些困难

Cross-domain Object Detection through Coarse-to-Fine Feature Adaptation-CVPR2020论文阅读

代码阅读

ART模块

  • train.py中的train方法中调用了source_forwardtarget_forward,两个方法都有类似如下的代码:

    gt_pooled_feat, gt_labels, rois, cls_prob, bbox_pred, \
    rpn_loss_cls, rpn_loss_box, RCNN_loss_cls, RCNN_loss_bbox, \
    rois_label, domain_scores, weight_map = model(im_data, im_info, gt_boxes, num_boxes)
    
    
    • 注意这里的weight_map是重点,这个参数就是已经上采样之后的注意力图(这里已经把三个域分类器所需要的大小的注意力图进行了review和在维度1上的拼接),这个参数的维度应该是[batchsize, n],n代表了base_feat1的长*宽(传给第一个分类器的特征图大小)+base_feat2的长*宽(传给第二个分类器的特征图大小)+base_feat3的长*宽(传给第三个分类器的特征图大小)
    • 另一个值得关注的参数是domain_scores,这个参数就是三个域分类器的得分结果(二分类),形状已经调成和weight_map类似[batchsize, m],这个m是不是等于n还需要进一步确定
    • 调整上面两个参数形状的过程在lib/model/faster_rcnn/faster_rcnn.py这个文件中
    • ART实际上就是上面两个参数的运算,具体看论文,运算的代码就是下方的代码块:
    domain_labels = Variable(torch.ones_like(domain_scores).float().cuda())
    loss_domain = F.binary_cross_entropy(domain_scores, domain_labels, (1.0 + weight_map.data))
    
    
    • 这个loss_domain就是论文里说的 L A R T L_{ART} LART
  • 接下来我们进入model函数去看那两个返回值到底是怎么来的,model调用的实际上是网络模块类的forward方法,这里用的是VGG16网络,所以我们去找lib/model/faster_rcnn/vgg16.py,结果我们发现这里没有forward函数,不要急,我们会发现这个vgg16实际上是faster_rcnn的子类,既然子类没有,那么forward肯定在父类里,也就是lib/model/faster_rcnn/faster_rcnn.py这个文件中。

  • 但是vgg16那个文件并不是没有的,下面先说一下lib/model/faster_rcnn/vgg16.py中和ART有关的内容(实际都有关,但我们这里捡一些对我们修改有帮助的内容说),它定义了三个域分类器的位置,代码如下:

        self.RCNN_base1 = nn.Sequential(*list(vgg.features._modules.values())[:17]) # 256
        self.RCNN_base2 = nn.Sequential(*list(vgg.features._modules.values())[17:24]) # 512
        self.RCNN_base3 = nn.Sequential(*list(vgg.features._modules.values())[24:-1]) # 512
    
    • 另外还定义了三个域分类器的网络结构,代码如下:

          self.Domain_classifier1 = Discriminator(256)
          self.Domain_classifier2 = Discriminator(512)
          self.Domain_classifier3 = Discriminator(512)
      
    • Discriminator这个类的定义也在vgg16这个文件里,其实很简单的三层fc,代码如下:

      class Discriminator(nn.Module):
      
          def __init__(self, dim):
              super(Discriminator, self).__init__()
              self.conv1 = conv1x1(dim, 256)
              self.in1 = nn.InstanceNorm2d(256)
              self.conv2 = conv1x1(256, 128)
              self.in2 = nn.InstanceNorm2d(128)
              self.conv3 = conv1x1(128, 1)
      
          def forward(self, x):
              x = F.leaky_relu(self.in1(self.conv1(x)))
              x = F.leaky_relu(self.in2(self.conv2(x)))
              x = F.sigmoid(self.conv3(x))
              # x = x.view(x.size(0), -1)
              return x
      
  • 好了,接下来我们去看lib/model/faster_rcnn/faster_rcnn.py中的forward代码,找到vgg16的父类_fasterRCNN中的forward

    • 首先将vgg16文件中定义的三个域分类器位置的特征提取出来:

              base_feat1 = self.RCNN_base1(im_data)
              base_feat2 = self.RCNN_base2(base_feat1)
              base_feat3 = self.RCNN_base3(base_feat2)
      
    • 接下来调用三个分类器分类并对结果进行拼接和调整维度,这里的grad_reverse应该就是对抗思想的GRL了,domain_scores这个值就是我们之前讲的train.py里的domain_scores

              if self.training:
                  ds1 = self.Domain_classifier1(grad_reverse(base_feat1))
                  ds2 = self.Domain_classifier2(grad_reverse(base_feat2))
                  ds3 = self.Domain_classifier3(grad_reverse(base_feat3))
      
                  domain_scores = torch.cat((ds1.view(batch_size, -1), \
                                             ds2.view(batch_size, -1), \
                                             ds3.view(batch_size, -1)), 1)
      
    • 然后调用rpn网络得到attention_map,这里怎么得到这个返回值需要去看rpn的代码(暂留):

              rois, rpn_loss_cls, rpn_loss_bbox, rpn_feat, attention_map = self.RCNN_rpn(base_feat3, im_info, gt_boxes, num_boxes, target=target)
      
    • 对注意力图进行上采样和拼接,调整维度,这里的attention_scores就是train.py里的weight_map了:

              if self.training:
                  at1 = F.upsample(attention_map, size=base_feat1.shape[2:4], mode='bilinear', align_corners=False)
                  at2 = F.upsample(attention_map, size=base_feat2.shape[2:4], mode='bilinear', align_corners=False)
                  at3 = F.upsample(attention_map, size=base_feat3.shape[2:4], mode='bilinear', align_corners=False)
      
                  attention_scores = torch.cat((at1.view(batch_size, -1), \
                                                  at2.view(batch_size, -1), \
                                                  at3.view(batch_size, -1)), 1)
      
  • 这个ART模块差不多就是这些内容了

PSA模块

  • 我们还是从头开始看,实现回顾PSA有什么,两个全局原型(两个域),对应变量名是sgptgp;两个对应的局部原型,一个loss,没了…

  • main.py中在设定完优化器之后,大概107行开始,初始化:

        if args.prior:
            log('[*] Use Prior prototypes!')
            sgp = torch.from_numpy(np.load(args.sp)).type(torch.float32)
            tgp = torch.from_numpy(np.load(args.tp)).type(torch.float32)
        else:
            sgp = torch.zeros(imdb.num_classes - 1, feat_dim).type(torch.float32)
            tgp = torch.zeros(imdb.num_classes - 1, feat_dim).type(torch.float32)
    
    • 训练时没有设定这个参数,所以应该是全0初始,然后在调用train时将这两个值传给train.py
  • train.py在调用函数时将sgp传给source_forward,将tgp传给target_forward,并且函数还会将值返回,确保可以在函数中修改。

    • 两个函数差不多,我以source_forward为例,在source_forward中利用局部原型更新全局原型,这部分主要是计算,计算方法详见论文,论文里说的更清楚一些,代码:

          if step > args.warmup_steps:
              for c in range(1, num_classes):
                  mask = (gt_labels == c)
                  if torch.sum(mask).item() > 0:
                      # 利用局部原型更新全局原型
                      local_prototypes = torch.mean(gt_pooled_feat[mask], 0)
                      alpha = (F.cosine_similarity(sgp[c - 1], local_prototypes, dim=0).item() + 1) / 2.0
                      sgp[c - 1] = (1.0 - alpha) * sgp[c - 1] + alpha * local_prototypes
      
    • 注意这里的限制条件,warmup结束后才开始调用PSA模块,这里论文也有说明

    • 更新之后会把sgp再返回到train函数中

    • warmup之后,train函数中会计算 L A R T L_{ART} LART,也就是sgptgp之间的差异,在代码中变量名为loss_class,代码如下:

          if step >= args.warmup_steps:
              loss_class = args.lam * step / args.max_steps * (sgp - tgp).pow(2).sum(1).mean()
      
    • ART模块的内容较少,就这些了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值