기술 회고

[기술 회고] Segmentation 모델 실험과 성능 개선

sybear02 2026. 7. 27. 13:22

Segmentation 모델 실험과 성능 개선

이 글은 현장실습에서 MobileNetV2 + FPN 기반 Segmentation 모델을 반복 실험하며 성능을 올린 과정을 정리한 기술 회고다. 협업 과제의 기밀을 위해 탐지 대상의 구체 명칭은 적지 않고, 아키텍처·손실함수·데이터·후처리 측면에서 무엇을 바꿨는지만 남긴다.

프로젝트와 동작 원리

목표는 이미지에서 관심 영역을 픽셀 단위로 나누는 Segmentation 모델을 만들고, 그 결과를 후속 분석 파이프라인에 넘기는 것이었다. Backbone은 MobileNetV2, 구조는 FPN을 사용했다. 경량 encoder로 학습·추론 부담을 줄이면서, FPN으로 다중 스케일 특징을 쓰는 조합이었다. segmentation-models-pytorch로 ImageNet 사전학습 encoder를 붙여 시작했다.

학습 환경은 Windows, Python 3.11, PyTorch(CUDA), VRAM 4GB급 GPU였다. 메모리가 넉넉하지 않아서 해상도·배치 크기 선택이 곧 학습 가능 여부를 가른다. Augmentation은 Albumentations로 Resize, Flip, Rotate, ColorJitter, Normalize를 구성했다.

평가 지표는 mIoU를 중심으로 두고, 이후 precision·recall도 같이 보기 시작했다. 평균 mIoU만 보면 희소 클래스 실패가 가려질 수 있어서였다.

무엇이 문제였나

초기 사전학습 기반 모델은 mIoU가 어느 정도 나왔지만, 데이터 증강·클래스를 추가하거나 해상도를 키우면 오히려 수치가 떨어지는 실험이 반복됐다.

클래스 불균형도 컸다. 배경 비율이 높고 관심 클래스는 픽셀이 적은 전형적인 분할 문제였다. 학습이 중간에 끊기면 처음부터 다시 해야 해서, 실험 속도도 문제였다. 그래서 모델 한 방을 노리기보다, 버전을 나눠 가설을 하나씩 검증하는 쪽으로 갔다.

실제로 어떻게 개선했나

해상도와 입력 크기

256, 384, 512 등 입력 크기를 바꿔 가며 학습했다. 해상도를 올린다고 무조건 좋아지지 않았다. VRAM 제약과 세부 영역 학습이 맞물려, 큰 입력이 오히려 mIoU를 떨어뜨린 버전도 있었다. 해상도는 “선명도”가 아니라 학습 안정성과 클래스별 픽셀 분포까지 바꾸는 변수였다.

손실함수 실험

CE만으로 부족하다고 보고 Lovász-Softmax, Dice, Focal, 둘을 섞은 Hybrid Loss를 시험했다. 불균형이 큰 분할에서 region overlap을 직접 보는 Dice와, 어려운 픽셀에 가중을 주는 Focal을 조합하는 쪽이 방향성으로 맞았다. 비율을 잘못 잡으면 한쪽 지표만 올라가고 mIoU는 무너지는 경우도 있어서, loss 비중 자체를 실험 축으로 뒀다.

클래스별 가중치도 함께 썼다. 배경은 낮게, 희소·중요 클래스는 높게 두는 방식이었다. 구체 클래스명은 생략하지만, “픽셀 비율이 아닌 업무 중요도·희소성”을 가중치에 반영하려 했다.

지표와 체크포인트

어느 버전부터는 precision·recall을 같이 로깅했다. mIoU만 보면 recall이 높은데 precision이 무너진 상태, 또는 그 반대를 구분하기 어려웠다. Best 모델 저장 기준도 단순 마지막 epoch가 아니라 validation mIoU가 갱신될 때 checkpoint를 덮어쓰도록 바꿨다. Early Stopping도 patience 기반으로 붙여, 지표가 정체되면 학습을 끊었다.

임계값과 후처리

추론 임계값을 올리거나 후처리를 넣었다 빼 보는 실험도 했다. 후처리를 넣으면 한쪽 지표는 나아 보여도 다른 쪽이 흔들릴 수 있어서, “예쁜 마스크”와 “지표상 이득”을 구분해 봐야 했다. 어떤 버전에서는 후처리를 제거하고 threshold만 조정하는 쪽이 나았다.

아키텍처 변경

초기 U-Net 계열 설정에서 FPN 쪽으로 구조를 정리·변경한 실험이 있었다. Encoder는 MobileNetV2를 유지한 채 neck/decoder 쪽을 바꿔, 경량 backbone + 다중 스케일 융합을 맞추려 했다.

데이터 쪽과 병행

모델만 돌리지 않았다. 중복 이미지 격리, 해상도 분석, 마스크 검증, 데이터셋 재분할(train/valid 등), 추가 클래스 병합 스크립트를 돌려 학습 분포를 바꿨다. 증강·클래스 추가가 한 번에 성능을 떨어뜨린 버전은, 데이터 합치는 방식과 정규화 가정이 깨졌을 가능성이 컸다. 그래서 이후에는 “데이터 변경 → 지표 확인 → loss/threshold 조정” 순서를 더 의식했다.

버전을 十余 개 가까이 쌓아 가며, 실패 실험도 기록으로 남겼다. 예전에 잘 나왔던 설정으로 회귀했다가 threshold만 올리는 식으로, 좋은 축을 고정하고 변수 하나만 바꾸는 습관이 생겼다.

학습 결과의 쓰임

분할 모델이 어느 정도 안정된 뒤에는, 영역 유형 인식과 후속 분석을 같이 보는 멀티태스크·파이프라인 쪽으로 확장했다. Segmentation 출력을 입력으로 받아 규칙 기반 추론 엔진과 결합하고, FastAPI로 이미지 입력·결과 시각화·JSON 리포트를 반환하는 서버도 구성했다. 분할 성능 개선은 그 파이프라인의 첫 번째 게이트였다.

수치로는 초기 대비 실험 과정에서 mIoU·precision·recall을 함께 끌어올린 버전을 확보했다. 절대 수치와 도메인 해석은 공개하지 않지만, “한 지표만 보는 실험”에서 “지표를 나눠 보고 loss·threshold·데이터를 되돌릴 수 있게 만든 실험”으로 바뀐 것이 핵심이다.

정리

Segmentation 개선에서 한 일은 모델을 한 번 더 크게 만드는 것이 아니었다. MobileNetV2+FPN을 기준으로 해상도·Hybrid Loss·클래스 가중치·mIoU 기준 체크포인트·Early Stopping·threshold/후처리·데이터 정리를 버전 단위로 검증했다. VRAM이 작은 환경에서는 해상도 선택이 성능과 학습 가능 여부를 동시에 좌우한다는 점도 남았다.

다음에 다시 한다면 실험 로그(데이터 해시, loss 비중, threshold, 해상도)를 표로 더 엄격히 남기고, 실패 버전을 재현 가능한 설정 파일로 고정해 두고 싶다.