YOLACT++代码分析2——Yolact模型

Yolact模型

在YOLACT++代码分析1——数据增强,我们讲解了数据增强的部分,这一部分主要讲Yolact模型。

首先看到train.py文件

    #image_path训练图片文件夹
    #info_file标签文件夹
    dataset = COCODetection(image_path=cfg.dataset.train_images,
                            info_file=cfg.dataset.train_info,
                            transform=SSDAugmentation(MEANS))

1.继续看看COCODetection的构造函数:

def __init__(self, image_path, info_file, transform=None,
                 target_transform=None,
                 dataset_name='MS COCO', has_gt=True):
        # Do this here because we have too many things named COCO
        from pycocotools.coco import COCO
        
        if target_transform is None:
            target_transform = COCOAnnotationTransform()

        self.root = image_path
        self.coco = COCO(info_file) #将标签文件导入COCO API
        
        #self.coco.imgToAnns 里面包含了标签文件中所有的bbox、category_id、image_id、segmentation
        #的信息,很显然这里这里取出所有训练的图片中的信息:len(self.ids)=159
        self.ids = list(self.coco.imgToAnns.keys())
        if len(self.ids) == 0 or not has_gt:
            self.ids = list(self.coco.imgs.keys())
        
        self.transform = transform 
        #transform是SSDAugmentation的实例对象
        #COCOAnnotationTransform这个类作用:将COCO的标签转换成bbox coords and label index
        #的张量
        self.target_transform = COCOAnnotationTransform()
        
        self.name = dataset_name
        self.has_gt = has_gt

这里要讲下COCOAnnotationTransform(),这个类的__call__方法将coco标签中的bbox和category_id信息存到一个列表中:[xmin, ymin, xmax, ymax, category_id]。这里需要注意coco标签中的bbox信息:[xmin,ymin,w,h].

2.下面回到train.py文件中: yolact_net = Yolact()

class Yolact(nn.Module):
    def __init__(self):
        super().__init__()
        
        1.首先定义ResNet101的backbone
        self.backbone = construct_backbone(cfg.backbone) #默认resnet101

1).yolact默认以ResNet101作为backbone
ResNet
2).不让BN层参与梯度传播

       2.让上面定义的backbone里面的除Conv之外的层不参与梯度计算(学习)
        if cfg.freeze_bn:
            self.freeze_bn()
    def freeze_bn(self, enable=False):
        """ Adapted from https://discuss.pytorch.org/t/how-to-train-with-frozen-batchnorm/12106/8 """
        for module in self.modules():
            if isinstance(module, nn.BatchNorm2d):
                module.train() if enable else module.eval()
                module.weight.requires_grad = enable
                module.bias.requires_grad = enable    
'''           
第一次循环,module为Yolact类 ,那么if肯定不成立
第二次循环,module为ResNetbackbone类,就是上面定义的网络层,if不成立
第三次循环,进入ResNetbackbone中,依次访问里面的层,这次module为Modulelist
第四次循环,module为Modulelist中的第一个Sequential
第五次循环,module为Bottleneck。if不成立
第六次循环,module为conv1,if不成立
第七次循环,module为bn1,if成立:
                    module.weight.requires_grad = enable #enable = False
                    module.bias.requires_grad = enable
'''

上面循环访问顺序参考下面具体模型(backbone:ResNet101的一部分):
backbone:ResNet101的一部分
3).下面我们直接看到这一行代码:Protonet Architecture

self.proto_net, cfg.mask_dim = make_net(in_channels, 
                       cfg.mask_proto_net, include_last_relu=False)

输入参数:
在这里插入图片描述
这个make_net函数就不粘贴出来了,直接看到make_net函数中:

#conf就是上图中cfg.mask_proto_net
net = sum([make_layer(x) for x in conf], [])

第一个循环:x为(256, 3, {‘padding’: 1}),进入make_layer函数:直接执行到193行,剩余的循环与上面原理一样,我们看最终的输出net:
在这里插入图片描述
我们返回到yolact.py文件中:

self.proto_net, cfg.mask_dim = make_net(in_channels, cfg.mask_proto_net, include_last_relu=False)

查看self.proto_net,这个就是论文中 Protonet Architecture:

Sequential(
  (0): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (1): ReLU(inplace=True)
  (2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (3): ReLU(inplace=True)
  (4): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (5): ReLU(inplace=True)
  (6): InterpolateModule()
  (7): ReLU(inplace=True)
  (8): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (9): ReLU(inplace=True)
  (10): Conv2d(256, 32, kernel_size=(1, 1), stride=(1, 1))
)

在这里插入图片描述
看看论文怎么说: prototype生成分支为一张图片预测k个 prototype masks。我们将FCN网络的最后一层通道数改为k ,每一个prototype对应一个通道。并与backbone连接上,以P3 feature map(Fig 2)作为输入。
论文中也解释了为什么将P3输入到Protonet ?因为从更深层次的主干特征中提取protonet会产生 more robust masks和higher resolution prototypes,就导致在小的对象上得到更高质量的Mask和更好的性能。因此,我们使用了FPN网络,因为它最大的那个特征层(在我们的例子中是p3;见图2) 是最深的。我们将它的尺寸提升到输入图像的四分之一,以提高小对象的性能。
最后,作者以ReLU作激活函数,跟在Protonet 后面。

4).下面我们来到yolact.py的492行:FPN

if cfg.fpn is not None:
   # Some hacky rewiring to accomodate the FPN
   self.fpn = FPN([src_channels[i] for i in self.selected_layers])
   self.selected_layers = list(range(len(self.selected_layers) + cfg.fpn.num_downsample))
   src_channels = [cfg.fpn.num_features] * len(self.selected_layers)
'''
FPN的输入参数:
	src_channels:[256, 512, 1024, 2048]
	self.selected_layers:[1, 2, 3]
'''

我们来到了 FPN:

class FPN(ScriptModuleWrapper):
    """
    Implements a general version of the FPN introduced in
    https://arxiv.org/pdf/1612.03144.pdf

    Parameters (in cfg.fpn):
        - num_features (int): The number of output features in the fpn layers.
        - interpolation_mode (str): The mode to pass to F.interpolate.
        - num_downsample (int): The number of downsampled layers to add onto the selected layers.
                                These extra layers are downsampled from the last selected layer.

    Args:
        - in_channels (list): For each conv layer you supply in the forward pass,
                              how many features will it have?
    """
    __constants__ = ['interpolation_mode', 'num_downsample', 'use_conv_downsample', 'relu_pred_layers',
                     'lat_layers', 'pred_layers', 'downsample_layers', 'relu_downsample_layers']

    def __init__(self, in_channels):
        super().__init__()

        self.lat_layers  = nn.ModuleList([
            nn.Conv2d(x, cfg.fpn.num_features, kernel_size=1)
            for x in reversed(in_channels)
        ])

        # This is here for backwards compatability
        padding = 1 if cfg.fpn.pad else 0
        self.pred_layers = nn.ModuleList([
            nn.Conv2d(cfg.fpn.num_features, cfg.fpn.num_features, kernel_size=3, padding=padding)
            for _ in in_channels
        ])

        if cfg.fpn.use_conv_downsample:
            self.downsample_layers = nn.ModuleList([
                nn.Conv2d(cfg.fpn.num_features, cfg.fpn.num_features, kernel_size=3, padding=1, stride=2)
                for _ in range(cfg.fpn.num_downsample)
            ])
        
        self.interpolation_mode     = cfg.fpn.interpolation_mode
        self.num_downsample         = cfg.fpn.num_downsample
        self.use_conv_downsample    = cfg.fpn.use_conv_downsample
        self.relu_downsample_layers = cfg.fpn.relu_downsample_layers
        self.relu_pred_layers       = cfg.fpn.relu_pred_layers

相信大家都看过FPN的论文:
在这里插入图片描述
当然这里不是一一对应的,yolact的结构:
在这里插入图片描述
P7,P6,P5没有特征融合,直接输入Preditction Head中,然后P4、P3均融合了上一层的特征和对应的backbone的特征。

下面就好理解FPN里面的操作,看到yolact.py的341行:注意这里我们仅仅只是执行到FPN的__init__方法,并没有执行forward方法,所下面都是定义FPN要用到网络层的实例属性:

self.lat_layers  = nn.ModuleList([
            nn.Conv2d(x, cfg.fpn.num_features, kernel_size=1)
            for x in reversed(in_channels) #倒序一个列表,并对其生成迭代器
        ])

最终self.lat_layers输出:
在这里插入图片描述
继续看到348行,self.pred_layers:
在这里插入图片描述
继续看到353行,self.downsample_layers:
在这里插入图片描述
self.downsample_layers用于两次下采样,很显然是针对P5使用的,生成P6,再对P6下采样,生成P7.

self.lat_layers、self.pred_layers、self.downsample_layers具体怎么用来搭建FPN网络,大家看这个图片,就能明白:
在这里插入图片描述
5).下面我们来到yolact.py的506行:定义PredictionHead,执行完5次循环:

 pred = PredictionModule(src_channels[layer_idx], src_channels[layer_idx],
                            aspect_ratios = cfg.backbone.pred_aspect_ratios[idx],
                            scales        = cfg.backbone.pred_scales[idx],
                            parent        = parent,
                            index         = idx)

具体的PredictionModule类就不讲解了,比较简单,主要是__init__方法中这三行代码:

self.bbox_layer = nn.Conv2d(out_channels, self.num_priors * 4,                **cfg.head_layer_params)
self.conf_layer = nn.Conv2d(out_channels, self.num_priors * self.num_classes, **cfg.head_layer_params)
self.mask_layer = nn.Conv2d(out_channels, self.num_priors * self.mask_dim,    **cfg.head_layer_params)

输出pred:

ModuleList(
  (0): PredictionModule(
    (upfeature): Sequential(
      (0): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (1): ReLU(inplace=True)
    )
    (bbox_layer): Conv2d(256, 12, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (conf_layer): Conv2d(256, 15, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (mask_layer): Conv2d(256, 96, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  )
  (1): PredictionModule()
  (2): PredictionModule()
  (3): PredictionModule()
  (4): PredictionModule()
)
bbox_layer :预测anchor的偏移量:gt与匹配到的anchor之间的中心点offset,以及它们宽度和高度比。
in_channels = 256
out_channels =  12 (每个像素点预测3个anchor)

conf_layer :预测anchor的置信度
out_channels= 12 ,因为每个像素点生成3个anchor,而我们总共要检测4类
所以,输出为12

mask_layer:预测mask coefficients
out_channels = 3*32,
每个anchor预测k(32)个mask coefficients,这是因为一张图片生成32个prototype masks。

看看论文怎么说:
在这里插入图片描述
注意代码中的:

(bbox_layer): Conv2d(256, 12, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(conf_layer): Conv2d(256, 15, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(mask_layer): Conv2d(256, 96, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))

是上图Class 、Box、Mask的最后一个卷积层。

继续看到yoloact.py 557行:

self.semantic_seg_conv = nn.Conv2d(src_channels[0], cfg.num_classes-1, kernel_size=1)

到这里应该就看完了train.py文件中:yolact_net = Yolact() 。再次声明这里仅仅是定义了一个对象(即执行__init__方法),并没有真正的输入图片让其训练。可以看到上面的很多class里面都有一个forward函数,模型之间的连接就在哪里,后面真正输入图片至网络中时,即:前向传播时会调用各个类中forward函数,实现前向传播。后面博客会写到:YOLACT++代码分析3——前向传播

Reference
YOLACT++ Better Real-time Instance Segmentation
YOLACT++源码

  • 18
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
对于训练自己的数据集,你可以按照以下步骤进行操作: 1. 数据集准备:首先,你需要准备好自己的数据集。确保数据集中包含对应的图像和相应的标注信息,这些标注信息可以是物体的边界框、类别标签等。同时,确保数据集中的图像样本多样化且覆盖你感兴趣的场景。 2. 数据预处理:在训练之前,你可能需要对数据集进行一些预处理操作,以确保数据的一致性和质量。这可能包括图像尺寸调整、数据增强(如随机裁剪、旋转、缩放等)等。 3. 模型配置:选择适合你任务的YOLO系列模型(如YOLOv3、YOLOv4等),并进行相应的配置。配置中包括网络结构、超参数、训练批次大小、学习率等。 4. 模型训练:使用准备好的数据集和配置好的模型,进行模型训练。在训练过程中,你可以使用GPU来加速训练过程,并通过监控训练损失和验证指标来评估模型的性能。 5. 模型评估和调优:在模型训练完成后,你可以使用测试集来评估模型的性能。根据评估结果,你可以进行模型的调优,如调整超参数、增加训练数据量等。 6. 模型应用:最后,你可以将训练好的模型应用于你感兴趣的任务中,如目标检测、实时物体跟踪等。 需要注意的是,YOLO系列模型是相对复杂的深度学习模型,训练过程可能需要较长时间和大量的计算资源。同时,合理的数据集规模和质量对于训练效果也具有重要影响。因此,在进行训练之前,请确保你有足够的计算资源和高质量的数据集。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值