[杂记]mmdetection3.x中的数据流与基本流程详解(数据集读取, 数据增强, 训练)
之前跑了一下mmdetection 3.x自带的一些算法, 但是具体的代码细节总是看了就忘, 所以想做一些笔记, 方便初学者参考. 其实比较不能忍的是, 官网的文档还是空的…
这次想写其中的数据流是如何运作的, 包括从读取数据集的样本与真值, 到数据增强, 再到模型的forward当中.
0. MMDetection整体组成部分
让我们首先回顾一下C++的标准模板库(STL)是怎样设计的. STL的三个核心组件是容器, 算法与迭代器. 容器, 例如vector, queue等等, 他们是负责存储数据的, 算法是负责进行一些操作, 例如排序, 查找等等. 而迭代器是容器与算法之间的桥梁, 也就是算法可以通过迭代器去访问容器, 使得算法可以独立于容器的类型进行操作. 三个部分相辅相成, 就达到了泛型编程的理念.
再让我们回顾一下一套深度学习的代码包含什么部分. 从大的方面来说, 需要有数据的读取与增强(DataLoader), 模型的定义, 损失函数的计算, 负责梯度传播的优化器, 在验证(测试)集上的评估等. 同理, MMDetection也是按照这种方式来的, 并且每个部分接口相通, 就可以实现更广义的模型定义和训练方式.
在mmengine/registry/__init__.py
中, 我们可以看到, MMEngine(或者说MMDetection)总体有这些类型的模块:
from .root import (DATA_SAMPLERS, DATASETS, EVALUATOR, FUNCTIONS, HOOKS,INFERENCERS, LOG_PROCESSORS, LOOPS, METRICS, MODEL_WRAPPERS,MODELS, OPTIM_WRAPPER_CONSTRUCTORS, OPTIM_WRAPPERS,OPTIMIZERS, PARAM_SCHEDULERS, RUNNER_CONSTRUCTORS, RUNNERS,TASK_UTILS, TRANSFORMS, VISBACKENDS, VISUALIZERS,WEIGHT_INITIALIZERS)
那么以上这么多模块可以分成几类, 分别负责什么呢? 按照我个人的理解, MMDetection的整体组成部分可以表示为下图:
为了节省空间, 优化器相关并未画出
1. 认识config文件
mmdetection设计的核心思想是通过字典来配置整个的训练过程和模型定义, 这些字典放在一个.py的config文件中. 一般来说,config文件最重要的就是数据加载(train_dataloader, val_dataloader和test_dataloader
), 模型定义(model
)和训练与测试过程(train_pipeline, test_pipeline
). 除此之外, 还有一些训练, 测试配置(train_cfg, test_cfg
)等等. 具体config的例子可以参照官网Learn about configs.
需要注意的是, mmdetection中字典定义class的方式, 往往是键type
表示类的名字, 之后的其他键都是类初始化需要的参数. 例如, 如果我想自定义一个模型, 叫做MyModel
, 定义在当前目录下的./models/my_model.py
中, 定义方式如下:
from mmdet.registry import MODELS # 自定义模型, 需要在模型库中"注册", 初始化时才能找到定义
from mmdet.models.mot.base import BaseMOTModel # 一个模型基类@MODELS.register_module() # 装饰器 在模型库中"注册"
class MyModel(BaseMOTModel):def __init__(self, arg1=..., arg2=..., arg3=...):...def loss(self, inputs, data_samples): # 前向传播, inputs是输入tensor, data_samples是包含标签的列表...
如果按上述方式定义了模型, 那么在我们的配置文件中, 就是这个样子:
# 必须将自定义类的py文件导入 这样可以自动register自定义模型 否则模型初始化时找不到custom_imports = dict(imports=['models.my_model'],allow_failed_imports=False)# 现在就可以愉快的传参了
models=dict(type='MyModel', arg1=1, arg2=[16, 128], arg3=dict(channel=256), ...
)
同样, 我们可以自定义DataLoader, Loss, 等等.
此外, dict是可以嵌套的, 例如mmdetection将检测模型分成了backbone, neck和head
三部分, 那么如果我们又自定义了一个Head, 叫MyHead
:
from mmdet.registry import MODELS # 自定义模型, 需要在模型库中"注册", 初始化时才能找到定义
from mmengine.model import BaseModule # 一个模型基类@MODELS.register_module() # 装饰器 在模型库中"注册"
class MyHead(BaseModule):def __init__(self, arg4=...):...
这样, 如果MyModel
的前向传播过程中需要一个head, 则代码大致是这个样子:
from mmdet.registry import MODELS # 自定义模型, 需要在模型库中"注册", 初始化时才能找到定义
from mmdet.models.mot.base import BaseMOTModel # 一个模型基类@MODELS.register_module() # 装饰器 在模型库中"注册"
class MyModel(BaseMOTModel):def __init__(self, arg1=..., arg2=..., arg3=...,head=...):self.head = MODELS.build(head) # 建立Head的模型, 类型是nn.Module...def loss(self, inputs, data_samples): # 前向传播, inputs是输入tensor, data_samples是包含标签的列表... # 一些其他过程ret = self.head(inputs) # forward... # 后处理
配置文件中对应更改为:
如果按上述方式定义了模型, 那么在我们的配置文件中, 就是这个样子:
custom_imports = dict(imports=['models.my_model', '自定义HEAD所在的py文件'],allow_failed_imports=False)models=dict(type='MyModel', arg1=1, arg2=[16, 128], arg3=dict(channel=256), head=dict( # 定义headtype='MyHead',arg4=256,...)...
)
篇幅所限, 自定义损失函数, 数据增强之类的就不一一列举了.
2. 数据流
我们接下来以检测与跟踪任务为例, 看看数据到底是如何被读入的. 我们以训练过程说明.
在训练过程中, 我们会初始化一个RUNNER类, 其读入我们的config文件并依次完成各种(模型, 数据加载, 优化器, 钩子等等)的初始化. 我们以官方提供的train.py
为例:
runner = Runner.from_cfg(cfg)
from_cfg()
是一个类方法(classmethod), 在其中我们实例化了Runner
类.
随后, 我们调用Runner
的train()
方法进行训练. 首先, 我们实例化训练循环:
self._train_loop = self.build_train_loop(self._train_loop) # type: ignore
训练循环就属于LOOP类型.
在这里, 我们以最常用的EpochBasedTrainLoop
为例. 在EpochBasedTrainLoop
的初始化函数中, 根据config文件中的train_dataloader
字典实例化出torch
的DataLoader
类():
data_loader = DataLoader(dataset=dataset,sampler=sampler if batch_sampler is None else None,batch_sampler=batch_sampler,collate_fn=collate_fn,worker_init_fn=init_fn,**dataloader_cfg)return data_loader
当然, 我们知道torch的DataLoader类在调用的时候, 会调用到dataset(类别是torch.utils.data.Dataset
)的__getitem__
方法. 因此, 我们从__getitem__
入手来探索数据流.
在MMDetection的设计中, 数据集的类都是继承于MMengine中的BaseDataset
, 其中的__getitem__
是这样写的:
def __getitem__(self, idx: int) -> dict:if not self._fully_initialized:print_log('Please call `full_init()` method manually to accelerate ''the speed.',logger='current',level=logging.WARNING)self.full_init()if self.test_mode:data = self.prepare_data(idx)if data is None:raise Exception('Test time pipline should not get `None` ''data_sample')return datafor _ in range(self.max_refetch + 1):data = self.prepare_data(idx)# Broken images or random augmentations may cause the returned data# to be Noneif data is None:idx = self._rand_another()continuereturn dataraise Exception(f'Cannot find valid image after {self.max_refetch}! ''Please check your image path and pipeline')
我们可以看到, 在__getitem__
中最核心的是self.prepare_data(idx)
. 按照这种思路一级一级向上查找, 我们就可以总结出如下图的数据读取流程:
其中, 数据增强pipeline是一系列类型为TRANSFORMS
类的列表, 再每经过一次数据增强时, 字典都会被更新.
我们以较为常用的随机便宜(RandomShift)来说, 其是这样定义的:
@TRANSFORMS.register_module()
class RandomShift(BaseTransform):def __init__(self,...@autocast_box_type()def transform(self, results: dict) -> dict: # transform方法, 更新字典, 图像与对应的边界框等都需要被更新"""Transform function to random shift images, bounding boxes.Args:results (dict): Result dict from loading pipeline.Returns:dict: Shift results."""if self._random_prob() < self.prob:img_shape = results['img'].shape[:2]random_shift_x = random.randint(-self.max_shift_px,self.max_shift_px)random_shift_y = random.randint(-self.max_shift_px,self.max_shift_px)new_x = max(0, random_shift_x)ori_x = max(0, -random_shift_x)new_y = max(0, random_shift_y)ori_y = max(0, -random_shift_y)# TODO: support mask and semantic segmentation maps.bboxes = results['gt_bboxes'].clone()bboxes.translate_([random_shift_x, random_shift_y])# clip borderbboxes.clip_(img_shape)# remove invalid bboxesvalid_inds = (bboxes.widths > self.filter_thr_px).numpy() & (bboxes.heights > self.filter_thr_px).numpy()# If the shift does not contain any gt-bbox area, skip this# image.if not valid_inds.any():return resultsbboxes = bboxes[valid_inds]results['gt_bboxes'] = bboxesresults['gt_bboxes_labels'] = results['gt_bboxes_labels'][valid_inds]if results.get('gt_ignore_flags', None) is not None:results['gt_ignore_flags'] = \results['gt_ignore_flags'][valid_inds]# shift imgimg = results['img']new_img = np.zeros_like(img)img_h, img_w = img.shape[:2]new_h = img_h - np.abs(random_shift_y)new_w = img_w - np.abs(random_shift_x)new_img[new_y:new_y + new_h, new_x:new_x + new_w] \= img[ori_y:ori_y + new_h, ori_x:ori_x + new_w]results['img'] = new_imgreturn results
需要注意的是, 经过pipeline后, 字典最终会被更新成如下形式:
dict = {'inputs': torch.Tensor, 'data_samples': DetDataSample或TrackDataSample等}
其中'inputs'
键对应的值就是转换为tensor的图片, 而'data_samples'
键对应的值是表示样本的类, 在检测任务中, 是DetDataSample
, 跟踪任务中, 是TrackDataSample
. DetDataSample
类有许多成员, 包括该样本(图片)的目标的边界框真值, 分割真值等:
class DetDataSample(BaseDataElement):"""A data structure interface of MMDetection. They are used as interfacesbetween different components.The attributes in ``DetDataSample`` are divided into several parts:- ``proposals``(InstanceData): Region proposals used in two-stagedetectors.- ``gt_instances``(InstanceData): Ground truth of instance annotations.- ``pred_instances``(InstanceData): Instances of detection predictions.- ``pred_track_instances``(InstanceData): Instances of trackingpredictions.- ``ignored_instances``(InstanceData): Instances to be ignored duringtraining/testing.- ``gt_panoptic_seg``(PixelData): Ground truth of panopticsegmentation.- ``pred_panoptic_seg``(PixelData): Prediction of panopticsegmentation.- ``gt_sem_seg``(PixelData): Ground truth of semantic segmentation.- ``pred_sem_seg``(PixelData): Prediction of semantic segmentation.
以上过程可以借用MMEngine文档里的一个图说明:
最终, 模型的forward, loss, predict等方法都是接收inputs: torch.Tensor
与data_samples
作为输入, 例如:
def loss(self, inputs: Tensor, data_samples: TrackSampleList,**kwargs) -> Union[dict, tuple]:
相关文章:

[杂记]mmdetection3.x中的数据流与基本流程详解(数据集读取, 数据增强, 训练)
之前跑了一下mmdetection 3.x自带的一些算法, 但是具体的代码细节总是看了就忘, 所以想做一些笔记, 方便初学者参考. 其实比较不能忍的是, 官网的文档还是空的… 这次想写其中的数据流是如何运作的, 包括从读取数据集的样本与真值, 到数据增强, 再到模型的forward当中. 0. MMDe…...

阿里云香港轻量应用服务器怎么样,建站速度快吗?
阿里云香港服务器中国香港数据中心网络线路类型BGP多线精品,中国电信CN2高速网络高质量、大规格BGP带宽,运营商精品公网直连中国内地,时延更低,优化海外回中国内地流量的公网线路,可以提高国际业务访问质量。阿里云服务…...

事务及在SpringBoot项目中使用的两种方式
1.事务简介 事务(transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。 事物的四大特性: 原子性(Atomicity)…...

stm32--笔记
一、引脚与变量 二、STM32时钟 [STM32-时钟系统详解_stm32时钟_KevinFlyn的博客-CSDN博客] 三、定时器中断实验 1、定时器中断实验 stm32关于通用定时器的周期、频率计算公式_stm32tim频率计算_胶囊咖啡的博客-CSDN博客 【STM32】通用…...
2024前端面试准备之CSS篇(二)
全文链接 1. 什么是伪类和伪元素 伪类(Pseudo-class): 伪类是选择器的一种,用于选择特定状态或条件下的元素。它们以冒号(:)开头,用于向选择器添加额外的特定条件。例如,:hover伪类用于选择鼠标悬停在元素上的状态,:nth-child(n)伪类用于选择父元素下的第n个子元素等。…...

轨道交通信号增强与覆盖解决方案——经济高效,灵活应用于各类轨道交通场景!
方案背景 我国是世界上轨道交通里程最长的国家,轨道交通也为我们的日常出行带来极大的便利。伴随着无线通信技术的快速发展将我们带入电子时代,出行的过程中对无线通信的依赖程度越来越高,无论是车站还是车内都需要强大、高质量的解决方案以…...
学习数据接构和算法的第10天
题目讲解 尾插 #include <stdio.h> #include <stdlib.h> // 定义顺序表结构 #define MAX_SIZE 100 struct ArrayList {int array[MAX_SIZE];int size; // 当前元素个数 }; // 初始化顺序表 void init(struct ArrayList *list) {list->size 0; // 初始时元素个…...

初识KMP算法
目录 1.KMP算法的介绍 2.next数组 3.总结 1.KMP算法的介绍 首先我们会疑惑,什么是KMP算法?这个算法是用来干什么的? KMP(Knuth-Morris-Pratt)算法是一种用于字符串匹配的经典算法,它的目标是在一个主文本…...

Javaweb之SpringBootWeb案例之AOP概述及入门的详细解析
2.1 AOP概述 什么是AOP? AOP英文全称:Aspect Oriented Programming(面向切面编程、面向方面编程),其实说白了,面向切面编程就是面向特定方法编程。 那什么又是面向方法编程呢,为什么又需要面向…...
【Java代码洁癖】NO.2 单元测试mock显式赋值,不能忍
反例 RunWith(MockitoJunitRunner.class) public class Test {Mockpublic SomeBean someBean new SomeBean(); } 正例 RunWith(MockitoJunitRunner.class) public class Test {Mockpublic SomeBean someBean ; } 解读 使用Mock注解的对象不应该被显式赋值,应当…...

2024.2.19
使用fread和fwrite完成两个文件的拷贝 #include<stdio.h> #include<stdlib.h> #include<string.h> int main(int argc, const char *argv[]) {FILE *fpNULL;if((fpfopen("./tset.txt","w"))NULL){perror("open error");retur…...

B端系统升级方案模板:针对美观性和体验性升级(总体方案)
大家好,我是大美B端工场,专注于前端开发和UI设计,有需求可以私信。本篇从全局分享如何升级B端系统,搞B端系统升级的有个整体思维,不是说美化几个图标,修改几个页面就能解决的,这个方案模板&…...

第九篇:node静态文件服务(中间件)
🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 📘 引言: 当今互联网时代&am…...
软件测试-功能测试-测试流程-如何进行需求评审?对于测试人员来讲,如何从测试的角度评审需求文档?
导言 产品人员编写的需求文档,无疑是一个项目或者一项新功能的开端。需求文档的优劣,直接影响开发人员的代码质量,更会影响到后续的测试工作。所以,我认为,需求评审对于开发质量以及测试质量至关重要,那么…...

无刷电机驱动详解
无刷电机驱动详解 有刷电机和无刷电机字面上理解最大的区别就是有无电刷,实际上区别还有换向器,电刷和换向器的作用是什么?电刷负责在旋转部件与静止部件之间传导电流,换向器则利用旋转惯性周期性的改变线圈中电流的方向。 所以…...

Linux+Win双系统远程重启到Win
背景 电脑安装了双系统(ubuntu 22.04 win11),默认进入ubuntu系统。给电脑设置了WoL(Wake-on-LAN),方便远程开机远程控制。 但是ubuntu的引导程序grub无法远程控制,远程开机会默认进入ubuntu。 虽然说可以进入ubuntu后…...

【XR806开发板试用】+移植rosserial到XR806
1 XR806简介 板子来源于极术社区的试用,XR806的在线网址 其主要参数: 主控XR806AF2LDDRSIP 288KB SRAM存储SIP 160KB Code ROM. SIP 16Mbit Flash.天线板载WiFi/BT双天线,可共存按键reboot按键 1,功能按键 1灯红色电源指示灯 1…...
JSON协议详解、语法及应用
文章目录 一、什么是JSON二、JSON协议结构协议结构包括要素JSON语法规则JSON的协议结构示例 三、JSON的特点四、JSON常见应用场景 一、什么是JSON JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它以易于阅读和编写的文本格式…...
kubeasz部署k8s:v1.27.5集群
安装k8s集群相关系统及组件的详细版本号 Ubuntu 22.04.3 LTS k8s: v1.27.5 containerd: 1.6.23 etcd: v3.5.9 coredns: 1.11.1 calico: v3.24.6 安装步骤清单: 1.deploy机器做好对所有k8s node节点的免密登陆操作 2.deploy机器安装好python2版本以及pip,…...

RSA加密,解密,加签及验签
目录 1.说明 2.加密和加签的区别 3.后端加密,解密,加签及验签示例 4.前端加密,解密,加签及验签示例 5.前端加密,后端解密,前端加签,后端验签 6.注意事项 1.说明 RSA算法是一种非对称加密…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...