MMCV에서 직접 만든 데이터셋 학습 시키기(COCO, Pascal VOC Custom Dataset 만들기)

프로그래밍 팁 2023. 3. 27. 22:58

 

 제가 인공지능에 대해 한창 공부를 할때마다 가장 아쉬웠던 점 중 하나는 자신이 구현하고자 하는 모델에 적합한 데이터를 구하는 것이 어려웠던 것이었습니다. 단지 사람을 감지해내는 기술이 필요한 것이라면 SOTA 알고리즘을 찾은 다음 고성능의 기학습된 모델을 적용하는 것이 가장 좋고 실제로도 제가 다시 재학습 해서 만든 모델보다도 성능이 뛰어나기 때문에 굳이 데이터셋을 직접 만드는 고생을 할 이유가 없기도 합니다.

 

 그러나 내가 구현하고자 하는 물체가 기존의 모델들에 구현되어 있지 않는 경우에는 어쩔수없이 데이터를 수집하여 나만의 데이터셋을 만들어야만합니다. 그러나 기존의 모델들의 성능에 준하는 새로운 모델을 만들기 위해서는 무려 수십만장의 데이터를 수집해야 하는데 이를 개인이 직접 수집하기에는 시간과 비용이 만만치 않습니다. 

 

 그렇다면, 내가 원하는 데이터셋을 만들어 이를 테스트해 볼 수 있는 방법은 없는걸까요? 이번 포스팅에서는 Custom Dataset을 본인이 직접 제작하는 방법과 이 데이터셋을 사용하여 MMCV 기반 딥러닝 모델(MMDetection, MMSegmentation)에서 Custom Dataset을 학습해보고자 합니다.

 

 

1. 데이터 수집 및 Labeling

 원하는 모델 설계에 앞서 자신이 감지하고자 하는 물체가 실제 학습이 가능한지 우선 확인해 볼 필요가 있습니다. 우선 자신이 학습하고자 하는 이미지를 100장 정도 수집한 후 Labeling 작업을 수행합니다. 최근에는 Labeling 작업을 쉽고 효율적으로 수행할 수 있는 Tool들이 많아졌지만 Tutorial 목적으로 작업을 하고자 하시는 분들께서는 추가 비용을 요구하지 않는 오픈소스인 LableMe를 사용해보는 것을 추천 드립니다.

 

 LabelMe를 사용하는 방법에 대해 제가 이전에 정리하였던 자료가 있으니 아래의 포스팅을 참조해주시길 바랍니다.

https://elecs.tistory.com/430

 

Lableme로 Coco Dataset과 VOC Dataset 만들기

고성능의 인공지능을 만들기 위한 가장 중요한 작업 중 하나는 최대한 많은 양질의 데이터를 확보하는 것입니다. 상상을 뛰어넘는 인공지능의 놀라운 발전을 보았을 때 마치 AI를 연구하는 사람

elecs.tistory.com

2. 보편적으로 사용되는 Custom Dataset 양식으로 변환

 

 지금까지 공개된 인공지능 모델들의 대부분은 많은 사람들이 자신들이 개발한 소스코드를 쉽게 사용할 수 있도록 설계되어 있습니다. 특히 기존에 공개된 COCO 데이터셋과 PascalVOC는 대중적으로 공개된 퍼블릭 데이터셋으로 누구가 쉽게 접할 수 있어 인공지능에 입문하는 사람들이 처음으로 사용하게 되는 데이터셋이기도 합니다. 

 

 위에서 언급한 제 이전의 포스팅 자료에서 소개드렸던 LabelMe로 Labeling 작업을 수행한 다음 COCO 혹은 PascalVOC 데이터셋 양식으로 변경하는 내용에 대해 다루었으니 해당 부분을 참조해주시길 바랍니다.

 

3. MMCV에서 사용하기

 

 위의 과정을 통해 Custom 데이터셋을 만드셨다면 이를 가지고 OpenMMLab에서 제작한 MMCV에서 바로 사용하실 수 있습니다. LableMe를 통해 제작한 Custom Dataset으로 MMDetection과 MMSegmentation에서의 동작을 확인하였습니다. 각 프로젝트에 바로 사용할 수 있는 데이터셋 양식은 다음과 같습니다. 나머지 데이터셋 양식의 경우 추가적인 설정을 해주시면 사용하실 수 있습니다.

 

MMDetection: COCO Dataset

 

 LabelMe를 기준으로 Custom Dataset을 Coco 방식으로 변활할 때 Label을 수행한 Json 파일을 Train 용도와 Test 용도로 구분한 다음 각 용도별 Dataset을 Coco로 변환하면 Train 그리고 Test 수행을 위한 Annotation.json 파일을 생성할 수 있습니다. 

 위의 과정을 통해 생성된 Coco Dataset은 MMDetection에서 제공되는 config 파일에 있는 기본 dataset을 수정하여 자신의 환경에 맞도록 고쳐주시면 바로 사용하실 수 있습니다. 

 

/mmdetection/configs/_base_/datasets/coco*.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# dataset settings
dataset_type = 'CocoDataset'
data_root = 'data/coco/' #Custon Coco Dataset의 폴더 위치
....
#Train, Val, Test 데이터의 Json 및 이미지 파일 경로 
data = dict(
    samples_per_gpu=2,
    workers_per_gpu=2,
    train=dict(
        type=dataset_type,
        ann_file=data_root + 'annotations/train.json',
        img_prefix=data_root + 'train/',
        pipeline=train_pipeline),
    val=dict(
        type=dataset_type,
        ann_file=data_root + 'annotations/val.json',
        img_prefix=data_root + 'val/',
        pipeline=test_pipeline),
    test=dict(
        type=dataset_type,
        ann_file=data_root + 'annotations/test.json',
        img_prefix=data_root + 'test/',
        pipeline=test_pipeline))
....
cs

 

MMSegmentation: PascalVOC

 

 LableMe에서 labelme2voc로 변환한 데이터들은 MMSegmentation에서 바로 사용하실 수 있습니다. 여기서 만들어진 Segmentation 이미지들 중에서 train.txt, val.txt, test.txt로 사용하고자 하는 파일들의 제목들을 작성해주시고 아래와 같이 Custom Dataset으로 구성하실 수 있습니다.

 

/mmsegmentation/configs/_base_/datasets/custom_voc12.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# dataset settings
dataset_type = 'PascalVOCDataset'
data_root = 'data/VOCdevkit/VOC2012' # Custom Voc 데이터셋의 폴더 위치
img_norm_cfg = dict(
    mean=[123.675116.28103.53], std=[58.39557.1257.375], to_rgb=True)
crop_size = (512512)
train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations'),
    dict(type='Resize', img_scale=(2048512), ratio_range=(0.52.0)),
    dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
    dict(type='RandomFlip', prob=0.5),
    dict(type='PhotoMetricDistortion'),
    dict(type='Normalize'**img_norm_cfg),
    dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
    dict(type='DefaultFormatBundle'),
    dict(type='Collect', keys=['img''gt_semantic_seg']),
]
test_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(
        type='MultiScaleFlipAug',
        img_scale=(2048512),
        # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
        flip=False,
        transforms=[
            dict(type='Resize', keep_ratio=True),
            dict(type='RandomFlip'),
            dict(type='Normalize'**img_norm_cfg),
            dict(type='ImageToTensor', keys=['img']),
            dict(type='Collect', keys=['img']),
        ])
]
data = dict(
    samples_per_gpu=4,
    workers_per_gpu=4,
    train=dict(
        type=dataset_type,
        data_root=data_root,
        img_dir='JPEGImages',
        ann_dir='SegmentationClassPNG',
        split='train.txt',
        pipeline=train_pipeline),
    val=dict(
        type=dataset_type,
        data_root=data_root,
        img_dir='JPEGImages',
        ann_dir='SegmentationClassPNG',
        split='val.txt',
        pipeline=test_pipeline),
    test=dict(
        type=dataset_type,
        data_root=data_root,
        img_dir='JPEGImages',
        ann_dir='SegmentationClassPNG',
        split='test.txt',
        pipeline=test_pipeline))
cs

 

300x250