Search

MMSegmentation - Segmenter Finetuning with Standford Background Dataset

카테고리
VCMI코드리뷰
Index
ML/DL
MMSegmentation
Segmenter
ViT
Semantic Segmentation
날짜
2024/03/03

MMSegmentation Segmenter 모델 Fine Tuning

MMSegmentation Github에있는 튜토리얼 코드(MMSegmentation_Tutorial.ipynb)를 기반으로 MMSegmentation의 Segmenter 모델을 커스텀 데이터셋에서 파인튜닝하는 작업을 진행하려고 한다.
→ ipynb 파일형태로 튜토리얼 파일이 제공되고 있기때문에, 추후 코랩에서 해당 파일을 클론하고 수정해서 파인튜닝을 진행하면 된다.

1. MMSegmentation 학습 환경 세팅

MMSegmentation을 사용한 파인튜닝 작업을 진행하기 위해 필요한 라이브러리 설치 작업을 진행한다.
Pytorch 버전: 1.12, CUDA 버전: 11.3

1.1 컴파일러 라이브러리 버전확인

모델 학습을 위한 nvcc, gcc 컴파일러 라이브러리 설치 및 버전확인을 진행한다.
nvcc (NVIDIA CUDA Compiler)
: NVIDIA의 CUDA 프로그래밍 언어를 위한 컴파일러 라이브러리
→ nvcc 는 CUDA 코드(.cu 파일)를 받아서 GPU에서 실행할 수 있는 바이너리 코드로 컴파일
gcc (GNU Compiler Collection)
: C, C++, Objective-C, Go 등 다양한 프로그래밍 언어 컴파일러 모음 라이브러리
→ gcc는 GNU 프로젝트의 일부로서, 리눅스와 같은 유닉스 기반 시스템에서 많이 사용됨
# nvcc 버전확인 !nvcc -V # GCC 버전확인 !gcc --version
Bash
복사

1.2 PyTorch, openmim 관련 라이브러리 설치

PyTorch 관련 라이브러리 버전
pytorch
torchvision
torchaudio
cudatoolkit
버전 정보
1.12.0
0.13.0
0.12.0
11.3
Pytorch 관련 라이브러리들
openmim 관련 라이브러리 설치
openmim
: MIM(MMlab Image Model)을 설치하고 관리하기 위한 커맨드 라인 도구로, MMLab의 다양한 오픈소스 프로젝트(MMClassification,MMSegmentation 등)를 쉽게 설치하고 사용할 수 있도록 지원
mmengine
: MMLab에서 제공하는 딥러닝 모델을 개발하고 학습하기 위한 유틸리티 및 프레임워크를 제공하는 라이브러리
→ 모델 학습, 평가, 추론 등의 과정을 수행하고 최적화하기 위한 기능들을 포함
mmcv
: 컴퓨터 비전 프로젝트를 위한 MMLab의 유틸리티 라이브러리
# conda로 pytorch 관련 라이브러리 설치 !conda install pytorch==1.12.0 torchvision==0.13.0 torchaudio==0.12.0 cudatoolkit=11.3 -c pytorch # mim, mmengine, mmcv 라이브러리 설치 !pip install -U openmim !mim install mmengine !mim install 'mmcv >= 2.0.0rc1'
Bash
복사
open-mmlab 깃허브에서 mmsegmentation 레포지토리 클론 후, 설치
# mmsegmentation 디렉토리 초기화(삭제) !rm -rf mmsegmentation # 깃 레포지토리 클론 및 디렉토리 이동 !git clone -b main https://github.com/open-mmlab/mmsegmentation.git %cd mmsegmentation # mmsegmentation 설치 !pip install -e .
Bash
복사
torch, torchvision, mmseg 모듈 버전 확인
# Pytorch 설치 확인 및 cuda 사용가능 여부 확인 import torch, torchvision print(torch.__version__, torch.cuda.is_available()) # MMSegmentation 모듈 설치 확인 import mmseg print(mmseg.__version__)
Bash
복사

2. 커스텀 데이터 셋 준비

MMSegmentation에서 제공하는 Segmenter 모델을 커스텀 데이터셋에서 파인튜닝하기 위한 방법은 아래와 같다.
1.
새로운 데이터셋 클래스 생성
2.
해당 데이터셋 클래스 config 파일 생성
3.
커스텀 데이터셋으로 training 및 evaluation 진행

2.1 새로운 데이터 셋 클래스 생성

해당 튜토리얼에서는 Stanford Background Dataset를 커스텀 데이터셋 예제로 사용하였다.
이외의 다른 데이터셋도 동일한 방법으로 준비과정을 거치면 된다.
커스텀 데이터 파일 다운로드 및 압축해제
!wget http://dags.stanford.edu/data/iccv09Data.tar.gz -O stanford_background.tar.gz !tar xf stanford_background.tar.gz
Bash
복사
iccv09Data 데이터셋 폴더 구조
mmcv 라이브러리를 사용해 데이터 확인
OpenMMLab에서 제공하는 컴퓨터 비전 작업을 위한 오픈소스 도구 라이브러리인 mmcv를 사용해 커스텀 데이터를 확인한다.
import mmcv import mmengine import matplotlib.pyplot as plt img = mmcv.imread('iccv09Data/images/6000124.jpg') plt.figure(figsize=(8, 6)) plt.imshow(mmcv.bgr2rgb(img)) plt.show()
Python
복사
annotation을 semantic map 포맷으로 변환
데이터 루트(iccv09Data) 폴더 아래에 있는 labels 폴더에는 데이터셋 주석 파일(annotation)들이 있다.
해당 annotation 텍스트 파일을 읽어서 semantic segmatation map 이미지로 변환해야 한다.
# dataset root와 이미지, annotation이 위치할 디렉토리 정의 data_root = 'iccv09Data' img_dir = 'images' ann_dir = 'labels' # 각 클래스에 대한 팔레트(색상)을 정의 - 각 클래스를 시각적으로 구분하는데 사용 classes = ('sky', 'tree', 'road', 'grass', 'water', 'bldg', 'mntn', 'fg obj') palette = [[128, 128, 128], [129, 127, 38], [120, 69, 125], [53, 125, 34], [0, 11, 123], [118, 20, 12], [122, 81, 25], [241, 134, 51]]
Python
복사
import os.path as osp import numpy as np from PIL import Image # 데이터셋 annotation을 to semantic segmentation map으로 변환 for file in mmengine.scandir(osp.join(data_root, ann_dir), suffix='.regions.txt'): seg_map = np.loadtxt(osp.join(data_root, ann_dir, file)).astype(np.uint8) seg_map[seg_map>7]=7 # 커스텀 데이터셋의 클래스 수가 8개라서 그 이외의 라벨에 대한 예외처리 seg_img = Image.fromarray(seg_map).convert('P') #Numpy 배열로부터 PIL 이미지 객체 생성 seg_img.putpalette(np.array(palette, dtype=np.uint8)) # 이미지의 팔레트 설정 # 변환된 이미지를 주석파일이 위치한 같은 디렉토리에 저장 seg_img.save(osp.join(data_root, ann_dir, file.replace('.regions.txt', '.png')))
Python
복사
변환된 Segmentation map 이미지 확인
matplotlib.patches 라이브러리 사용해서 segmentation map 이미지 확인
import matplotlib.patches as mpatches img = Image.open('iccv09Data/labels/6000124.png') plt.figure(figsize=(8, 6)) im = plt.imshow(np.array(img.convert('RGB'))) # 각 클래스 팔레트 색상에 대해서 패치 생성 patches = [mpatches.Patch(color=np.array(palette[i])/255., label=classes[i]) for i in range(8)] # 각 패치에 대해서 legend 생성 plt.legend(handles=patches, bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0., fontsize='large') # matplotlib.patches로 segmentation map 확인 plt.show()
Python
복사
train, validation 데이터 셋 나누기
‘splits’ 디렉토리 아래에 train, validation, test 용 데이터 랜덤하게 나누기
Train : Validation : Test = 6:2:2
train용 데이터 파일들의 이름은 train.txt, validation용 데이터 파일들의 이름은 val.txt에 저장됨
# train, validation 데이터셋 나누기 split_dir = 'splits' mmengine.mkdir_or_exist(osp.join(data_root, split_dir)) filename_list = [osp.splitext(filename)[0] for filename in mmengine.scandir( osp.join(data_root, ann_dir), suffix='.png')] # Train, validation, test 세트로 데이터를 분할하기 위한 인덱스 계산 train_ratio, val_ratio = 0.6, 0.2 train_length = int(len(filename_list) * train_ratio) val_length = int(len(filename_list) * val_ratio) # Train 세트 파일 목록 저장 with open(osp.join(data_root, split_dir, 'train.txt'), 'w') as f: f.writelines(line + '\n' for line in filename_list[:train_length]) # Validation 세트 파일 목록 저장 with open(osp.join(data_root, split_dir, 'val.txt'), 'w') as f: f.writelines(line + '\n' for line in filename_list[train_length:train_length + val_length]) # Test 세트 파일 목록 저장 with open(osp.join(data_root, split_dir, 'test.txt'), 'w') as f: f.writelines(line + '\n' for line in filename_list[train_length + val_length:])
Python
복사
커스텀 데이터 클래스 생성하기
mmseg.registry, mmseg.datasetsDATASETS, BaseSegDataset 클래스를 기반으로 커스텀 세크멘테이션 데이터 셋 클래스 구현
mmseg.registry DATASETS: 데이터셋을 등록하는데 사용하는 레지스트리 객체
@DATASETS.register_module() 데코레이터를 사용해 등록할 수 있고, 해당 데코레이터를 사용해 config 파일이나, 다른 메커니즘을 통해 데이터셋 참조 및 사용 가능
mmseg.datasets BaseSegDataset : 모든 세그멘테이션 데이터셋의 기본 클래스로 데이터셋 정의에 필요한 기본 메서드와 속성 제공
from mmseg.registry import DATASETS from mmseg.datasets import BaseSegDataset @DATASETS.register_module() class StanfordBackgroundDataset(BaseSegDataset): # 클래스, 팔레트 정보에대한 딕셔너리 METAINFO 생성 METAINFO = dict(classes = classes, palette = palette) def __init__(self, **kwargs): super().__init__(img_suffix='.jpg', seg_map_suffix='.png', **kwargs)
Python
복사

2.2 모델 및 데이터셋 config 파일 생성

다음으로는 모델 학습을 위한 config 파일 수정작업을 진행해야한다.
또한, 파인튜닝 작업을 진행하기 위해서는 기존에 사전학습했던 모델의 checkpoint에서 학습 가중치들을 불러와야한다.
config, checkpoint 파일 다운
해당 예제에서는 CityScapes 데이터셋에서 사전학습된 PSPNet 모델의 config 파일을 사용하고 있다.
Segmenter 모델을 사용하기 위해선 mmsegmentation/configs 폴더에서 segmenter 모델의 config 파일을 찾아서 지정해주어야한다.
# mmsegmentation 라이브러리를 설치하고 Segmenter 모델의 설정 파일을 다운로드 !mim download mmsegmentation --config segmenter_vit-t_mask_8xb1-160k_ade20k-512x512 --dest .
Bash
복사
→ 위 코드는 Segmenter 모델 중, vit-t 모델을 백본으로 사용하고, 디코더 모델로 mask transformer를 사용하여 ADE20K 데이터셋에서 사전학습을 진행한 모델의 config 파일을 다운 받는 예제이다.
mmengine.Config 모듈에 다운 받은 config 파일을 등록한다.
from mmengine import Config #config 파일 로드하기 cfg = Config.fromfile('configs/segmenter/segmenter_vit-t_mask_8xb1-160k_ade20k-512x512.py') print(f'Config:\n{cfg.pretty_text}')
Python
복사
Config 파일 수정
새로운 커스텀 데이터로 학습하기 위해서는 config 파일의 다음 항목들을 수정해야한다.
모델 구조의 세부 설정 변경
1.
백본 모델, 디코더 헤드, 보조 헤드의 정규화 설정
2.
디코더 헤드와 보조헤드의 클래스 수
3.
모델의 데이터 전처리 크기
데이터 셋 타입을 새로운 커스텀 데이터의 이름으로 변경, 루트 디렉토리 재설정
Training, Validation, Test 파이프라인 및 데이터로더 설정
max_iter, val_interval 등과 같은 모델 학습관련 세부 설정 수정
cfg.norm_cfg = dict(type='LN', requires_grad=True) cfg.crop_size = (512, 512) cfg.model.data_preprocessor.size = cfg.crop_size cfg.model.backbone.norm_cfg = cfg.norm_cfg cfg.model.decode_head.num_classes = 8 # 데이터셋 타입과 경로를 수정 cfg.dataset_type = 'StanfordBackgroundDataset' cfg.data_root = data_root # 트레이닝 데이터 로더의 배치 사이즈를 설정 cfg.train_dataloader.batch_size = 8 # 트레이닝 파이프라인을 설정 cfg.train_pipeline = [ dict(type='LoadImageFromFile'), dict(type='LoadAnnotations'), dict(type='RandomResize', scale=(320, 240), ratio_range=(0.5, 2.0), keep_ratio=True), dict(type='RandomCrop', crop_size=cfg.crop_size, cat_max_ratio=0.75), dict(type='RandomFlip', prob=0.5), dict(type='PhotoMetricDistortion'), dict(type='PackSegInputs'), ] # 테스트 파이프라인을 설정 cfg.test_pipeline = [ dict(type='LoadImageFromFile'), dict(type='Resize', scale=(320, 240), keep_ratio=True), dict(type='LoadAnnotations'), dict(type='PackSegInputs') ] #train dataloader 설정 cfg.train_dataloader.dataset.type = cfg.dataset_type cfg.train_dataloader.dataset.data_root = cfg.data_root cfg.train_dataloader.dataset.data_prefix = dict(img_path=img_dir, seg_map_path=ann_dir) cfg.train_dataloader.dataset.pipeline = cfg.train_pipeline cfg.train_dataloader.dataset.ann_file = 'splits/train.txt' #validation dataloader 설정 cfg.val_dataloader.dataset.type = cfg.dataset_type cfg.val_dataloader.dataset.data_root = cfg.data_root cfg.val_dataloader.dataset.data_prefix = dict(img_path=img_dir, seg_map_path=ann_dir) cfg.val_dataloader.dataset.pipeline = cfg.test_pipeline cfg.val_dataloader.dataset.ann_file = 'splits/val.txt' #test dataloader 설정 cfg.test_dataloader.dataset.type = cfg.dataset_type cfg.test_dataloader.dataset.data_root = cfg.data_root cfg.test_dataloader.dataset.data_prefix = dict(img_path=img_dir, seg_map_path=ann_dir) cfg.test_dataloader.dataset.pipeline = cfg.test_pipeline cfg.test_dataloader.dataset.ann_file = 'splits/test.txt' #사전학습된 모델 checkpoint 불러오기 cfg.load_from = 'segmenter_vit-t_mask_8x1_512x512_160k_ade20k_20220105_151706-ffcf7509.pth' #로그 디렉토리 설정 cfg.work_dir = './work_dirs/tutorial' cfg.train_cfg.max_iters = 300 cfg.train_cfg.val_interval = 300 cfg.default_hooks.logger.interval = 10 cfg.default_hooks.checkpoint.interval = 300 #결과 재현을 위한 시드 값을 설정 cfg['randomness'] = dict(seed=0) #최종 설정(config)을 출력 print(f'Config:\n{cfg.pretty_text}')
Python
복사

3. 모델 학습 및 평가

mmengine.runner 라이브러리의 Runner 모듈에 수정한 config 파일 등록
from mmengine.runner import Runner runner = Runner.from_cfg(cfg)
Python
복사
모델 학습 진행
runner.train()
Python
복사
모델 학습을 진행하게 되면, 아래와 같이 10 iteration마다 train 및 validation 로그가 생기는 것을 확인 할 수 있다.
Train 작업 이후, Test 작업 실행
runner.test()
Python
복사
학습 완료 후, 추론 결과 시각화
# mmseg.apis에서 모델 초기화, 추론, 결과 시각화를 위한 함수를 임포트 from mmseg.apis import init_model, inference_model, show_result_pyplot # 파인튜닝이 완료된 모델의 체크포인트 경로를 지정 checkpoint_path = './work_dirs/tutorial/iter_200.pth' # 설정(cfg)과 체크포인트를 사용하여 모델을 초기화 model = init_model(cfg, checkpoint_path, 'cuda:0') # 추론을 위한 이미지파일 불러온 뒤, 추론 수행 img = mmcv.imread('iccv09Data/images/6000124.jpg') result = inference_model(model, img) # show_result_pyplot 함수를 사용하여 모델의 추론 결과를 시각화 plt.figure(figsize=(8, 6)) vis_result = show_result_pyplot(model, img, result) plt.imshow(mmcv.bgr2rgb(vis_result)) # RGB 포맷으로 변환된 이미지 출력
Python
복사
mmseg.apis의 init_model 함수
mmseg.apis 의 inference 함수

4. FineTuning 결과 분석

ADE20K 160k 데이터셋에서 사전학습 시킨 Segmenter 모델(segmenter-vit-t_mask_8x1_512x512)을 Standford Background 데이터셋에서 파인튜닝한 결과는 다음과 같다.
주요 Metrics 결과
1.
Train 이후 Validation 결과
aAcc(Overall Accuracy) : 87.1200
mIoU : 61.2000
mAcc : 68.8400
data_time(평균 데이터로딩 속도) : 0.0048
2.
Test 결과
aAcc(Overall Accuracy) : 85.9800
mIoU : 69.9700
mAcc : 79.2800
data_time(평균 데이터로딩 속도) : 0.0044

4.1 Train loss 경과

4.2 Segmentation Accuracy 경과

4.3 Class 별 Segmentation IOU 값 및 Accuracy

Standford Background 데이터셋에서는 총 8개의 클래스 레이블에 대해 Segmentation 작업을 진행한다.
Validation 작업에 대한 Segmentation IOU 및 Accuracy
Test 작업에 대한 Segmentation IOU 및 Accuracy

4.4 Inference 결과 시각화

테스트 이미지 원본
테스트 이미지(label) Segmentation Map
임의의 테스트 이미지에 대해 추론 작업을 진행하고 시각화한 결과이다.
테스트 이미지의 나무(tree), 건물(bldg), 도로(road), 잔디(grass), figure object(fg_obj)에 대해서 비교적 정확하게 segmentation이 가능하다는 것을 확인할 수 있다.