MMDetection之pipline详解


前言

上篇介绍了mmdet如何构建dataset的大体思路。本篇则更详细的介绍mmdet如何读取数据的。

1、CustomDataset类实例化

 在mmdet/datasets/custom.py中定义了类CustomDataset,大多数据集的Dataset的实现均需继承该类,比如coco数据集。不同Dataset类初始化过程大同小异,故本文以父类的实例化为出发点,首先介绍CustomDataset类的初始化配置文件初始化过程

# config配置文件
    train=dict(
        type=dataset_type,
        imgset=data_root + 'ImageSets/trainval.txt',
        classwise=False,
        ann_file=data_root + 'FullDataSet/Annotations/',
        img_prefix=data_root + 'FullDataSet/AllImages/',
        pipeline=train_pipeline),
@DATASETS.register_module()
class CustomDataset(Dataset):
    CLASSES = None
    def __init__(self,
                 ann_file,                 # anno的路径
                 pipeline,                 # 是个字典{'loadImg','loadAnno'}
                 classes=None,            
                 data_root=None,           # 数据集根目录: 比如/home/dataset/coco
                 img_prefix='',            # 图像的目录  : /images
                 seg_prefix=None,
                 proposal_file=None,
                 test_mode=False,
                 filter_empty_gt=True):    # 过滤空的gt
        # processing pipeline
        self.pipeline = Compose(pipeline)  # 数据处理管道

 在实例化Dataset后,当遍历读取数据集时,会调用CustomDataset的getitem方法,故这里贴下,这里额外注意下results这个字典:

    def __getitem__(self, idx):
        if self.test_mode:
            return self.prepare_test_img(idx)
        while True:
            data = self.prepare_train_img(idx)  # 以train过程为例
            if data is None:
                idx = self._rand_another(idx)
                continue
            return data
            
    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) # result添加了两个信息
        if self.proposals is not None:
            results['proposals'] = self.proposals[idx]
        self.pre_pipeline(results)                           # 又向results里面传入了新的键
        return self.pipeline(results)                        # 将results送到管道
        
    def pre_pipeline(self, results):
        """Prepare results dict for pipeline"""
        results['img_prefix'] = self.img_prefix              # 添加了图像前缀等字段
        results['seg_prefix'] = self.seg_prefix
        results['proposal_file'] = self.proposal_file
        results['bbox_fields'] = []
        results['mask_fields'] = []
        results['seg_fields'] = []

  这里贴下results的一个样例。
在这里插入图片描述

2、Pipline

  在合成字典results后,CustomDaaset内部getitem方法通过self.piplien(results)送到管道。也就是官网中经常看到的那张图:
在这里插入图片描述
 其中绿色字段表示往result里面新添加的key,橙色字段表示当前pipline可能改变key的value。以Resize为例(绿色注释写的很清楚了):

    def __call__(self, results):
        """Call function to resize images, bounding boxes, masks, semantic
        segmentation map.

        Args:
            results (dict): Result dict from loading pipeline.

        Returns:
            dict: Resized results, 'img_shape', 'pad_shape', 'scale_factor',
                'keep_ratio' keys are added into result dict.
        """

        if 'scale' not in results:
            if 'scale_factor' in results:
                img_shape = results['img'].shape[:2]
                scale_factor = results['scale_factor']  # 往result添加scale_factor字段
                assert isinstance(scale_factor, float)
                results['scale'] = tuple(               # 添加scale字段
                    [int(x * scale_factor) for x in img_shape][::-1])
            else:
                self._random_scale(results)
        else:
            assert 'scale_factor' not in results, (
                "scale and scale_factor cannot be both set.")

        self._resize_img(results)
        self._resize_bboxes(results)
        self._resize_masks(results)
        self._resize_seg(results)
        return results

  由于前面添加的key有可能在后面用到,因此在构建自己的 Pipeline 时候一定要仔细检查你修改或者新增的字典 key 和 value,因为一旦你错误地覆盖或者修改原先字典里面的内容,代码也可能不会报错,如果出现 bug,则比较难排查。

3 、DefaultFormatBundle

&emspl我在看到这个pipline时比较蒙,因此,我单独拿出来分析下,先贴下代码:

from mmcv.parallel import DataContainer as DC    # DataContainer暂时理解为一个容器即可

@PIPELINES.register_module()
class DefaultFormatBundle(object):
    """
	# 就是将result中下面的key按1 --> 2的顺序打包了
    - img: (1)transpose, (2)to tensor, (3)to DataContainer (stack=True)
    - proposals: (1)to tensor, (2)to DataContainer
    - gt_bboxes: (1)to tensor, (2)to DataContainer
    - gt_bboxes_ignore: (1)to tensor, (2)to DataContainer
    - gt_labels: (1)to tensor, (2)to DataContainer
    - gt_masks: (1)to tensor, (2)to DataContainer (cpu_only=True)
    - gt_semantic_seg: (1)unsqueeze dim-0 (2)to tensor,
                       (3)to DataContainer (stack=True)
    """

    def __call__(self, results):
        """Call function to transform and format common fields in results.

        Args:
            results (dict): Result dict contains the data to convert.

        Returns:
            dict: The result dict contains the data that is formatted with
                default bundle.
        """

        if 'img' in results:
            img = results['img']
            # add default meta keys
            results = self._add_default_meta_keys(results)
            if len(img.shape) < 3:
                img = np.expand_dims(img, -1)
            img = np.ascontiguousarray(img.transpose(2, 0, 1))
            results['img'] = DC(to_tensor(img), stack=True)      # img转成tensor在放入DC
        for key in ['proposals', 'gt_bboxes', 'gt_bboxes_ignore', 'gt_labels']:
            if key not in results:
                continue
            results[key] = DC(to_tensor(results[key]))           # 转成tensor在放到DC
        if 'gt_masks' in results:
            results['gt_masks'] = DC(results['gt_masks'], cpu_only=True)
        if 'gt_semantic_seg' in results:
            results['gt_semantic_seg'] = DC(
                to_tensor(results['gt_semantic_seg'][None, ...]), stack=True)
        return results
	
	# 添加一些"pad_shape,scale_factor的默认字段避免无Resize的pipline导致的缺少key的问题"
    def _add_default_meta_keys(self, results):
        """Add default meta keys.
        """
        img = results['img']
        results.setdefault('pad_shape', img.shape)
        results.setdefault('scale_factor', 1.0)
        num_channels = 1 if len(img.shape) < 3 else img.shape[2]
        results.setdefault(
            'img_norm_cfg',
            dict(
                mean=np.zeros(num_channels, dtype=np.float32),
                std=np.ones(num_channels, dtype=np.float32),
                to_rgb=False))
        return results

  该类简单一句话,就是将result中的某些key:比如img,gt_bbox,gt_labels转成tensor在放到一个DataContainer的容器里面,哈哈那就在介绍下DataContainer。

3.1 DataContainer类

  实现在mmcv/parallel/data_container.py中, 这块请看注释。

class DataContainer:

	原文翻译:
		pytorch中collate打包函数是将一个batch数据在某个维度上进行堆叠,这种方式有以下缺点:
		1、所有tensor必须有相同尺寸
		2、类型限制比较死,仅有tensor或者numpy。
	为了更能灵活处理打包tensor,设计了DataContainer,但pytorch无法打包运算DataContainer对象。
	故又设计了MMDataPareel来处理运算DC中内容。

    def __init__(self,
                 data,
                 stack=False,
                 padding_value=0,
                 cpu_only=False,
                 pad_dims=2):
        self._data = data
        self._cpu_only = cpu_only
        self._stack = stack
        self._padding_value = padding_value
        assert pad_dims in [None, 1, 2, 3]
        self._pad_dims = pad_dims

4、Collate

@PIPELINES.register_module()
class Collect(object):

    def __init__(self,
                 keys,
                 meta_keys=('filename', 'ori_filename', 'ori_shape',
                            'img_shape', 'pad_shape', 'scale_factor', 'flip',
                            'flip_direction', 'img_norm_cfg')):
        self.keys = keys
        self.meta_keys = meta_keys

    def __call__(self, results):
        """Call function to collect keys in results. The keys in ``meta_keys``
        will be converted to :obj:mmcv.DataContainer.

        Args:
            results (dict): Result dict contains the data to collect.

        Returns:
            dict: The result dict contains the following keys
                - keys in``self.keys``
                - ``img_metas``
        """

        data = {}
        img_meta = {}
        for key in self.meta_keys:
            img_meta[key] = results[key]
        data['img_metas'] = DC(img_meta, cpu_only=True)  # 添加了一个img_meta的key。
        for key in self.keys:
            data[key] = results[key]
        return data

  就是添加了一个新的key: ‘img_meta’。

总结

 最后在复习下这张图即可。
在这里插入图片描述

### 扩散模型管道概述 扩散模型作为一种强大的生成模型,在机器学习领域得到了广泛应用。这类模型主要依赖于两个核心过程:前向扩散过程和反向生成过程。 #### 前向扩散过程 在前向过程中,原始数据逐渐被高斯噪声污染直至变得不可辨认。这一阶段可以视为将复杂的数据分布逐步转换成简单的高斯分布的过程[^1]。具体来说: - 初始状态是从真实数据集采样的样本。 - 随着时间步数增加,每一步都会加入一定量的随机噪声。 - 终止条件通常是当加噪程度达到预设阈值时停止操作。 此过程可以用一系列马尔可夫链来表示,其中每个时刻的状态仅取决于上一时刻的状态以及当前添加的噪声水平[^3]。 ```python import numpy as np def forward_diffusion(x_0, T=1000): beta_t = np.linspace(0.0001, 0.02, T) # Noise schedule x_t = x_0.copy() for t in range(T): noise = np.random.normal(size=x_0.shape) sqrt_alpha_hat_t = np.sqrt(1 - sum(beta_t[:t])) x_t = sqrt_alpha_hat_t * x_t + np.sqrt(1 - sqrt_alpha_hat_t ** 2) * noise return x_t ``` #### 反向生成过程 相比之下,反向过程旨在从未知(通常为标准正态分布)恢复出接近真实的样本。这是通过训练神经网络预测给定含噪输入下的去噪方向实现的。该网络接受带有所谓“时间戳”的含噪图像作为输入,并尝试估计对应的纯净信号[^4]。 ```python from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Conv2DTranspose def build_reverse_model(input_shape=(28, 28, 1), output_channels=1): model = Sequential([ Conv2DTranspose(filters=64, kernel_size=3, strides=2, padding='same', activation='relu', input_shape=input_shape), Conv2DTranspose(filters=output_channels, kernel_size=3, strides=1, padding='same'), ]) return model ``` #### 参数共享机制 对于涉及多种模态的任务,如文本到图像合成或多感官融合场景下,采用参数共享策略能够有效降低计算成本并提高泛化能力。例如,在构建多功能扩散模型(Versatile Diffusion)时,默认配置采用了四流架构;然而,得益于内部组件间的广泛重用,实际所需资源远低于独立部署多个单一功能模块的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值