MMSegmentation - 02

이승민·2024년 4월 5일

MMCV

목록 보기
2/3

안녕하세요. 이 글에서는 MMSegmentation의 Config 파일에 대해 알아보겠습니다.

Config 파일이란 MMSegmentation에서 다양한 실험을 수행하기 편리하게 만들어 둔 디자인입니다.

Config 파일 예시
mmsegmentation/configs/pspnet/pspnet_r50-d8_4xb2-40k_cityscapes-512x1024.py

_base_ = [
    '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/cityscapes.py',
    '../_base_/default_runtime.py', '../_base_/schedules/schedule_40k.py'
]
crop_size = (512, 1024)
data_preprocessor = dict(size=crop_size)
model = dict(data_preprocessor=data_preprocessor)

Config File Structure

위와 같이 Config 파일에서는 models, datasets, schedules, default_runtime.py 총 네 개의 구성을 사용합니다.

이전 글에서 mmsegmentation을 설치하셨다면, mmsegmentation 폴더를 확인할 수 있습니다.

mmsegmentation/configs/_base_/datasets
mmsegmentation/configs/_base_/models
mmsegmentation/configs/_base_/schedules
mmsegmentation/configs/_base_/default_runtime.py

해당 경로에는 datasets, models, schedules, default_runtime.py 네 개의 구성이 있습니다. 이것을 원시(primitive) 설정이라고 합니다.

또한 MMCV 연구실은 기존 방법을 상속하는 것을 권장합니다. 예를 들어 DeepLabv3 모델을 사용하고 싶다면, 아래와 같이 config 파일을 상속한 후 설정 파일을 수정하는 것을 권합니다.

만약 새로운 모델을 추가하고 싶다면 configs 하위에 xxxnet 폴더를 만들어 직접 추가해야 합니다. 이 부분은 다음에 다루도록 하겠습니다.

Config Name Style

Config 파일의 이름은 정해진 디자인을 따릅니다.
이름의 구성은 algorithm name, model component names, training settings, training dataset information, testing dataset information으로 되어 있습니다.

mmsegmentation/configs/pspnet/pspnet_r50-d8_4xb2-40k_cityscapes-512x1024.py
  • algorithm name : deeplabv3, pspnet
  • model component names : r50-d8
  • training settings : 4xb4-ce-linearlr-40k
  • training dataset information : cityscapes-512x1024
  • testing dataset information (optional)

이를 각각 보자면

r50-d8 : ResNet50 백본 사용, 백본의 출력을 8배 다운샘플링하여 입력으로 사용)
4xb4 : 4 gpus x 4 images per gpu (= gpus x batch per gpu)
ce : CrossEntropy loss
linearlr : Linear learning rate scheduler
40k : train 40k iterations
cityscapes-512x1024 : Cityscapes 데이터셋 사용 입력 사이즈 512x1024

testing dataset information 언급이 없다면, train 데이터셋과 같은 데이터셋으로 테스트되었음을 의미합니다.

An Example of PSPNet

Config 파일

Config 파일은 mmsegmentation/configs/pspnet 경로의 pspnet_r50-d8_4xb2-40k_cityscapes-512x1024.py 파일을 사용합니다.

mmsegmentation/configs/pspnet/pspnet_r50-d8_4xb2-40k_cityscapes-512x1024.py

_base_ = [
    '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/cityscapes.py',
    '../_base_/default_runtime.py', '../_base_/schedules/schedule_40k.py'
] # 새로운 설정 파일을 만들기 위해 기존의 기본 설정 파일들을 포함합니다.
crop_size = (512, 1024)
data_preprocessor = dict(size=crop_size)
model = dict(data_preprocessor=data_preprocessor)

Component 1 - Models

이제 각각의 구성 파일을 살펴봅시다.

mmsegmentation/configs/base/models/pspnet_r50-d8.py 파일을 열어보겠습니다.

# 모델 설정

norm_cfg = dict(type='SyncBN', requires_grad=True)  
# 세그멘테이션은 일반적으로 SyncBN을 사용합니다.

data_preprocessor = dict(  
# 데이터 전처리기의 설정, 일반적으로 이미지 정규화와 augmentation을 포함합니다.
    type='SegDataPreProcessor',  # 데이터 전처리기의 유형.
    mean=[123.675, 116.28, 103.53],  # 입력 이미지를 정규화하는 데 사용되는 평균 값.
    std=[58.395, 57.12, 57.375],  # 입력 이미지를 정규화하는 데 사용되는 표준 편차.
    bgr_to_rgb=True,  # 이미지를 BGR에서 RGB로 변환할 지 여부.
    pad_val=0,  # 이미지의 패딩 값.
    seg_pad_val=255)  # 세그멘테이션 맵의 패딩 값.

model = dict(
    type='EncoderDecoder',  # 세그멘터의 이름
    data_preprocessor=data_preprocessor,
    pretrained='open-mmlab://resnet50_v1c',  # 로드할 ImageNet 사전 훈련된 백본
    backbone=dict(
        type='ResNetV1c',  # 백본의 유형. 자세한 내용은 mmseg/models/backbones/resnet.py를 참조하십시오.
        depth=50,  # 백본의 깊이. 보통 50, 101이 사용됩니다.
        num_stages=4,  # 백본의 스테이지 수.
        out_indices=(0, 1, 2, 3),  # 각 스테이지에서 생성된 출력 피쳐 맵의 인덱스.
        dilations=(1, 1, 2, 4),  # 각 레이어의 확장 비율.
        strides=(1, 2, 1, 1),  # 각 레이어의 스트라이드.
        norm_cfg=norm_cfg,  # norm 레이어의 구성.
        norm_eval=False,  # BN의 통계를 얼릴지 여부
        style='pytorch',  # 백본의 스타일, 'pytorch'는 stride 2 레이어가 3x3 conv에 있음을 의미합니다.
        contract_dilation=True),  # dilation > 1인 경우 첫 번째 레이어를 contract할 지 여부.
    decode_head=dict(
        type='PSPHead',  # decode head의 유형. 가능한 옵션은 mmseg/models/decode_heads를 참조하십시오.
        in_channels=2048,  # decode head의 입력 채널.
        in_index=3,  # 선택할 피쳐 맵의 인덱스.
        channels=512,  # decode head의 중간 채널.
        pool_scales=(1, 2, 3, 6),  # PSPHead의 avg pooling 스케일.
        dropout_ratio=0.1,  # 최종 분류 레이어 전의 드롭아웃 비율.
        num_classes=19,  # 세그멘테이션 클래스의 수. 일반적으로 cityscapes에는 19개, VOC에는 21개, ADE20k에는 150개가 있습니다.
        norm_cfg=norm_cfg,  # norm 레이어의 구성.
        align_corners=False,  # 디코딩 중 리사이즈에 대한 align_corners 인자.
        loss_decode=dict(  # decode_head의 손실 함수 구성.
            type='CrossEntropyLoss',  # 세그멘테이션에 사용되는 손실의 유형.
            use_sigmoid=False,  # 세그멘테이션에 시그모이드 활성화를 사용할 지 여부.
            loss_weight=1.0)),  # decode_head의 손실 가중치.
    auxiliary_head=dict(
        type='FCNHead',  # 보조 헤드의 유형. 가능한 옵션은 mmseg/models/decode_heads를 참조하십시오.
        in_channels=1024,  # 보조 헤드의 입력 채널.
        in_index=2,  # 선택할 피쳐 맵의 인덱스.
        channels=256,  # 보조 헤드의 중간 채널.
        num_convs=1,  # FCNHead에서의 conv 수. 일반적으로 보조 헤드에서는 1입니다.
        concat_input=False,  # 분류 레이어 전에 conv의 출력을 입력과 연결할지 여부.
        dropout_ratio=0.1,  # 최종 분류 레이어 전의 드롭아웃 비율.
        num_classes=19,  # 세그멘테이션 클래스의 수. 일반적으로 cityscapes에는 19개, VOC에는 21개, ADE20k에는 150개가 있습니다.
        norm_cfg=norm_cfg,  # norm 레이어의 구성.
        align_corners=False,  # 디코딩 중 리사이즈에 대한 align_corners 인자.
        loss_decode=dict(  # 보조_head의 손실 함수 구성.
            type='CrossEntropyLoss',  # 세그멘테이션에 사용되는 손실의 유형.
            use_sigmoid=False,  # 세그멘테이션에 시그모이드 활성화를 사용할 지 여부.
            loss_weight=0.4)),  # 보조_head의 손실 가중치.
    # 모델 훈련 및 테스트 설정
    train_cfg=dict(),  # 현재는 train_cfg는 그저 플레이스 홀더입니다.
    test_cfg=dict(mode='whole'))  # 테스트 모드. 'whole' 및 'slide' 옵션이 있습니다. 'whole': 전체 이미지 fully-convolutional test. 'slide': 이미지에 슬라이딩 크롭 윈도우를 적용합니다.

Component 2 - Datasets

# 데이터셋 설정

dataset_type = 'CityscapesDataset'  # 데이터셋 타입, 이것은 데이터셋을 정의하는 데 사용됩니다.

data_root = 'data/cityscapes/'  # 데이터의 루트 경로.

crop_size = (512, 1024)  # 훈련 중 크롭 크기.

train_pipeline = [  # 훈련 파이프라인.
    dict(type='LoadImageFromFile'),  # 파일 경로에서 이미지를 로드하는 첫 번째 파이프라인.
    dict(type='LoadAnnotations'),  # 현재 이미지에 대한 어노테이션을 로드하는 두 번째 파이프라인.
    dict(type='RandomResize',  # 이미지 및 어노테이션 크기를 조절하는 증강 파이프라인.
        scale=(2048, 1024),  # 이미지의 스케일.
        ratio_range=(0.5, 2.0),  # 증강된 스케일 범위.
        keep_ratio=True),  # 이미지의 종횡비를 유지할지 여부.
    dict(type='RandomCrop',  # 현재 이미지에서 무작위로 패치를 자르는 증강 파이프라인.
        crop_size=crop_size,  # 패치의 크기.
        cat_max_ratio=0.75),  # 단일 카테고리가 차지할 수 있는 최대 영역 비율.
    dict(type='RandomFlip',  # 이미지 및 어노테이션을 뒤집는 증강 파이프라인
        prob=0.5),  # 뒤집기의 비율 또는 확률
    dict(type='PhotoMetricDistortion'),  # 여러 사진 메트릭 방법으로 현재 이미지를 왜곡하는 증강 파이프라인.
    dict(type='PackSegInputs')  # 시맨틱 세그멘테이션을 위한 입력 데이터를 패킹합니다.
]

test_pipeline = [
    dict(type='LoadImageFromFile'),  # 파일 경로에서 이미지를 로드하는 첫 번째 파이프라인
    dict(type='Resize',  # 리사이즈 증강을 사용합니다.
        scale=(2048, 1024),  # 이미지의 크기 조정.
        keep_ratio=True),  # 이미지의 종횡비를 유지할지 여부.
    # ``Resize`` 이후에 로딩 어노테이션을 추가합니다. 왜냐하면
    # ground truth가 resize 데이터 변환을 하지 않기 때문입니다.
    dict(type='LoadAnnotations'),  # 데이터셋에서 제공하는 시맨틱 세그멘테이션을 로드합니다.
    dict(type='PackSegInputs')  # 시맨틱 세그멘테이션을 위한 입력 데이터를 패킹합니다.
]

train_dataloader = dict(  # 훈련 데이터로더 설정
    batch_size=2,  # 단일 GPU의 배치 크기
    num_workers=2,  # 각 단일 GPU에 대해 데이터를 미리 가져오는 워커
    persistent_workers=True,  # 에폭 종료 후 워커 프로세스를 종료하여 훈련 속도를 가속화할 수 있습니다.
    sampler=dict(type='InfiniteSampler', shuffle=True),  # 훈련 중에 무작위로 섞습니다.
    dataset=dict(  # 훈련 데이터셋 설정
        type=dataset_type,  # 데이터셋 유형, 세부 사항은 mmseg/datasets/를 참조하십시오.
        data_root=data_root,  # 데이터셋의 루트.
        data_prefix=dict(
            img_path='leftImg8bit/train', seg_map_path='gtFine/train'),  # 훈련 데이터에 대한 접두사.
        pipeline=train_pipeline)) # 처리 파이프라인. 이것은 이전에 생성된 train_pipeline로 전달됩니다.

val_dataloader = dict(
    batch_size=1,  # 단일 GPU의 배치 크기
    num_workers=4,  # 각 단일 GPU에 대해 데이터를 미리 가져오는 워커
    persistent_workers=True,  # 에폭 종료 후 워커 프로세스를 종료하여 테스트 속도를 가속화할 수 있습니다.
    sampler=dict(type='DefaultSampler', shuffle=False),  # 검증 및 테스트 중에는 섞지 않습니다.
    dataset=dict(  # 테스트 데이터셋 설정
        type=dataset_type,  # 데이터셋 유형, 세부 사항은 mmseg/datasets/를 참조하십시오.
        data_root=data_root,  # 데이터셋의 루트.
        data_prefix=dict(
            img_path='leftImg8bit/val', seg_map_path='gtFine/val'),  # 테스트 데이터에 대한 접두사.
        pipeline=test_pipeline))  # 처리 파이프라인. 이것은 이전에 생성된 test_pipeline로 전달됩니다.

test_dataloader = val_dataloader

# 정확도를 측정하는 메트릭. 여기서는 IoUMetric을 사용합니다.
val_evaluator = dict(type='IoUMetric', iou_metrics=['mIoU'])
test_evaluator = val_evaluator

Component 3 - Schedules

# 옵티마이저
optimizer = dict(type='SGD',  # 옵티마이저의 유형, 자세한 내용은 https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/optimizer/default_constructor.py를 참조하십시오.
                lr=0.01,  # 옵티마이저의 학습률, PyTorch 문서에서 매개변수의 세부 사용법을 확인하십시오.
                momentum=0.9,  # 모멘텀
                weight_decay=0.0005)  # SGD의 가중치 감소

optim_wrapper = dict(type='OptimWrapper',  # Optimizer wrapper는 매개변수를 업데이트하는 데 공통 인터페이스를 제공합니다.
                    optimizer=optimizer,  # 모델 매개변수를 업데이트하는 데 사용되는 옵티마이저.
                    clip_grad=None)  # ``clip_grad``가 None이 아닌 경우, ``torch.nn.utils.clip_grad``의 인수가 됩니다.

# 학습 정책
param_scheduler = [
    dict(
        type='PolyLR',  # 스케줄러의 정책, Step, CosineAnnealing, Cyclic 등도 지원됩니다. 지원되는 LrUpdater의 세부사항은 https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/scheduler/lr_scheduler.py를 참조하십시오.
        eta_min=1e-4,  # 스케줄링 종료시 최소 학습률.
        power=0.9,  # 다항식 감소의 지수.
        begin=0,  # 매개변수 업데이트를 시작하는 단계.
        end=40000,  # 매개변수 업데이트를 중지하는 단계.
        by_epoch=False)  # 에포크 별로 계산할지 여부.
]

# 40k 반복에 대한 훈련 일정
train_cfg = dict(type='IterBasedTrainLoop', max_iters=40000, val_interval=4000)
val_cfg = dict(type='ValLoop')
test_cfg = dict(type='TestLoop')

# 기본 후크
default_hooks = dict(
    timer=dict(type='IterTimerHook'),  # 반복 중 소요 시간을 기록합니다.
    logger=dict(type='LoggerHook', interval=50, log_metric_by_epoch=False),  # ``Runner``의 다른 구성 요소에서 로그를 수집하고 기록합니다.
    param_scheduler=dict(type='ParamSchedulerHook'),  # optimizer의 일부 하이퍼파라미터를 업데이트합니다. 예: 학습률.
    checkpoint=dict(type='CheckpointHook', by_epoch=False, interval=4000),  # 일정 간격으로 체크포인트를 저장합니다.
    sampler_seed=dict(type='DistSamplerSeedHook'))  # 분산 훈련용 데이터 로딩 샘플러입니다.

Component 4 - default_runtime.py

# 레지스트리의 기본 범위를 mmseg로 설정합니다.
default_scope = 'mmseg'

# 환경
env_cfg = dict(
    cudnn_benchmark=True,  # cudnn benchmark 활성화
    mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),  # 다중 프로세스 구성
    dist_cfg=dict(backend='nccl'),  # 분산 학습 설정
)

log_level = 'INFO'  # 로그 레벨 설정
log_processor = dict(by_epoch=False)  # 로그 프로세서 설정
load_from = None  # 파일에서 체크포인트 로드하지 않음
resume = False  # 기존 모델에서 재개할지 여부

Config 확인하기

MMEngine에서 Config를 확인하기 위해 아래와 같은 코드를 사용할 수 있습니다.

from mmengine.config import Config

cfg = Config.fromfile('configs/pspnet/pspnet_r50-d8_4xb2-40k_cityscapes-512x1024.py')
print(cfg.train_dataloader)

위의 코드를 복사하여 임의로 config_test.py를 만들었습니다.
아래는 그 출력창입니다.

{'batch_size': 2,
 'num_workers': 2,
 'persistent_workers': True,
 'sampler': {'type': 'InfiniteSampler', 'shuffle': True},
 'dataset': {'type': 'CityscapesDataset',
  'data_root': 'data/cityscapes/',
  'data_prefix': {'img_path': 'leftImg8bit/train',
   'seg_map_path': 'gtFine/train'},
  'pipeline': [{'type': 'LoadImageFromFile'},
   {'type': 'LoadAnnotations'},
   {'type': 'RandomResize',
    'scale': (2048, 1024),
    'ratio_range': (0.5, 2.0),
    'keep_ratio': True},
   {'type': 'RandomCrop', 'crop_size': (512, 1024), 'cat_max_ratio': 0.75},
   {'type': 'RandomFlip', 'prob': 0.5},
   {'type': 'PhotoMetricDistortion'},
   {'type': 'PackSegInputs'}]}}

cfg는 mmengine.config.Config의 Instance 입니다. 자세한 내용은 MMEngine 구성 튜토리얼을 참조할 수 있습니다.

또한 cfg의 사용은 mmsegmentation/tools/train.py 경로에서 일부 찾을 수 있습니다.

mmsegmentation/tools/train.py

...
def main():
    args = parse_args()

    # load config
    cfg = Config.fromfile(args.config)
    cfg.launcher = args.launcher
    if args.cfg_options is not None:
        cfg.merge_from_dict(args.cfg_options)
...

Config 파일을 검사하기 위해 아래의 코드를 터미널에서 실행할 수 있다고 합니다.

python tools/misc/print_config.py configs/pspnet/pspnet_r50-d8_4xb2-40k_cityscapes-512x1024.py

FAQ

이후 추가하겠습니다.

0개의 댓글