[Detectron2/AdelaiDet笔记 ]使用Detectron2训练自己的数据集(直接修改框架内部文件)

如果觉得有帮助的话,请给我点个赞呗~

前言以及为什么写这篇的原因:
        国庆的时间在网络上查找如何使用Detectron2注册数据集,并使用了2位博主的相关代码,除了官方文档https://detectron2.readthedocs.io/以外,再次非常感谢2位博主的文章https://blog.csdn.net/qq_29750461,以及https://blog.csdn.net/weixin_39916966对我的帮助和启发。
        但目前我所看到的大家写的方法都是直接在tools/train_net.py上直接添加相应的函数并且配置cfg的参数(这一部分可以在yaml中配置,更耦合)。
       因此我想的是如何直接在Detectron2框架中的对应的python文件中修改对应部分的代码,使得train_net.py上不是那么冗余,使得添加的代码更加规范,更利于重构,并且进行下一步的修改会更加简单,这便是我写这篇博客的目的~

学会注册数据集之前应该至少会点什么:
        1. 制作好自己的数据集,如annatation的coco-json格式或者voc-xml格式,以及train和val的图片数量与路径等。(如果使用标准的数据集忽略这一步)
        2. 安装好Detectron2或AdelaiDet,至少熟悉了Detectron2AdelaiDet的INSTALL.md和GETTING_STARTED.md。AdelaiDet在安装的时候有一个小坑,具体可以看下另一篇文章AdelaiDet安装教程。(截至2020.1130)
        3. 配置好数据集,查阅github上的datasets/readme.md,如何Use Builtin Datasets。
AdelaiDet

# AdelaiDet需要以下命令:
git clone https://github.com/facebookresearch/detectron2.git
cd detectron2
git checkout -f 9eb4831 # 官方提示
cd ..	
python -m pip install -e detectron2
git clone https://github.com/aim-uofa/AdelaiDet.git
cd AdelaiDet
python setup.py build develop

        AdelaiDet是什么? AdelaiDet可以算是Detectron2的一个扩展包,基本和Detectron2类似。由于Detectron2中做detection的只有faster-rcnn的anchor-based模型,而AdelaiDet中含有了FCOS等比较新的anchor-free模型,AdelaiDet中的FCOS是采用coco format,因此我需要将训练的pascalraw数据集从voc format 转化成 coco format。并且raw数据集只有3个class。
        另外要特别说明的是,根据buildin.py文件中的COCO_CATEGORIES文件中的列表中展示的。 id都是从1开始的。因此在自己制作json文件时候,也要检查json中的catagories_id是不是从1开始的。

1.如何注册数据集

        借用之前博主的言语,只要熟悉了这几个函数,基本就能够明白注册的流程了。

data/datasets/builtin.py/register_all_coco(root="datasets") 
data/datasets/builtin_meta.py/_get_builtin_metadata(dataset_name) 
data/datasets/builtin_meta.py/_get_coco_instances_meta() 
data/datasets/register_coco.py/register_coco_instances(name, metadata, json_file, image_root)


#上面4个都和register dataset息息相关,一定要掌握上面4个函数
data/datasets/coco.py/load_coco_json(json_file, image_root, dataset_name=None, extra_annotation_keys=None)

        首先还是要把训练集按照文档的要求放置。详细的操作请看Detectron2的datasets目录下的readme.md

#COCO-format
coco/
  annotations/
    instances_{
    
    train,val}2017.json
    person_keypoints_{
    
    train,val}2017.json
  {
    
    train,val}2017/
    # image files that are mentioned in the corresponding json
#VOC-format
VOC20{
    
    07,12}/
  Annotations/
  ImageSets/
    Main/
      trainval.txt
      test.txt
      # train.txt or val.txt, if you use these splits
  JPEGImages/

1.1 detectron2/detectron2/data/datasets/builtin.py

1.1.1 _PREDEFINED_SPLITS_COCO

_PREDEFINED_SPLITS_COCO字典中,添加你的your_dataset_name : (image/root, json_file)

_PREDEFINED_SPLITS_COCO = {
    
    }

_PREDEFINED_SPLITS_COCO["coco"] = {
    
    
    "coco_2014_train": ("coco/train2014", "coco/annotations/instances_train2014.json"),
    "coco_2014_val": ("coco/val2014", "coco/annotations/instances_val2014.json"),
    "coco_2014_minival": ("coco/val2014", "coco/annotations/instances_minival2014.json"),
    "coco_2014_minival_100": ("coco/val2014", "coco/annotations/instances_minival2014_100.json"),
    "coco_2014_valminusminival": (
        "coco/val2014",
        "coco/annotations/instances_valminusminival2014.json",
    ),
    "coco_2017_train": ("coco/train2017", "coco/annotations/instances_train2017.json"),
    "coco_2017_val": ("coco/val2017", "coco/annotations/instances_val2017.json"),
    "coco_2017_test": ("coco/test2017", "coco/annotations/image_info_test2017.json"),
    "coco_2017_test-dev": ("coco/test2017", "coco/annotations/image_info_test-dev2017.json"),
    "coco_2017_val_100": ("coco/val2017", "coco/annotations/instances_val2017_100.json"),
    
	#我新注册的数据集,注意路径别写错了,以及注册的名字如coco_raw_train别写错了。    
    "coco_raw_train": ("coco_raw/train2017", "coco_raw/annotations/raw_train.json"),
    "coco_raw_val": ("coco_raw/val2017", "coco_raw/annotations/raw_val.json"),
    #"coco_raw_frcnn_train": ("coco_raw/train2017", "coco_raw/annotations/raw_train.json"),
    #"coco_raw_frcnn_val": ("coco_raw/val2017", "coco_raw/annotations/raw_val.json"),    
}

1.1.2 register_all_coco(root)

        这是注册函数的核心入口,在这里面还调用了_get_builtin_metadata(dataset_name)以及register_coco_instances()这2个函数
(注意,记得按照dataset.md教学中手动设置下环境变量,如果你的datasets没有放在./datasets相对路径下的话~)

# three steps
def register_all_coco(root): #first step
    for dataset_name, splits_per_dataset in _PREDEFINED_SPLITS_COCO.items():
        for key, (image_root, json_file) in splits_per_dataset.items():
            # Assume pre-defined datasets live in `./datasets`.
            # print('detectron2:', key, (image_root, json_file))
            register_coco_instances(  #second step
                key,
                _get_builtin_metadata(dataset_name), #third step
                os.path.join(root, json_file) if "://" not in json_file else json_file,
                os.path.join(root, image_root),
            )

1.2 detectron2/detectron2/data/datasets/builtin_meta.py

1.2.1 COCO_fomat_your_categories

COCO_CATEGORIES = [
    {
    
    "color": [220, 20, 60], "isthing": 1, "id": 1, "name": "person"},
    {
    
    "color": [119, 11, 32], "isthing": 1, "id": 2, "name": "bicycle"},
    {
    
    "color": [0, 0, 142], "isthing": 1, "id": 3, "name": "car"},
    {
    
    "color": [0, 0, 230], "isthing": 1, "id": 4, "name": "motorcycle"},
    {
    
    "color": [106, 0, 228], "isthing": 1, "id": 5, "name": "airplane"},
    {
    
    "color": [0, 60, 100], "isthing": 1, "id": 6, "name": "bus"},
    {
    
    "color": [0, 80, 100], "isthing": 1, "id": 7, "name": "train"},
    {
    
    "color": [0, 0, 70], "isthing": 1, "id": 8, "name": "truck"},
    {
    
    "color": [0, 0, 192], "isthing": 1, "id": 9, "name": "boat"},
    ##省略 
    ]

# 仿造制作数据集 注意此处的id,从1开始(我不知道0开始如何)		
# 以及name 必须必须要和json_file相对应,也就是categoried的顺序,需要认真检查。
COCO_CATEGORIES_raw = [
    {
    
    "color": [119, 11, 32], "isthing": 1, "id": 1, "name": "bicycle"},
    {
    
    "color": [0, 0, 142], "isthing": 1, "id": 2, "name": "car"},
    {
    
    "color": [220, 20, 60], "isthing": 1, "id": 3, "name": "person"},
]

1.2.2 _get_builtin_metadata(dataset_name)

接下来为数据集注册元数据。

def _get_builtin_metadata(dataset_name):
    if dataset_name == "coco":
        return _get_coco_instances_meta()  #如果是coco 进入_get_coco_instances_meta()_get_coco_instances_meta()函数
    if dataset_name == "coco_panoptic_separated":
        return _get_coco_panoptic_separated_meta()
    elif dataset_name == "coco_person":
        return {
    
    
            "thing_classes": ["person"],
            "keypoint_names": COCO_PERSON_KEYPOINT_NAMES,
            "keypoint_flip_map": COCO_PERSON_KEYPOINT_FLIP_MAP,
            "keypoint_connection_rules": KEYPOINT_CONNECTION_RULES,
        }

1.2.3 _get_coco_instances_meta

#注意: 可以在原来的基础之上进行修改,这里我把原来的COCO_CATEGORIES改成了我使用的 标注列表COCO_CATEGORIES_raw  改成你们自己的就好~
def _get_coco_instances_meta(): 
    thing_ids = [k["id"] for k in COCO_CATEGORIES_raw if k["isthing"] == 1] 
    thing_colors = [k["color"] for k in COCO_CATEGORIES_raw if k["isthing"] == 1]
    assert len(thing_ids) == 3, len(thing_ids)
    # Mapping from the incontiguous COCO category id to an id in [0, 79]
    thing_dataset_id_to_contiguous_id = {
    
    k: i for i, k in enumerate(thing_ids)}
    thing_classes = [k["name"] for k in COCO_CATEGORIES_raw if k["isthing"] == 1]
    #print("_get_coco_instances_meta thing_classes:", thing_classes)
    '''
    _get_coco_instances_meta thing_classes: ['bicycle', 'car', 'person']
    '''
    ret = {
    
    
        "thing_dataset_id_to_contiguous_id": thing_dataset_id_to_contiguous_id,
        "thing_classes": thing_classes,
        "thing_colors": thing_colors,
    }
    
    print("ret:", ret)
    '''
    ret = {'thing_dataset_id_to_contiguous_id': {1: 0, 2: 1, 3: 2}, 'thing_classes': ['bicycle', 'car', 'person'], 'thing_colors': [[119, 11, 32], [0, 0, 142], [220, 20, 60]]}
    '''
    return ret

        以上的几个内容添加或者修改完后,就可以进行对yaml参数文件的配置了。

2.如何修改配置文件

        首先配置文件的路径在detectron2/config/,师兄和我讲过很精辟的总结:把参数yaml文件看成是一种json文件。例如cfg.MODEL.FCOS.NUM_CLASSES就可以在对应的yaml文件写成:

 MODEL:
  FCOS:
   NUM_CLASSES: 80

        其实就是缩进2格就代表了从属的关系,Detectron2使用这种yaml文件是非常方便你就不用额外的写这么多了,只要设置好对应的–config-file 配置yaml文件就好。
        另外yaml文件是继承与覆盖的关系。如果要修改yaml文件,记得看清楚特殊的就在子类上修改,并且在DATASET.TRAIN/VAL中要与上一步中相对应。建议练手之前先copy一份,具体的默认配置参数再detectron2/detectron2/data/default.py文件下。
例:Base-FCOS.yaml 以及 R_50_1x.yaml

#由Base-FCOS.yaml修改的Base-FCOS_raw.yaml
MODEL:
  META_ARCHITECTURE: "OneStageDetector"
  BACKBONE:
    NAME: "build_fcos_resnet_fpn_backbone"
  RESNETS:
    OUT_FEATURES: ["res3", "res4", "res5"]
  FPN:
    IN_FEATURES: ["res3", "res4", "res5"]
  PROPOSAL_GENERATOR:
    NAME: "FCOS"
  # PIXEL_MEAN: [102.9801, 115.9465, 122.7717]
  FCOS:
    NUM_CLASSES: 3
DATASETS:
  TRAIN: ("coco_raw_train",) #your_dataset_train
  TEST: ("coco_raw_val",)#your_dataset_val
SOLVER:
  IMS_PER_BATCH: 8 #根据所使用的模型以及自己的GPU手动调节
  BASE_LR: 0.01  # Note that RetinaNet uses a different default learning rate
  STEPS: (7000, 9000) #在steps步后按照CFG.SOLVER.GAMMA降低学习率
  MAX_ITER: 11900 #MAX_ITER = epoch * image_train_total_nums / IMS_PER_BATCH
  CHECKPOINT_PERIOD: 425 #save XX.pth文件 per EVAL_PERIOD
  #MAX_ITER: 90000
INPUT:
  #MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800)
  MIN_SIZE_TRAIN: (400, 600) # dt2设置的图片最大尺寸默认是1333(也可以更改),因此图片的(height, width)会根据你设置的MIN_SIZE_TRAIN进行缩小或者放大。 
  MIN_SIZE_TEST: 400 
TEST:
  EVAL_PERIOD: 850 #eval
#由R_50_1x.yaml 修改后的文件R_50_1x_raw.yaml

_BASE_: "Base-FCOS_raw.yaml" #继承Base-FCOS.yaml
MODEL:
  WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" # pre-trained model
  FCOS: #MODEL.FCOS
    NUM_CLASSES: 3 #MODEL.FCOS.NUM_CLASSES
  RESNETS:
    DEPTH: 50
INPUT:
  MIN_SIZE_TRAIN: (400, 600)
OUTPUT_DIR: "output/fcos/R_50_1x"  # your output_dir 也可以手动在命令行中设置。

3.训练、测试、进行可视化

3.1 训练

        train_net.py是训练以及评估的入口,根据官方文档自行修改。



#重要的参数有:
# --config-file  yaml文件
# --num-gpus 当然可以手动设置CUDA_VISIBLE_DEVICES~
# OUTPUT_DIR   SOLVER.IMS_PER_BATCH SOLVER.BASE_LR 这些可以手动设置,当然也直接在yaml文件中设置好~
./train_net.py \
  --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml \
  --num-gpus 1   OUTPUT_DIR  path/to/yourdataset/ SOLVER.IMS_PER_BATCH 2 SOLVER.BASE_LR 0.0025

3.2 可视化评估

       运行demo.py,注意修改自己的yaml文件以及pth文件。以FCOS为例,评估中比较重要的NMS的阈值参数如下所示,可以适当调节。

# ---------------------------------------------------------------------------- #
# FCOS Head
# ---------------------------------------------------------------------------- #
_C.MODEL.FCOS = CN()

# This is the number of foreground classes.
_C.MODEL.FCOS.NUM_CLASSES = 80
_C.MODEL.FCOS.IN_FEATURES = ["p3", "p4", "p5", "p6", "p7"]
_C.MODEL.FCOS.FPN_STRIDES = [8, 16, 32, 64, 128]
_C.MODEL.FCOS.PRIOR_PROB = 0.01
_C.MODEL.FCOS.INFERENCE_TH_TRAIN = 0.05 # 直接舍去低于0.05的像素点。
_C.MODEL.FCOS.INFERENCE_TH_TEST = 0.05
_C.MODEL.FCOS.NMS_TH = 0.4 # NMS_TH如果设置的过高,那么就会出现top_1 与其他top_k的IOU< NMS_TH 就会留下多余的框
_C.MODEL.FCOS.PRE_NMS_TOPK_TRAIN = 1000
_C.MODEL.FCOS.PRE_NMS_TOPK_TEST = 1000
_C.MODEL.FCOS.POST_NMS_TOPK_TRAIN = 100
_C.MODEL.FCOS.POST_NMS_TOPK_TEST = 100
_C.MODEL.FCOS.TOP_LEVELS = 2
_C.MODEL.FCOS.NORM = "GN"  # Support GN or none
_C.MODEL.FCOS.USE_SCALE = True
# --input 可以是目录 也可是一张图片
# --output 可以是目录 也可是一张图片
#
cd demo/
CUDA_VISIBLE_DEVICES=0 python demo.py  --config-file ../configs/FCOS-Detection/R_50_1x_pascalraw_resize_rgb.yaml 
--input test_thr.jpg 
--output thr_06.jpg 
--opts MODEL.WEIGHTS ../test_fcos/RGB/MS_R_50_2x/model_final.pth 

在这里插入图片描述

3.3 测试评估

       同样,在OUTPUT_DIR查看生成的log.txt中的, 最后的mAP以及 A P 50 {AP_{50}} AP50 以及对应的bicycle,car, persopm的AP值。以FCOS为例:


# --eval-only 默认是train
# MODEL.WEIGHTS 在你的OUTPUT_DIR路径下去寻找你所希望使用的pth权重文件。
# OUTPUT_DIR pth文件以及log,torsorboard文件的保存路径
python tools/train_net.py \
    --config-file configs/FCOS-Detection/R_50_1x.yaml \
    --eval-only \
    --num-gpus 8 \
    OUTPUT_DIR training_dir/fcos_R_50_1x \
    MODEL.WEIGHTS training_dir/fcos_R_50_1x/model_final.pth

       还可以通过使用vs-code,cd到保存的目录下,然后使用tensorboard去查看events文件,简要命令如下。

# use vs-code
# cd /path/to/your/output_dir
# 如果文件过多可以先mkdir
# tensorboard --logdir='.'

在这里插入图片描述

4. 总结

       这周大部分时间在学习如何使用AdelaiDet子框架来训练自己的数据集。由于AdelaiDet是类似Detectron2的扩展框架,有些地方比较麻烦,并且上周的class对应不正确问题,导致卡壳了很久,这一整周又踩了许多坑,例如修改的json文件出错,训练不匹配,可视化后的图片没有class标签等等。
       最后非常感谢多个师兄在这一周多的时间对我的帮助,亲手指导,点拨了我很多等等。从一开始摘抄博客中相应的冗余的代码进行训练,却不知其所以然,到后来能够使用相应的配置文件,直接在对应的注册数据集文件中修改对应的代码,进行修改添加,深感自己debug水平,阅读代码水平之差,需要继续完成下一步的操作。
       因此将整个过程整理一下,方便后续的操作,也希望能够让其他人少走一点弯路,希望对大家有帮助。
tips:
如果有啥问题,麻烦在评论底下留言不要私信啦谢谢,评论底下留言,如果有其他人遇到这个问题,还可以帮助您~

5. references

  1. https://blog.csdn.net/qq_29750461
  2. https://blog.csdn.net/weixin_39916966
  3. https://detectron2.readthedocs.io/

猜你喜欢

转载自blog.csdn.net/weixin_43823854/article/details/108980188