• mmdetection - 训练数据加载流程之pipeline


    一、数据集处理流程

    首先通过官方文档流程图了解据预处理和数据增强整个流程:
    mmdetection数据预处理和数据增强都在pipeline过程中实现,下面这张图来自mmdetection官方文档,因为版本不同具体内容会稍有差别:
    mmdetection官方文档

    1、LoadImageFromFile

    img:当前图像
    img_shape:resize、crop后的图片大小
    ori_shape:原始图片大小

    2、LoadAnnotations

    gt_bboxes:数据集标注中给出的目标物体对应的边界框
    gt_bboxes_ignore:数据集标注中给出的需要忽略的目标物体对应的边界框
    gt_labels:数据集标注中给出的目标物体对应的label

    3、Resize

    更多Resize细节参考下面链接
    https://zhuanlan.zhihu.com/p/381117525
    img_scale=(512, 512), keep_ratio=True
    img_scale缩放之后图像的最终尺度,keep_ratio保持宽高比(scale、scale_factor表示的缩放比例系数)
    注意:当 keep_ratio 设置为 False 时候,输入的 img_scale 含义是 w,h, 输出尺度就是预设的输入尺度。当 keep_ratio 设置为 True 时候,输入的 img_scale 不区分 h,w,而是 min 和 max,输出会在保证宽高比不变的情况下,最大程度接近 [min(img_scale), max(img_scale)] 范围。

    4、RandomFlip

    flip_ratio:反转概率。direction:‘horizontal’, ‘vertical’, ‘diagonal’. Default: ‘horizontal’:翻转方向

    5、Normalize

    mean (sequence): Mean values of 3 channels.:均值
    std (sequence): Std values of 3 channels.:方差
    to_rgb (bool): Whether to convert the image from BGR to RGB,default is true.:是否BGR 转 RGB

    6、Pad

    size (tuple, optional):填充图像到指定大小
    size_divisor (int, optional):填充图像到可以整除size_divisor
    pad_to_square (bool): Whether to pad the image into a square.填充图像到正方形

    填充图像到指定大小,填充图像到尺寸可以除开

    7、DefaultFormatBundle

    定义在formating.py中,这一步就是把img、bboxes、labels转换为tensor,再转换为DataContainer。

    8、Collect

    定义在formating.py中,把一些标注信息插入到results[‘img_metas’]

    二、代码中pipeline调用过程

    然后,介绍pipeline处理数据过程中代码的一些细节问题:
    1、数据实例化的过程中,数据父类CustomDataset初始化函数中会实例化Compose去构建pipeline

    @DATASETS.register_module()
    class CustomDataset(Dataset):
        def __init__():
            self.pipeline = Compose(pipeline)
    @PIPELINES.register_module()
    class Compose:
        def __init__(self, transforms):
               transform = build_from_cfg(transform, PIPELINES)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2、按照pipeline去处理数据

    #从配置文件看出train_pipeline 包含LoadImageFromFile、LoadAnnotations、Resize、RandomFlip、Normalize、
    #Pad、DefaultFormatBundle、Collect九大步骤
    #Compose 的__init__函数把九个步骤添加到  self.transforms中
    #__call__函数 按照处理流程对数据进行变换
    @PIPELINES.register_module()
    
    class Compose:
        def __init__(self, transforms):
            assert isinstance(transforms, collections.abc.Sequence)
            self.transforms = []
            for transform in transforms:
                if isinstance(transform, dict):
                    transform = build_from_cfg(transform, PIPELINES)
                    self.transforms.append(transform)
                elif callable(transform):
                    self.transforms.append(transform)
                else:
                    raise TypeError('transform must be callable or a dict')
    	def __call__(self, data):
    			for t in self.transforms:
    			     data = t(data)
    			     if data is None:
    			         return None
    		 return data
    
    #在epoch_based_runner.py
    def run(self, data_loaders, workflow, max_epochs=None, **kwargs):
    	    epoch_runner(data_loaders[i], **kwargs)
    def train(self, data_loader, **kwargs):
    		for i, data_batch in enumerate(self.data_loader):#这里调用的 CustomDataset类的__getitem__方法
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    self.pipeline被调用的位置如下

        def prepare_train_img(self, idx):
    
            img_info = self.data_infos[idx]
            ann_info = self.get_ann_info(idx)
            results = dict(img_info=img_info, ann_info=ann_info)
            if self.proposals is not None:
                results['proposals'] = self.proposals[idx]
            self.pre_pipeline(results)
            return self.pipeline(results)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    三、python中的 __call__和 __getitem__方法

    3.1 __call__方法

    在Python中,函数其实是一个对象,一个类实例也可以变成一个可调用对象,只需要实现一个特殊方法__call__().Compose类实现了方法__call__,self.pipeline(results)实际上执行的是Compose类方法__call_

    @DATASETS.register_module()
    class CustomDataset(Dataset):
        def __init__(self):
            self.pipeline = Compose(pipeline)
        def prepare_train_img(self, idx):
    #什么时候调用的 __call__
            return self.pipeline(results)#这里调用的 __call__方法
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.1 __getitem__方法

    python 中的 __getitem__方法,常见的两种写法
    形式一: getitem(self, index) 一般用来迭代序列(常见序列如:列表、元组、字符串),或者求序列中的索引为 index 处的值。
    形式二: getitem(self, key) 一般用来迭代映射(常见映射如:字典),或者求映射中的键为 key 出的值。

    
    @DATASETS.register_module()
    class CustomDataset(Dataset):
        def __getitem__(self, idx):
            if self.test_mode:
                return self.prepare_test_img(idx)
            while True:
                data = self.prepare_train_img(idx)
                if data is None:
                    idx = self._rand_another(idx)
                    continue
                return data
     #下面伪代码
    cc=CustomDataset()
    b=cc[1]# 会自动调用 __getitem__方法
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    最后区分一下下面的概念:
    Epoch 使用训练集的全部数据对模型进行一次完整的训练,被称为“一代训练”或者“一轮训练”
    Batch 使用训练集中的一小部分样本对模型权重进行一次反向传播的参数更新,这一小部分样本被称为“一批数据”
    Iteration 使用一个Batch数据对模型进行一次参数更新的过程,被称之为“一次训练”
    (1)batchsize:批大小。在深度学习中,一般采用SGD训练,即每次训练在训练集中取batchsize个样本训练;
    (2)iteration:1个iteration等于使用batchsize个样本训练一次;
    (3)epoch:1个epoch等于使用训练集中的全部样本训练一次,通俗的讲epoch的值就是整个数据集被轮几次。

  • 相关阅读:
    C#:实现图像相似度算法​(附完整源码)
    spring:实现初始化动态bean|获取对象型数组配置文件
    记录在使用OpenCVSharp在netcore3.1框架下做视觉处理遇到的坑及解决过程
    Python基础笔记持续记录
    2022-08-08 mysql慢SQL-Q18-10GB数据量-mysql/innodb测试
    Python列表详解
    使用ASM修改组件化 ARouter
    机器学习在工业机器人领域有哪些应用?
    【Python】Python常用的日期时间函数
    【高效数据结构——位图bitmap】
  • 原文地址:https://blog.csdn.net/m0_37737957/article/details/127669670