最早一直想把mmdetection吃掉,但是一只拖延没有搞定。趁着近期休息,mmlab官方也举办了一个mmlab-challenge(https://openmmlab.com/competitions/algorithm-2021)复现论文的比赛,报名准备督促自己学习一下mmTask的框架。最终决定从mmsegmentation(https://github.com/open-mmlab/mmsegmentation)切入,记录一下过程中的问题。
首先建议大家学习的话直接从最新版的代码看起,时间隔的久的话代码变化还蛮大的。比如我最早是从CVPR2021-SETR的code(https://github.com/fudan-zvg/SETR)入手的,后来又看最新的code发现变化蛮大的(主要隔了估计也有快1年了...)。而且像cvpr这种顶会的论文复现code一般都会在最新版集成进去。再退一步,某一篇论文也只是一个你看code的切入点,因为你看完之后发现mmlab的code模块化很明显,不同的paper之间只有少量文件不同。
感受了一波contributer的感觉2333
1. 复刻 OpenMMLab 原代码库,点击 GitHub 页面右上角的 Fork 按钮即可
2. 克隆复刻的代码库到本地
git clone [email protected]:XXX/mmcv.git
3. 添加原代码库为上游代码库
git remote add upstream [email protected]:open-mmlab/mmcv
4. 拉取最新的原代码库的主分支
git pull upstream master
5. 新建并检出一个新的分支,进行开发
git checkout –b branchname
# 写code
git add [files]
git commit -m 'messages'
6. 推送到复刻的代码库
git push -u origin branchname
7. 创建一个 PR
code部分有个Compare & pull request
8. 创建 PR 时,可以关联给相关人员进行 review
点击create pull request
9. 关联相关的 issue 和 PR
10. 根据 reviewer 的意见修改代码,并推送修改
11. PR 合并之后删除该分支
git branch –d branchname # 删除本地分支
git push origin --delete branchname # 删除远程分支
遇到的一个基础问题就是代码风格问题,每个项目都有自己的格式,commit&push前尽量自行统一格式,免得pr之后对方还要就格式问题耽误时间。
格式的话repr中一般已经定义好了,在‘.pre-commit-config.yaml’这个文件中,我们需要安装pre-commit这个东西来实现commit前根据这个config文件来check code的style是否和repr要求的一样。
# After you clone the repository, you will need to install initialize pre-commit hook.
pip install -U pre-commit
# From the repository folder
pre-commit install
reference:https://github.com/open-mmlab/mmsegmentation/blob/master/.github/CONTRIBUTING.md
ps:在实际使用时pre-commit构建环境的时候出现了一些bug(‘fake_gem__-0.0.0.gem’,‘gem’,‘build’啥啥乱七八糟的),总结下来好像就是没有安装ruby,>=2.4版本的。本来apt install ruby-full就能直接安装的,但是貌似我修改了源之后最新版就是2.3,很蛋疼,还要自己添加新的源来下载更新版本的ruby,参考:https://www.brightbox.com/blog/2017/01/13/ruby-2-4-ubuntu-packages/。
$ sudo apt-add-repository ppa:brightbox/ruby-ng
$ sudo apt-get update
$ sudo apt-get install ruby2.4 ruby2.4-dev
$ ruby -v
ruby 2.4.10p364 (2020-03-31 revision 67879) [x86_64-linux-gnu]
先上代码结构,训练时主要使用时用到configs,mmseg,tools,mmcv。
configs---- 其中configs保存了不同论文的不同实验的各种配置,包括模型,数据集,优化器,等等之类的。以configs/setr/setr_mla_512x512_160k_b8_ade20k为例:
_base_ = []其中是一些默认设置,_base_是一个configs目录下的一个文件夹。
下边的配置是用于覆盖默认配置的。如optimizer中paramwise_cfg = dict(custom_keys={'head': dict(lr_mult=10.)})意思就是模型结构中名字包含‘head’子字符串的module对应的parameters的学习率要在基础学习率的基础上*10.,也就是0.001*10 = 0.01。
图1
mmseg---- 其中mmseg主要包含了用于初始化dataset和model的类文件,训练文件(apis/train.py,训练部分的pipeline)还有一些其他辅助类的文件(log之类的)。
tools---- tools就是分布式训练的command(dist_train.sh)和训练的pipeline(train.py)。
mmcv---- mmcv是提供基础功能的。主要会用到mmcv/runner和mmcv/utils,runner中包含真正训练时的code(是按照iteration还是epoch之类的,有对应实现的类,前边两个train.py是更外层的pipeline code)
注:mmcv里这两个也是mmlab各种库的精髓,一个是runner中通过Hook类来操作训练中更微观的步骤(比如学习率更新,优化,模型保存,写入log之类的操作,如下图2),这些hook都有专门的类来实现,类中有对应的函数(‘before_train_iter’,‘after_train_iter’),在runner.train中串联调用(所有的hooks都存在了一个list中,self.call_hook依次调用对应函数)来实现训练过程(Hook类会被insert一个priority优先级参数来控制hook list的顺序,也就是执行顺序,其实一般是按照加入的顺序来的)。
图2
图3
另外一个就是mmcv/utils/registry.py中Registry的使用。训练中所有的模块几乎都是通过Registry来实现的。Registry可以将strings映射到真实的classes(如type“ResNet”到class ResNet)
方式是通过在定义ResNet时候就通过@MODELS.register_module()来统一管理了,相当于我手里有个小本本,我的库里有什么模型都有记录,你想要啥,你在config中写好type,我帮你找,初始化出来返还给你。mm系列的的model,dataset,optimizer包括hook都是通过这种形式实现的,也都有对应Registry类实例:MODELS,DATASETS,OPTIMIZERS,HOOKS等。
图4
注:mmcv是应该装到本地的,是mm系列的基础库。但是我看代码的电脑windows缺编译器装不上这个,所以干脆下载下来放在目录中了)
# 待续。。。。。。
作为open-mmlab主库,还是值得记录一下的。
2.2.1.1 config.py
·class ConfigDict(Dict): 继承自addict中的Dict(which继承自python的dict),是对python dict的一种用法上的改进。
·def add_args(parser, cfg, prefix=''): 在parser中添加argument
·class Config:A facility for config and config files. (一堆静态函数。。。)
# 在def __init__(self, cfg_dict=None, cfg_text=None, filename=None):中定义了三个attr
super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict))
super(Config, self).__setattr__('_filename', filename)
super(Config, self).__setattr__('_text', text)
·class DictAction(Action): 继承自argparse中的Action。细节用到再补。
2.2.1.2 registry.py
·def build_from_cfg(cfg, registry, default_args=None):
"""Build a module from config dict. Args: cfg (dict): Config dict. It should at least contain the key "type". registry (:obj:`Registry`): The registry to search the type from. default_args (dict, optional): Default initialization arguments. Returns: object: The constructed object. """
cfg中包含type和一些类实例化时的参数,通过type结合register去寻找对应的类,再用cfg剩余的args初始化这个类并返回。
·class Registry:
A registry to map strings to classes.
segmentation task specific的库
2.3.1.4 train.py
初始化seed的函数(set_random_seed);
以及mmseg的基础训练文件(build data_loaders、optimizer、给model套分布式壳子;构建runner(from mmcv)、register train和validation的hooks);
2.3.4.1 builder.py
BACKBONES = Registry('backbone') NECKS = Registry('neck') HEADS = Registry('head') LOSSES = Registry('loss') SEGMENTORS= Registry('segmentor')
· def build(cfg, registry, default_args=None):
调用mmcv.utils.registry.py中的build_from_cfg函数构建nn module。
· build是基本函数,服务于以下:
def build_backbone(cfg): """Build backbone.""" return build(cfg, BACKBONES) def build_neck(cfg): """Build neck.""" return build(cfg, NECKS) def build_head(cfg): """Build head.""" return build(cfg, HEADS) def build_loss(cfg): """Build loss.""" return build(cfg, LOSSES) def build_segmentor(cfg, train_cfg=None, test_cfg=None): """Build segmentor.""" return build(cfg, SEGMENTORS, dict(train_cfg=train_cfg, test_cfg=test_cfg))
一些训练测试脚本文件之类的。
使用mmcv中完备的config管理工具记录当前实验的配置信息至log中。
build segmentor。build datasets。