[Detectron2/AdelaiDet]笔记 使用Detectron2/AdelaiDet直接修改对应文件训练自己的数据集

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

AdelaiDet可以算是Detectron2的一个扩展包,基本和Detectron2类似。
buildin.py
builtin_meta.py

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

前提

安装好detectron2或AdelaiDet。或者至少熟悉了detectron2或AdelaiDet的INSTALL.md和GETTING_STARTED.md以及datasets/readme.md如何Use Builtin Datasets

1.如何注册数据集

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

data/datasets/buildin.py/register_all_coco(root="datasets") 
data/datasets/buildin_meta.py/_get_builtin_metadata(dataset_name) 
data/datasets/buildin_meta.py/_get_coco_instances_meta() 
data/datasets/register_coco.py/register_coco_instances(name, metadata, json_file, image_root)
data/datasets/coco.py/load_coco_json(json_file, image_root, dataset_name=None, extra_annotation_keys=None)

首先还是要把训练集按照文档的要求放置。

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

1.1 buildin.py

在_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"),    
}

register_all_coco(root),这是注册函数的核心入口

在这里面还调用了_get_builtin_metadata(dataset_name)以及register_coco_instances()这2个函数

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. buildin_meta.py

_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,
        }

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(我不知道0开始如何)		 以及name 必须必须要和json_file相对应,需要认真检查。
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"},
]

_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

2.如何修改配置文件

师兄和我讲过很精辟的总结,把参数yaml文件看成是一直json文件。
例如cfg.MODEL.FCOS.NUM_CLASSES就可以在对应的yaml文件写成:

 1. MODEL:
     FCOS:
      NUM_CLASSES: 80

其实就是空2格的一种结构。detectron2使用这种yaml文件是非常方便你就不用写这么多了这么多了。
只要设置好对应的–config-file 配置yaml文件就好。
另外yaml文件是继承与覆盖的关系。如果要修改yaml文件。记得看清楚特殊的就在子类上修改。建议练手之前先copy一份。

#由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
  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)
  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.进行可视化

cd demo/
python demo.py --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml \
  --input input1.jpg input2.jpg \
  [--other-options]
  --opts MODEL.WEIGHTS detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl

result

[Detectron2/AdelaiDet]笔记 使用Detectron2/AdelaiDet直接修改对应文件训练自己的数据集_第1张图片

[Detectron2/AdelaiDet]笔记 使用Detectron2/AdelaiDet直接修改对应文件训练自己的数据集_第2张图片

4. 总结

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

你可能感兴趣的:(笔记)