0. 前言
- 目标:走马观花,两天时间浏览Detectron2源码,稍微记录一下。
- 与 TensorFlow Object Detection API、mmdetection 一样,Detectron2 也是通过配置文件来设置各种参数,所有的相关内容都像搭积木一样一点一点拼凑起来。
- 我自己感觉,一般所有代码都可以分为三个部分,所以看Detectron2的源码也分为以下三个部分。
- 数据处理:包括数据读取、数据增强以及其他数据预处理。
- 模型构建:没啥好说的。
- 模型训练、预测、评估:包括模型存取、优化器、学习率、损失函数、性能指标、TensorBoard等。
- 资料汇总
- Github
- 官方文档(强烈推荐)
- 知乎讨论:如何评价FAIR于2019年10月10日开源的Detectron2目标检测框架?
- 里面有一些开发人员的说明,值得一看。
- 其他一些个人博客
- 知乎专栏 - detectron2项目指北
- 这个知乎专栏中有一系列《Detectron2源码阅读笔记》文章。
- 本文内容:
- 第一章:概述,分为四个部分:看源码的过程、源码目录结构、注册机制、官方文档的阅读笔记。
- 第二章:数据处理,介绍detectron2中数据处理相关源码的结构、基本运行流程等。
- 第三章:模型搭建,介绍detectron2中模型搭建的基本流程。
- 第四章:模型训练/预测/评估,重点介绍了模型训练相关类的结构以及实现思路。
- TODO: 基于detectron2中的projects,阅读TensorMask/DensePose/TridentNet论文,并研究源码并训练模型。
1. 概述
1.1. 自己的源码阅读流程
- 设定目标:
- 刚刚从TF转向PyTorch,所以希望进一步熟悉PyTorch。
- 进一步熟悉目标检测、实例分割、关键点检测等模型。
- 寻找/研究源码中存在的一些tricks。
- 后续需要通过detectron2来复现新论文。
- 总结自己之前的一些步骤
- 第一步:阅读所有官方文档。
- 第二步:尝试根据 Getting Started 文档内容,运行
demo
中的脚本。 - 第三步:从数据处理、模型构建、模型训练/预测/评估三个方面,分别浏览源码。
- 个人感受:
- PyTorch代码比TensorFlow代码容易多了。
- Detectron2源码比TensorFlow Object Detection API源码直观多了,上手容易多了。
- 当然,也可能是因为比较熟悉TensorFlow Object Detection API的源码,所以看Detectron2的就很容易。
1.2. 目录结构
configs
:示例配置文件合集。datasets
:数据集准备工作,主要就是各个数据集的基本结构,以及需要如何预处理。demo
:快速体验Detectron2,与Getting Started文档对应。如果想要体验Model ZOO中结果的内容就可以用这个。detectron2
:项目主要代码都在这里了。dev
:一些开发者会用到的脚本。docker
:没啥好介绍的。docs
:一些官方文档。projects
:基于Detectron2的三个项目,DensePose/TensorMask/TridentNet。- 在知乎里,Detectron2的开发人员介绍,如果想要利用detectron2直接复现所有论文可能比较困难(我的理解就是直接修改detectron2中的代码),一种比较好的方式就是将detectron2作为一个包来调用来构建新的模型。
- 这里的三个项目就是利用detectron2复现模型的示例。
tests
:单元测试类。tools
:常用脚本,如训练、benchmark、展示数据集等。
1.3. 搭积木过程
- Detectron2 的基本思路就是利用配置文件搭积木。
- 第一步,将模型拆分为多个模块,每个模块可以叫做一个类型的积木。
- 第二步,构建配置文件。
- 第三步,通过配置文件,选择对应的积木。
- 配置文件概述
- 有一个默认配置文件,即
detectron2/config/default.py
文件。 - 示例配置文件放在
configs
文件夹中,且使用yaml形式。 - 所有示例配置文件都是建立在默认配置文件基础上的,即所有示例配置文件中的配置其实都是不全的,缺失的配置需要到默认配置文件中寻找。
- 感觉这也是为什么 detectron2 的配置文件比 mmdetection 看起来简洁很多。
- 有得必有失,虽然简洁,但在看源码的时候经常需要查看默认配置文件,也不是特别方便。
- 示例配置文件中有一个
_BASE_
属性,可以将其他示例配置文件作为基础,如果有冲突则用当前配置文件的信息覆盖。
- 有一个默认配置文件,即
- 如何使用配置文件搭积木
- 模型搭建的Registry机制
- 调用了 fvcore.common.registry.Registry,该对象的作用是保存一个字典,key为方法/类的名称,value为方法/类,利用
@registry_object.register
修饰目标方法/类,这样可以在导入detectron2的同时将 key/value 保存起来。 - 对于每一个类型的积木(如backbone, anchor generator, proposal generator, roi head等)都对应一个Registry对象。更多类型可以看
detectron2/modeling/__init__.py
文件。 - 一般,一个Registry对应一个
build.py
,主要就是从Registry中通过名称获取方法/类,然后将示例配置文件中参数导入目标方法/类中。
- 调用了 fvcore.common.registry.Registry,该对象的作用是保存一个字典,key为方法/类的名称,value为方法/类,利用
- 数据集的Registry机制
- 主要使用了
detectron2/data/catalog.py
中的MetadataCatalog
与DatasetCatalog
,前者保存了数据集的元数据,后者保留了一个方法,该方法用于获取数据集list(dict)
。 - 使用了Registry机制,但不是通过注解实现的,而是在
detectron2/data/datasets/buildin.py
中调用了register_all_coco()
等四个方法,这些方法调用了MetadataCatalog
和DatasetCatalog
的注册方法。 - 一般建数据集会调用
build_detection_train_loader
方法,该方法会调用DatasetCatalog
中的方法,获取list(dict)
。
- 主要使用了
- 模型训练/预测/评估的的搭积木不复杂,就是根据配置文件,直接创建对应的对象(如lr, optimizer等)。
- 模型搭建的Registry机制
1.4. 官方文档阅读
- 官方文档地址,如果想了解Detectron2的源码,强烈建议先看看。
- tutorials
- Installation:安装,没啥好说的。
- Getting Started with Detectron2:跑个Demo,没啥好说的。
- Extend Detectron2's Defaults
- 谈了谈Detectron2的基本设计思路。一方面要有足够的灵活性(做研究总是要做新东西),一方面要有较好的高层抽象。
- 基本设计思路:所有的方法和类都可以从一个配置文件中获取所需要的参数(配置文件中没有的,就使用默认参数)。
- 介绍了扩展detectron2的一些相关文档。
- Use Custom Datasets
- dataset只是解析数据集,而没有进行数据处理(数据处理在后面dataloader中进行)。
- dataset的输出将会作为后续dataloader的输入。
- 自定义数据集步骤:
- 注册数据集(使用注释告诉detectron2如何获取该数据集),需要制定数据集名称以及一个
get_dict
方法,该方法用于获取一个list[dict]
对象,每个字典就是一条输入数据,具体的key列表可以到文档中自己看。 - 可以注册一些自定义 metadata。
- 注册数据集(使用注释告诉detectron2如何获取该数据集),需要制定数据集名称以及一个
- 数据集的 metadata 介绍
- 一个记录数据库相关信息的字典,比如primitive information that helps interpret what's in the dataset, e.g., names of classes, colors of classes, root of files, etc.
- 可以通过
MetadataCatalog.get(dataset_name).set(name, value)
为新数据库添加元数据。
- Use Custom Dataloaders
- 介绍数据处理模块,其实就是一系列数据增强等操作,以上述dataset的结果作为输入,并作为后续Model的输入。
- 具体过程如下:
- 首先,根据数据集名称获取一个已经注册的数据集(就是上面的dataset),获取
list[dict]
对象。 - 其次,数据增强等其他数据处理流程都内置于
DatasetMapeer
中。 - 最后,需要batch数据,batch后的数据一般就作为
model.forward()
的输入。
- 首先,根据数据集名称获取一个已经注册的数据集(就是上面的dataset),获取
- 介绍如何自定义Dataloader、使用自定义Dataloader可以参考DensePose的代码。
- Use Models
- 主要介绍如何构建模型。
- 构建模型方式:通过调用
build_model
,build_backbone
,build_roi_heads
等方法来构建。 - 要导入权重可以使用
DetectionCheckpointer(model).load(file_path)
。 - 使用模型就是
outputs = model(inputs)
- 模型输入使用的参数通过
list[dict]
来实现,即上面dataloader的输出,具体的key形式可以参考这个页面中的内容。 - 模型输出也是一个
list[dict]
,具体的形式可以参考这篇文章中的内容。
- Write Models
- 自定义模型相关。
- 举了个例子如果自定义backbone该怎么做。
- Training
- 就提了下训练相关的代码。
- 一般使用
tools/plain_train_net.py
来训练模型。 - 最简单的训练结构是
SimpleTrainer().train()
。 - 一般使用的类是
DefaultTrainer().train()
。
- Use Configs
- 介绍了配置系统的基本结构,即使用yaml和yacs来配置。
- 配置文件的使用,其实就是对
CfgNode
对象的使用。 - 建议使用配置文件的方式,我比较在意的是 使用
_BASE_
参数来重复配置定义到一个文件中。
- notes
- Benchmarks
- 记录一下训练时间,与其他库也比较比较。
- Compatibility with Other Libraries
- 与其他目标检测库的Compatibility。
- Contributing to detectron2
- Change Log
- Benchmarks
- API Documentation
2. 数据处理
2.1. 概述
- 实现的功能:
- 解析COCO、cityscapes等数据集。
- 提供数据预处理以及增强的接口。
- 通过配置文件即可实现数据集解析、预处理、增强等操作。
- 主要入口:
detectron2/data/build.py
中的build_detection_train_loader
或build_detection_test_loader
方法。 - 相关代码:主要位于
detectron2/detectron2/data
目录下。 - 相关配置:
detectron2/config/defaults.py
中_C.INPUT _C.DATASETS _C.DATALOADER
开头的配置。
2.2. 基本流程
- 第一步:在导入 detectron2 模块时,通过Register机制注册一些常用的数据集。
- 注册机制可以参考
1.3.
中的内容。 - 注册代码在
detectron2/detectron2/data/datasets/builtin.py
。
- 注册机制可以参考
- 第二步:通过数据集名称以及完成注册的
DatasetCatalog
对象以及MetadataCatalog
对象,解析数据集并获取数据集基本信息。- 从源码角度看,就是调用了
DatasetCatalog
中的对应的方法,获取list[dict]
对象。
- 从源码角度看,就是调用了
- 第三步:通过mapper函数,对解析完的数据集进行进一步处理,包括数据增强,并将修改数据的结构,使之可以直接作为后续模型的输入。
- 从源码角度理解就是,从
DatasetCatalog
获取的是list[dict]
,mapper函数输出的也是list[dict]
,但前后两个字典的形式是不一样的,具体可以参考官方文档,里面都有具体的描述。
- 从源码角度理解就是,从
2.3. build_detection_train_loader 方法解析
- 源码位于
detectron2/detectron2/data/build.py
中。 - 流程:
- 第一步:获取
list[dict]
对象。先根据数据库名称调用DatasetCatalog
中的方法,获取原始list[dict]
对象,再通过一些条件进行筛选。 - 第二步:构建
DatasetFromList
对象,该类是torch.utils.data.Dataset
的子类。 - 第三步:根据mapper对上面的dataset对象进行进一步处理。
- 浏览了下
DatasetMapper
的源码,主要工作包括读取图像、resize、crop、flip、转换数据与标签的形式等。
- 浏览了下
- 第四步:构建
torch.utils.data.sampler.Sampler
对象,实现的功能好像包括Repeat Sample、shuffle、batch功能。 - 第五步:根据上面的 dataset, sampler 等对象构建
torch.utils.data.DataLoader
对象。
- 第一步:获取
- 感想:
- 好像也没有什么特别的数据增强工作。
- Detectron2实现的
DatasetFromList
、MapDataset
等,有点tf.data
的感觉,挺有意思。
2.4. 其他
- 数据增强
- 方法主要都在
detectron2/detectron2/data/transforms/transform_gen.py
中定义。 - 调用的话主要是通过
from detectron2.data import transforms as T
以及T.ResizeShortestEdge
来实现。 - 在默认实现中,就没用到什么特别的数据增强。具体的可以到
DatasetMapper
的源码中看。
- 方法主要都在
3. 模型搭建
3.1. 概述
- 实现的功能:通过配置文件构建模型。
- 主要入口:
detectron2/detectron2/modeling/meta_arch/build.py
中的def build_model(cfg)
方法。 - 相关代码:
detectron2/detectron2/modeling
目录下。 - 相关配置:
detectron2/config/defaults.py
中_C.MODEL
开头的配置。
3.2. 基本流程
- 第一步:根据注册机制,在导入 detectron2 时,将各个类型的积木通过注解的方式保存到 Registry 对象中。
- 第二步:根据配置文件中
META_ARCHITECTURE
参数,选择基本框架,也就是 meta arch。- 基本框架(meta arch)的类型没集中,包括 rcnn, retinanet, semantic seg, panoptic 四种。
- 每个基本框架(meta arch)中都定义了一系列子部件,也都是用Register机制来管理(即通过配置文件与Register对象来构建)。
- 基本框架的定义中,就包含了模型如何构建、如何训练、如何预测等相关功能。
- 第三步:通过配置文件分别构建选中meta arch中各个部件。
3.3. 其他
- Registry对象列表
ANCHOR_GENERATOR_REGISTRY
:如何生成anchors。BACKBONE_REGISTRY
:主干网络,包括FPN。META_ARCH_REGISTRY
:基本网络,总体结构。SEM_SEG_HEADS_REGISTRY
:应该是用来做语义分隔的。PROPOSAL_GENERATOR_REGISTRY
:Faster RCNN中的Region proposal Network,即如何生成proposals。RPN_HEAD_REGISTRY
:第一阶段训练所需的输入。ROI_BOX_HEAD_REGISTRY
:ROI Head中的bbox分支。ROI_HEADS_REGISTRY
:通过特征图和第一阶段的proposals得到ROI。ROI_KEYPOINT_HEAD_REGISTRY
:ROI Head中的keypoint分支。ROI_MASK_HEAD_REGISTRY
:ROI Head中的mask分支。
- 除了通过注册机制管理的部件外,还有一系列模型所需的部件,具体的可以参考 meta_arch 中的相关源码。
4. 训练/评估/预测
4.1. 概述
- 实现的功能:通过配置文件构建模型。
- 主要入口:
detectron2/detectron2/engine/defaults.py
中的DefaultTrainer, DefaultPredictor
。 - 相关代码:主要在
detectron2/detectron2/engine
和detectron2/detectron2/solver
中 - 相关配置:
detectron2/config/defaults.py
中_C.SOLVER _C.TEST
开头的配置。
4.2. 训练代码结构
- 主要包括了:
TrainerBase, SimpleTrainer, DefaultTrainer
三个类。 TrainerBase
:- 定义在
detectron2/detectron2/engine/train_loop.py
中。 - 主要功能:
- 提供了 hooks 机制,可以通过导入
HookBase
对象,在训练过程的各个时间点进行自定义处理。 - 定义了训练函数为
train(self, start_iter: int, max_iter: int)
,且维训练提供了一个EventStorage
对象。
- 提供了 hooks 机制,可以通过导入
- 这个与TF中的SessionRunHook类似,只不过TF已经实现在源码里,而Detectron2中是自己实现的。
- 定义在
SimpleTrainer
:- 定义在
detectron2/detectron2/engine/train_loop.py
中。 - 主要功能:在
TrainerBase
的基础上添加了训练所需的基本参数以及最基本的训练过程代码。 - 基本训练参数指的是
model/data_loader/optimizer
- 基本训练过程包括位于
run_step
函数中,主要包括的功能是:- 导入数据。
- 计算损失函数(并确保损失函数是有效的)。
- 记录一些性能指标(包括损失函数、时间点),保存到
EventStorage
对象中。 - 进行梯度下降操作。
- 定义在
DefaultTrainer
- 定义在
detectron2/detectron2/engine/defaults.py
中。 - 主要功能:
- 在
SimpleTrainer
的基础上,提供了通过配置文件创建模型、数据集、优化器、学习率等一系列操作。 - 提供了 checkpoint 功能。
- 使用了一系列常见的 hooks。
- 在
- hooks的定义都在
detectron2/detectron2/engine/hooks.py
中。
- 定义在