MMDetection-配置文件
簡介
在本專欄的上一篇文章中,初步介紹了MMDetection的由來以及環境的配置,并提到了控制整個pipeline的配置文件,本文就來詳細聊一聊配置文件的點點滴滴。
配置文件結構
不同于Detectron2采用YAML文件作為配置文件的方式,MMDetection采用Python腳本作為配置文件,這一定程度上方便了解析。不過,在談具體的配置文件的結構之前,首先介紹一個官方提供的工具,它位于mmdetection根目錄的tools文件夾中,你可以在根目錄下通過python tools/misc/print_config.py /path/to/config_file來查看完整的配置,例如我們打印最基礎的faster r-cnn的配置,命令為python tools/misc/print_config.py configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py。
查看faster_rcnn_r50_fpn_1x_coco.py文件其實會發現,它只有如下的幾行內容,顯然,它是繼承了_base_的配置文件作為自己的配置的,而print_config.py則打印層層繼承后最終的配置文件內容,具體如下。
_base_ = ['../_base_/models/faster_rcnn_r50_fpn.py','../_base_/datasets/coco_detection.py','../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py' ] Config: model = dict(type='FasterRCNN',pretrained='torchvision://resnet50',backbone=dict(type='ResNet',depth=50,num_stages=4,out_indices=(0, 1, 2, 3),frozen_stages=1,norm_cfg=dict(type='BN', requires_grad=True),norm_eval=True,style='pytorch'),neck=dict(type='FPN',in_channels=[256, 512, 1024, 2048],out_channels=256,num_outs=5),rpn_head=dict(type='RPNHead',in_channels=256,feat_channels=256,anchor_generator=dict(type='AnchorGenerator',scales=[8],ratios=[0.5, 1.0, 2.0],strides=[4, 8, 16, 32, 64]),bbox_coder=dict(type='DeltaXYWHBBoxCoder',target_means=[0.0, 0.0, 0.0, 0.0],target_stds=[1.0, 1.0, 1.0, 1.0]),loss_cls=dict(type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),loss_bbox=dict(type='L1Loss', loss_weight=1.0)),roi_head=dict(type='StandardRoIHead',bbox_roi_extractor=dict(type='SingleRoIExtractor',roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),out_channels=256,featmap_strides=[4, 8, 16, 32]),bbox_head=dict(type='Shared2FCBBoxHead',in_channels=256,fc_out_channels=1024,roi_feat_size=7,num_classes=80,bbox_coder=dict(type='DeltaXYWHBBoxCoder',target_means=[0.0, 0.0, 0.0, 0.0],target_stds=[0.1, 0.1, 0.2, 0.2]),reg_class_agnostic=False,loss_cls=dict(type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),loss_bbox=dict(type='L1Loss', loss_weight=1.0))),train_cfg=dict(rpn=dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.7,neg_iou_thr=0.3,min_pos_iou=0.3,match_low_quality=True,ignore_iof_thr=-1),sampler=dict(type='RandomSampler',num=256,pos_fraction=0.5,neg_pos_ub=-1,add_gt_as_proposals=False),allowed_border=-1,pos_weight=-1,debug=False),rpn_proposal=dict(nms_across_levels=False,nms_pre=2000,nms_post=1000,max_num=1000,nms_thr=0.7,min_bbox_size=0),rcnn=dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.5,neg_iou_thr=0.5,min_pos_iou=0.5,match_low_quality=False,ignore_iof_thr=-1),sampler=dict(type='RandomSampler',num=512,pos_fraction=0.25,neg_pos_ub=-1,add_gt_as_proposals=True),pos_weight=-1,debug=False)),test_cfg=dict(rpn=dict(nms_across_levels=False,nms_pre=1000,nms_post=1000,max_num=1000,nms_thr=0.7,min_bbox_size=0),rcnn=dict(score_thr=0.05,nms=dict(type='nms', iou_threshold=0.5),max_per_img=100))) dataset_type = 'CocoDataset' data_root = 'data/coco/' img_norm_cfg = dict(mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) train_pipeline = [dict(type='LoadImageFromFile'),dict(type='LoadAnnotations', with_bbox=True),dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),dict(type='RandomFlip', flip_ratio=0.5),dict(type='Normalize',mean=[123.675, 116.28, 103.53],std=[58.395, 57.12, 57.375],to_rgb=True),dict(type='Pad', size_divisor=32),dict(type='DefaultFormatBundle'),dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']) ] test_pipeline = [dict(type='LoadImageFromFile'),dict(type='MultiScaleFlipAug',img_scale=(1333, 800),flip=False,transforms=[dict(type='Resize', keep_ratio=True),dict(type='RandomFlip'),dict(type='Normalize',mean=[123.675, 116.28, 103.53],std=[58.395, 57.12, 57.375],to_rgb=True),dict(type='Pad', size_divisor=32),dict(type='ImageToTensor', keys=['img']),dict(type='Collect', keys=['img'])]) ] data = dict(samples_per_gpu=2,workers_per_gpu=2,train=dict(type='CocoDataset',ann_file='data/coco/annotations/instances_train2017.json',img_prefix='data/coco/train2017/',pipeline=[dict(type='LoadImageFromFile'),dict(type='LoadAnnotations', with_bbox=True),dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),dict(type='RandomFlip', flip_ratio=0.5),dict(type='Normalize',mean=[123.675, 116.28, 103.53],std=[58.395, 57.12, 57.375],to_rgb=True),dict(type='Pad', size_divisor=32),dict(type='DefaultFormatBundle'),dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])]),val=dict(type='CocoDataset',ann_file='data/coco/annotations/instances_val2017.json',img_prefix='data/coco/val2017/',pipeline=[dict(type='LoadImageFromFile'),dict(type='MultiScaleFlipAug',img_scale=(1333, 800),flip=False,transforms=[dict(type='Resize', keep_ratio=True),dict(type='RandomFlip'),dict(type='Normalize',mean=[123.675, 116.28, 103.53],std=[58.395, 57.12, 57.375],to_rgb=True),dict(type='Pad', size_divisor=32),dict(type='ImageToTensor', keys=['img']),dict(type='Collect', keys=['img'])])]),test=dict(type='CocoDataset',ann_file='data/coco/annotations/instances_val2017.json',img_prefix='data/coco/val2017/',pipeline=[dict(type='LoadImageFromFile'),dict(type='MultiScaleFlipAug',img_scale=(1333, 800),flip=False,transforms=[dict(type='Resize', keep_ratio=True),dict(type='RandomFlip'),dict(type='Normalize',mean=[123.675, 116.28, 103.53],std=[58.395, 57.12, 57.375],to_rgb=True),dict(type='Pad', size_divisor=32),dict(type='ImageToTensor', keys=['img']),dict(type='Collect', keys=['img'])])])) evaluation = dict(interval=1, metric='bbox') optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001) optimizer_config = dict(grad_clip=None) lr_config = dict(policy='step',warmup='linear',warmup_iters=500,warmup_ratio=0.001,step=[8, 11]) total_epochs = 12 checkpoint_config = dict(interval=1) log_config = dict(interval=50, hooks=[dict(type='TextLoggerHook')]) dist_params = dict(backend='nccl') log_level = 'INFO' load_from = None resume_from = None workflow = [('train', 1)]這個配置文件稍稍有點長,它是使用ResNet50作為backbone并以FPN為neck的一個Faster R-CNN檢測器的配置,從最外層來看其實內容不多。首先是model,它包含了關于模型的所有配置,包括backbone、neck、head等等,具體的后面文章會專門講解;接著就是dataset的配置,包括數據集類型、數據加載pipeline、訓練集驗證集和測試集的加載配置等等;最后是runtime相關的東西,包括評估指標、優化器、學習率及其調度配置、訓練總輪數、工作流等等。
大體聊了聊配置文件的內容結構,再來回到原來的問題上,配置文件的組織結構,打開configs目錄的_base_文件夾,其實會看到dataset, model, schedule三個文件夾和default_runtime.py這四個基本組件,它們的內容也是顧名思義為數據集、模型,而schedule和runtime通常合并看作運行時,這個_base_文件夾的目錄樹如下。
. ├── datasets │ ├── cityscapes_detection.py │ ├── cityscapes_instance.py │ ├── coco_detection.py │ ├── coco_instance.py │ ├── coco_instance_semantic.py │ ├── deepfashion.py │ ├── lvis_v0.5_instance.py │ ├── lvis_v1_instance.py │ ├── voc0712.py │ └── wider_face.py ├── models │ ├── cascade_mask_rcnn_r50_fpn.py │ ├── cascade_rcnn_r50_fpn.py │ ├── faster_rcnn_r50_caffe_c4.py │ ├── faster_rcnn_r50_caffe_dc5.py │ ├── faster_rcnn_r50_fpn.py │ ├── fast_rcnn_r50_fpn.py │ ├── mask_rcnn_r50_caffe_c4.py │ ├── mask_rcnn_r50_fpn.py │ ├── retinanet_r50_fpn.py │ ├── rpn_r50_caffe_c4.py │ ├── rpn_r50_fpn.py │ └── ssd300.py ├── schedules │ ├── schedule_1x.py │ ├── schedule_20e.py │ └── schedule_2x.py └── default_runtime.py如上所示,datasets里包含了常見數據集的數據集配置(如COCO、VOC)、models里則是最基礎的一些模型結構的配置、schedules和default_runtime.py是常用的運行時配置。顯然,通過它們我們可以輕松構建一些新的檢測器配置,在這里作為被繼承對象的_base_里的內容稱為原始配置。
一般,當創建一個新的方法的時候,會創建以方法名為名的文件夾,里面放置各種存在不同之處的配置文件,如configs/faster_rcnn文件夾。官方推薦的是,對一個方法文件夾內最好只存在一個原始配置,其他的配置文件都繼承自它即可,如faster_rcnn下的配置文件均繼承自faster_rcnn_r50_fpn_1x_coco.py和faster_rcnn_r50_fpn_2x_coco.py,而它倆又繼承自_base_,因此,最大繼承等級為3。
官方推薦從現有的檢測器方法去創建新的方法,如對Faster R-CNN進行修改工作,那么在配置文件中指定_base_ = ../faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py,然后修改必要的字段即可。當然,絕大多是情況還是我上面所說的不與現有方法共享結構的新配置,這是就需要configs下新建方法文件夾了(注意,這是規范,不是必須)。
到這里,大體的配置文件結構就講完了,實際上這里使用的是mmcv的一個Config類進行封裝的,更詳細的內容可以參考文檔。
配置文件命名規范
官方建議的配置文件命名規范如下,其中用大括號括起來的是必須的,中括號內的則為可選內容,每個字段的說明也如下。
{model}_[model setting]_{backbone}_{neck}_[norm setting]_[misc]_[gpu x batch_per_gpu]_{schedule}_{dataset}- {model}: 模型名稱,如faster_rcnn。
- [model setting]: 特定的模型設置,如moment for reppoints。
- {backbone}: 骨干網絡名稱如r50 (ResNet-50), x101 (ResNeXt-101)。
- {neck}: 頸部網絡名稱如fpn, pafpn, nasfpn, c4。
- [norm_setting]: 標準化設置,默認為BN可選為 gn (Group Normalization), syncbn (Synchronized Batch Normalization),其中gn-head/gn-neck 表示GN僅僅用在head/neck中, 而gn-all意味著GN被用在整個模型中包括backbone、neck、head等。
- [misc]: 雜項設置或者模型插件, 如dconv, gcb, attention等。
- [gpu x batch_per_gpu]: GPU數目和每個GPU上放置樣本量,默認為8x2,表示有8張卡,每張卡放兩個樣本。
- {schedule}: 訓練計劃表,可選的是1x、2x、20e等。1x和2x表示單位輪數的幾倍(默認12輪為一個單位輪數),20e在級聯模型中使用,表示20輪。對于1x/2x,初始學習速率在8/16和11/22時衰減10倍。對于20e,初始學習速率在第16和第19個時期衰減10倍。
- {dataset}: 數據集名稱,如coco、voc_0712等。
VOC訓練示例
上文講解了配置文件的文件結構和命名規則,其中繼承機制尤為重要,這里只需要記住繼承內容后有要重寫的,只要覆蓋對應的參數即可(換句話說,只改要改的字典參數)。下面就構造用Faster R-CNN在VOC數據集上進行訓練的配置文件,Pascal VOC數據集是一個常用的目標檢測數據集,它的數據集格式的解析已經被MMDetection封裝了,所以可以直接用,只是數據集本地路徑需要修改。(關于全新的數據集格式則需要自己寫解析,或者將其處理為已有的數據集格式,這個后面的文章會詳細說明。)
關于VOC數據集,可以去官方下載,也可以評論獲取百度網盤鏈接,解壓后的數據集文件結構應該時如下的結構。
. └── VOCdevkit├── create_segmentations_from_detections.m├── devkit_doc.pdf├── example_action.m├── example_action_nobb.m├── example_classifier.m├── example_detector.m├── example_layout.m├── example_segmenter.m├── local│ ├── VOC2006│ ├── VOC2007│ └── VOC2012├── results│ ├── VOC2006│ ├── VOC2007│ └── VOC2012├── viewanno.m├── viewdet.m├── VOC2007│ ├── Annotations│ ├── ImageSets│ ├── JPEGImages│ ├── SegmentationClass│ └── SegmentationObject├── VOC2012│ ├── Annotations│ ├── ImageSets│ ├── JPEGImages│ ├── SegmentationClass│ └── SegmentationObject└── VOCcode├── PASemptyobject.m├── PASemptyrecord.m├── PASerrmsg.m├── PASreadrecord.m├── PASreadrectxt.m├── VOCap.m├── VOCevalaction.m├── VOCevalcls.m├── VOCevaldet.m├── VOCevallayout.m├── VOCevallayout_pr.m├── VOCevalseg.m├── VOChash_init.m├── VOChash_lookup.m├── VOCinit.m├── VOClabelcolormap.m├── VOCreadrecxml.m├── VOCreadxml.m├── VOCwritexml.m└── VOCxml2struct.m首先,我們要在configs/faster_rcnn/里新建faster_rcnn_r50_fpn_1x_voc0712.py文件,然后按部件分別繼承模型、數據集和運行時的內容,然后修改數據集配置文件中的同名項data_root及相關的一些配置即可,同時模型的最終類別頭的類別數也要修改,具體內容如下。
_base_ = ['../_base_/models/faster_rcnn_r50_fpn.py','../_base_/datasets/voc0712.py','../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py' ]model = dict(roi_head=dict(bbox_head=dict(num_classes=20)))dataset_type = 'VOCDataset' data_root = '自己的數據集根目錄' img_norm_cfg = dict(mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) train_pipeline = [dict(type='LoadImageFromFile'),dict(type='LoadAnnotations', with_bbox=True),dict(type='Resize', img_scale=(1000, 600), keep_ratio=True),dict(type='RandomFlip', flip_ratio=0.5),dict(type='Normalize', **img_norm_cfg),dict(type='Pad', size_divisor=32),dict(type='DefaultFormatBundle'),dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']), ] test_pipeline = [dict(type='LoadImageFromFile'),dict(type='MultiScaleFlipAug',img_scale=(1000, 600),flip=False,transforms=[dict(type='Resize', keep_ratio=True),dict(type='RandomFlip'),dict(type='Normalize', **img_norm_cfg),dict(type='Pad', size_divisor=32),dict(type='ImageToTensor', keys=['img']),dict(type='Collect', keys=['img']),]) ] data = dict(samples_per_gpu=2,workers_per_gpu=2,train=dict(type='RepeatDataset',times=3,dataset=dict(type=dataset_type,ann_file=[data_root + 'VOC2007/ImageSets/Main/trainval.txt',data_root + 'VOC2012/ImageSets/Main/trainval.txt'],img_prefix=[data_root + 'VOC2007/', data_root + 'VOC2012/'],pipeline=train_pipeline)),val=dict(type=dataset_type,ann_file=data_root + 'VOC2007/ImageSets/Main/test.txt',img_prefix=data_root + 'VOC2007/',pipeline=test_pipeline),test=dict(type=dataset_type,ann_file=data_root + 'VOC2007/ImageSets/Main/test.txt',img_prefix=data_root + 'VOC2007/',pipeline=test_pipeline)) evaluation = dict(interval=1, metric='mAP')然后我們使用python tools/train.py configs/faster_rcnn/faster_rcnn_r50_fpn_1x_voc0712.py --work-dir runs在單卡上進行訓練(需要注意的是,我這里沒有按照官方建議修改學習率)。由于我這邊是四卡的機器,因此我采用bash tools/dist_train.sh configs/faster_rcnn/faster_rcnn_r50_fpn_1x_voc0712.py 4 --work-dir ./runs/命令進行單機多卡訓練。
這也訓練的話,訓練日志默認保存在了項目根目錄下的work_dirs里面,找到對應的實驗,可以對其進行可視化分析,日志分析的模塊對應的是tools/analysis_tools/analyze_logs.py,我這里簡單進行了loss和mAP曲線可視化,如下圖。
總結
本文主要介紹了MMDetetion的配置文件相關的內容,這是組織各個組件的關鍵,也是學習這個框架必須了解的東西,這部分對應的官方文檔這里給出鏈接。最后,如果我的文章對你有所幫助,歡迎點贊收藏評論一鍵三連,你的支持是我不懈創作的動力。
總結
以上是生活随笔為你收集整理的MMDetection-配置文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: RTX3090深度学习环境配置(PyTo
- 下一篇: DetNet解读