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 새로운 데이터 셋 클래스 생성
이외의 다른 데이터셋도 동일한 방법으로 준비과정을 거치면 된다.
•
커스텀 데이터 파일 다운로드 및 압축해제
!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.datasets 의 DATASETS, 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 파일을 사용하고 있다.
# 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이 가능하다는 것을 확인할 수 있다.