목차
1. mmcv 2.x ver code flow 리뷰 ( 순한맛 )
1-1. faster rcnn train/ inference코드 리뷰 & Config 수정 (부스트 캠프 제공)
1-2. faster rcnn의 scheduler 잡지식
2. mmcv 2.x config 파일 사용법 ( 순한맛 )
2-1. cascade rcnn 사용법
2-2. ConvNext 사용법 (mask rcnn, fp16 error)
3. mmcv 외부 라이브러리 사용법 ( 안순한맛 ) // DINO, CoDETR은 MMCV 3.x ver
3-1. swin b / L 사용법
3-2. UniverseNet 사용법
3-3. FocalNet 사용법
4. Optional
4-1. wandb 사용법, 사용 안 하는 법
4-2. val 나누기 (random split, stratified group k fold)
4-3. loss function 바꾸기, cross entropy에 가중치 바꾸기, nms→soft-nms 바꾸기,
4-4. ensemble. nms→wbf로 추론 하기
4-5. correlation matrix hitmap 제작
4-6. val data에 bbox, gt_box 띄우기
faster_rcnn_train.ipynb 파일이다.
train
# 모듈 import
from mmcv import Config
from mmdet.datasets import build_dataset
from mmdet.models import build_detector
from mmdet.apis import train_detector
from mmdet.datasets import (build_dataloader, build_dataset,
replace_ImageToTensor)
from mmdet.utils import get_device
classes = ("General trash", "Paper", "Paper pack", "Metal", "Glass",
"Plastic", "Styrofoam", "Plastic bag", "Battery", "Clothing")
# config file 들고오기
cfg = Config.fromfile('./configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py')
root='../../../dataset/'
# dataset config 수정
cfg.data.train.classes = classes
cfg.data.train.img_prefix = root
cfg.data.train.ann_file = root + 'train_fold1.json' # train json 정보
cfg.data.train.pipeline[2]['img_scale'] = (512,512) # Resize
cfg.data.val.classes = classes
cfg.data.val.img_prefix = root
cfg.data.val.ann_file = root + 'val_fold1.json' # train json 정보
cfg.data.val.pipeline[2]['img_scale'] = (512,512) # Resize
cfg.data.test.classes = classes
cfg.data.test.img_prefix = root
cfg.data.test.ann_file = root + 'test.json' # test json 정보
cfg.data.test.pipeline[1]['img_scale'] = (512,512) # Resize
cfg.data.samples_per_gpu = 16
cfg.seed = 2022
cfg.gpu_ids = [0]
cfg.work_dir = './work_dirs/faster_rcnn_r50_fpn_1x_trash'
cfg.model.roi_head.bbox_head.num_classes = 10
cfg.optimizer_config.grad_clip = dict(max_norm=35, norm_type=2)
cfg.checkpoint_config = dict(max_keep_ckpts=3, interval=1)
cfg.device = get_device()
# build_dataset
datasets = [build_dataset(cfg.data.train)]
# dataset 확인
datasets[0]
# 모델 build 및 pretrained network 불러오기
model = build_detector(cfg.model)
model.init_weights()
# 모델 학습
train_detector(model, datasets, cfg, distributed=False, validate=False)
이렇게 구성 돼 있다. 아~ 어렵다 어려워.
이게 무슨 의미인지 하나하나 보자.
############### 여기서 부터 보자 ###############
from mmcv import Config
from mmdet.datasets import build_dataset
from mmdet.models import build_detector
from mmdet.apis import train_detector
from mmdet.datasets import (build_dataloader, build_dataset,
replace_ImageToTensor)
from mmdet.utils import get_device
classes = ("General trash", "Paper", "Paper pack", "Metal", "Glass",
"Plastic", "Styrofoam", "Plastic bag", "Battery", "Clothing")
# config file 들고오기
cfg = Config.fromfile('./configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py') # config파일 = 쓰기 쉽게 만들어둔 모델 = 그냥 갖다 쓰자
root='../../../dataset/' #train, test 데이터가 있는 폴더로 지정해주고 마지막에 / 를 더해주자.
# dataset config 수정
cfg.data.train.classes = classes
cfg.data.train.img_prefix = root
cfg.data.train.ann_file = root + 'train_fold1.json' # train json 정보 : json은 어떻게 구성되는지 다음 포스팅에서 다룸
cfg.data.train.pipeline[2]['img_scale'] = (512,512) # Resize #이제 config가 뭔지 관찰해보자.
cfg.data.test.classes = classes
cfg.data.test.img_prefix = root
cfg.data.test.ann_file = root + 'test.json' # test json 정보
cfg.data.test.pipeline[1]['img_scale'] = (512,512)# Resize
#cfg로 불러온 자료에서 data에 접근해서 test에 접근해서
#pipeline의 두 번째 인덱스에 접근해서 'img_scale' 딕셔너리 key에 접근해서 size를 변경하겠다는 거다.
cfg.data.samples_per_gpu = 64 # batch size다. 커맨드창에 nvidia-smi를 쳐서 gpu memory를 관찰하자.
cfg.seed = 2022
cfg.gpu_ids = [0] # 첫 번재 GPU를 쓰겠다는 뜻. 다중 GPU를 쓴다면 변경해주자.
cfg.work_dir = './work_dirs/faster_rcnn_r50_fpn_1x_trash' # epoch마다 훈련 weight 자료인 pth를 저장하는 위치다.
cfg.model.roi_head.bbox_head.num_classes = 10 # 이번 대회는 10개 class로 분류해야한다. 마지막 FN층을 정의한다.
cfg.optimizer_config.grad_clip = dict(max_norm=35, norm_type=2) # 급격한 grad를 보정해준다. 물론 35를 넘지 않는 거는 아니다. 억제하는 정도인 것 같다.
cfg.checkpoint_config = dict(max_keep_ckpts=3, interval=1) # morm_type=2는 L2 loss다. max_keep_ckpts=3는 30epoch 훈련시킨다면 28,29,30 epoch의 pth만 저장한다.
cfg.device = get_device()
이제 cfg = Config.fromfile('./configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py') 에서
이 주소로 따라 들어가서 모델이 어떻게 선언돼있는지 살펴보자.
먼저 살펴볼 곳은
cfg.model.roi_head.bbox_head.num_classes = 10 과
cfg.data.train.pipeline[2]['img_scale'] = (512,512) 부분이다.
./configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py 로 가자.
여기로 들어가면 덩그러니 이것만 적혀있다.
4개의 요소가 있다.
models → 모델을 저기서 정의해놨다.
datasets → 데이터 받아오는 방식
schedules → learning rate 조절, optimizer 선언
default_runtime → 음..(?) // 나중에 val 나눌 때 접근할 일이 생긴다.
cfg.model.roi_head.bbox_head.num_classes = 10 이걸 보면, model에 접근한다.
models에 들어가자
여기서 model에서 roi_head에 접근해서 bbox_head에 접근해서 num_classes의 값을 10으로 바꾸는 거다.
밑에 코드를 하나씩 따라가보자.
뭔가 많이 선언 돼있다.
# model settings
model = dict(
type='FasterRCNN',
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',
init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),
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],
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.],
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))),
# model training and testing settings
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_pre=2000,
max_per_img=1000,
nms=dict(type='nms', iou_threshold=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_pre=1000,
max_per_img=1000,
nms=dict(type='nms', iou_threshold=0.7),
min_bbox_size=0),
rcnn=dict(
score_thr=0.05,
nms=dict(type='nms', iou_threshold=0.5),
max_per_img=100)
# soft-nms is also supported for rcnn testing
# e.g., nms=dict(type='soft_nms', iou_threshold=0.5, min_score=0.05)
))
이제 대충 코드가 눈에 보일 것 같다.
num_classes는 바꿨고
cfg.data.train.pipeline[2]['img_scale'] = (512,512) 이걸 찾는데 없다.
이번에는 base에 있는 datasets에 들어가자
바로 img_scale을 선언하는 부분이 있다. 그런데 어떻게 접근하는지 잘 보자.
# dataset settings
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', **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=(1333, 800),
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=dataset_type,
ann_file=data_root + 'annotations/instances_train2017.json',
img_prefix=data_root + 'train2017/', #### cfg 로 이 부분도 수정하고 있을 거다! ####
pipeline=train_pipeline),
val=dict(
type=dataset_type,
ann_file=data_root + 'annotations/instances_val2017.json',
img_prefix=data_root + 'val2017/',
pipeline=test_pipeline),
test=dict(
type=dataset_type,
ann_file=data_root + 'annotations/instances_val2017.json',
img_prefix=data_root + 'val2017/',
pipeline=test_pipeline))
evaluation = dict(interval=1, metric='bbox')
나는 멍청해서 이것도 이해하는데 한참 걸렸었다.
나같은 사람은 세상에 많을 테니 그림으로 하나 더 얹어놨다.
cfg.data.train.pipeline[2]['img_scale'] = (512,512) 이제 이해할 수 있다 !
cfg로 faster_rcnn_r50_fpn_1x_coco.py 파일에 접근했고
_base_로 coco_detection.py 파일에 접근해서
data에 접근하고
train에 접근하고
pipeline = train_pipeline에 접근해서
리스트 2번째 딕셔너리에 접근해서
'img_scale' key에 접근하는 거다.
이제 모델 수정 부분은 마쳤다.
이제 daseline의 아래쪽을 좀 더 보자
datasets = [build_dataset(cfg.data.train)] # data.train에 저장된 root경로와 augmentation 방법을 가져온다
datasets[0]
model = build_detector(cfg.model)
model.init_weights() # pretrain 된 pth를 가져오는데 모델과 pth의 깊이나 형태가 다르면 메세지가 뜬다
train_detector(model, datasets[0], cfg, distributed=False, validate=False) 이걸 실행하면 훈련된다.
순한맛 faster rcnn train의 코드 리뷰, config 뜯어보기는 끝
이번에는 inference를 보자
inference
import mmcv
from mmcv import Config
from mmdet.datasets import (build_dataloader, build_dataset,
replace_ImageToTensor)
from mmdet.models import build_detector
from mmdet.apis import single_gpu_test
from mmcv.runner import load_checkpoint
import os
from mmcv.parallel import MMDataParallel
import pandas as pd
from pandas import DataFrame
from pycocotools.coco import COCO
import numpy as np
classes = ("General trash", "Paper", "Paper pack", "Metal", "Glass",
"Plastic", "Styrofoam", "Plastic bag", "Battery", "Clothing")
# config file 들고오기
cfg = Config.fromfile('./configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py') # 같은 cfg를 불러온다
root='../../../dataset/'
epoch = 'latest' # 이건 './work_dirs/faster_rcnn_r50_fpn_1x_trash' 이 주소에 저장된 pth의 이름 !
# dataset config 수정
cfg.data.test.classes = classes
cfg.data.test.img_prefix = root
cfg.data.test.ann_file = root + 'test.json'
cfg.data.test.pipeline[1]['img_scale'] = (512,512) # Resize
cfg.data.test.test_mode = True
cfg.data.samples_per_gpu = 4
cfg.seed=2021
cfg.gpu_ids = [1]
cfg.work_dir = './work_dirs/faster_rcnn_r50_fpn_1x_trash'
cfg.model.roi_head.bbox_head.num_classes = 10
cfg.optimizer_config.grad_clip = dict(max_norm=35, norm_type=2)
cfg.model.train_cfg = None
# build dataset & dataloader
dataset = build_dataset(cfg.data.test)
data_loader = build_dataloader(
dataset,
samples_per_gpu=1,
workers_per_gpu=cfg.data.workers_per_gpu,
dist=False,
shuffle=False)
# checkpoint path
checkpoint_path = os.path.join(cfg.work_dir, f'{epoch}.pth') # 여기서 원하는 pth파일이 들어가진다!
model = build_detector(cfg.model, test_cfg=cfg.get('test_cfg')) # build detector
checkpoint = load_checkpoint(model, checkpoint_path, map_location='cpu') # ckpt load
model.CLASSES = dataset.CLASSES
model = MMDataParallel(model.cuda(), device_ids=[0])
output = single_gpu_test(model, data_loader, show_score_thr=0.05)
설명할 게 없다. 끝
'Lectures > BoostCamp -Naver' 카테고리의 다른 글
2.1 mmdetection cascade rcnn config 사용법 (0) | 2024.01.20 |
---|---|
1-2. mmdetection faster rcnn의 scheduler 잡지식 (0) | 2024.01.20 |
level 2 Detection 대회 정리 (0) | 2024.01.20 |
mmdetection (2.x ver) 사용법 총정리 (mmcf-full==1.6.2, mmcv=2.1.0) (0) | 2024.01.19 |
level 1 classification 대회 정리 (down sampling, targeted augmentation) (0) | 2024.01.19 |
댓글