본문으로 바로가기
Archive for AI Study
Project

손 뼈 Semantic Segmentation: 분포 분석부터 고해상도 학습까지

손 X-ray의 29개 뼈 클래스를 분할하면서 회전 분포, 작은 뼈의 경계, 제한된 GPU 메모리를 문제로 정의했다. 데이터 전처리와 증강, 고해상도 학습, EMA·PointRend를 비교하고 효과가 있었던 방법을 다음 실험에 반영했다.

손 뼈 Segmentation에서는 큰 뼈를 대략 찾는 것보다 작은 뼈와 겹치는 경계를 정확하게 나누는 일이 어려웠다. 베이스라인의 Dice는 이미 약 0.94였지만, 클래스별 예측을 보면 작은 손목 뼈와 손가락 끝에서 개선할 부분이 보였다. 모델을 바꾸기 전에 데이터 분포와 입력 해상도, 경계 예측을 따로 확인하고 싶었다.

이 글은 부스트캠프 AI Tech 8기에서 진행한 팀 프로젝트 중 내가 다룬 EDA·전처리·증강과 모델링 실험을 중심으로 정리한 기록이다. 전체 실험과 팀의 모델 조합은 Weekly Review의 Wrap-up Report에 정리했다.

항목 내용
기간 2025.12.15–2026.01.07
과제 손 X-ray의 29개 뼈 클래스 Semantic Segmentation
데이터 2048×2048 이미지 · 학습 800장 · 테스트 288장
내가 집중한 작업 EDA·Feature Engineering·전처리·증강·모델링 및 비교 실험
실험 환경 Tesla V100 32GB · PyTorch 계열 라이브러리 · W&B·Notion·GitHub

손 X-ray와 뼈 클래스의 annotation

회전 분포를 확인하고 검증 데이터를 나누기

이미지를 살펴보니 손이 곧게 놓인 경우와 기울어진 경우가 있었다. 회전 여부는 메타데이터에 없어서 새끼손가락 끝의 뼈 f-16과 손목 뼈 Ulna의 중심을 잇는 벡터로 각도를 구했다. 33°를 기준으로 나눠 분포를 비교했더니, 회전된 손의 비율이 테스트에서 학습 데이터보다 약 10배 높았다.

학습 데이터의 손 회전각 분포

테스트 데이터의 손 회전각 분포

처음에는 이 차이를 오버샘플링으로 맞춰보려고 했다. 회전된 손과 남성 데이터를 더 뽑아 학습했지만, Public과 Validation의 점수 차이가 0.003에서 0.004로 커졌다. 비율을 맞췄다는 이유만으로 모델이 더 잘 일반화하지는 않았다. 이후 실험에서는 이 오버샘플링을 제외했다.

검증 데이터를 나눌 때는 환자 ID를 그룹으로 묶었다. 같은 사람의 양손이 학습과 검증에 나뉘면 서로 비슷한 구조를 보고 평가할 수 있기 때문이다. 성별과 키·몸무게의 관계도 확인했고, GroupRandomK-Fold에서 성별 비율이 크게 치우치지 않아 이 분할을 사용했다.

전처리와 증강은 한 가지씩 바꿔 비교했다

일부 이미지의 상단과 좌측에는 흰색 노이즈가 있었다. 해당 영역의 80픽셀을 제거하고 검은색으로 채우자 학습 초기 수렴이 빨라졌다. 배경 전체를 제거하는 방법도 시도했지만 추가적인 성능 향상은 확인하지 못했다.

상단의 흰색 노이즈를 제거하기 전과 후

밝은 픽셀이 많은 이미지를 이상치로 보고 제거한 실험에서는 오히려 점수가 약 0.001 떨어졌다. 반지나 네일아트가 있다는 이유만으로 학습에서 빼는 방식은 맞지 않았다. 이후에는 Dice가 낮은 예측을 시각화해 annotation이 뒤바뀐 사례와 보형물 사례를 확인했다. 이 사례들을 정리한 실험에서는 test score가 0.0009 높아졌다.

증강도 여러 방법을 한 번에 추가하지 않고 나머지 설정을 통제하면서 비교했다. W&B로 학습 곡선과 지표를 기록하고, 결과를 Notion에 남겨 팀원들과 공유했다.

증강 비교 실험 Validation Dice
Baseline 0.9546
RandomBrightnessContrast 0.9548
ShiftScaleRotate 0.9566
GridDistortion 0.9559
GridDropout 0.9537

증강별 W&B Validation Dice 비교

이 비교에서 가장 높은 점수를 낸 방법은 SSR이었다. 이동 범위 0.05, 스케일 범위 0.1, 회전 범위 15°로 작은 변화를 줬고, baseline 대비 Dice가 0.002 높아졌다. 그래서 후속 실험에 SSR을 적용했다. 의료 영상에 자주 쓰는 CLAHE도 비교했지만 일관된 개선은 확인하지 못했다.

분포를 관찰한 것과 그 분포를 보정한 방법이 효과가 있는지는 별도로 봐야 했다. 오버샘플링은 채택하지 않았고, 증강 비교에서는 실제로 성능이 높아진 방법을 남겼다.

해상도를 보존하려고 학습 방식을 바꿨다

2048px 원본을 512px로 줄이면 작은 뼈와 경계의 정보도 함께 줄어든다. 클래스별 Dice와 예측 이미지를 보고, 모델의 파라미터 수를 늘리는 것보다 입력 해상도를 유지하는 쪽을 먼저 실험했다.

해상도를 높일 때는 GPU 메모리가 제한이었다. AMP로 메모리 사용을 줄이고 배치 크기를 낮췄지만, 배치가 1인 환경에서는 Batch Norm을 쓰는 학습이 불안정했다. Group Norm으로 바꿔 작은 배치에서도 학습할 수 있게 하고, HRNet으로 고해상도 특징을 유지하는 조합을 비교했다.

모델링 비교 해상도 Dice 조건
DeepLabV3+·ResNet50 baseline 512px 약 0.94 초기 baseline
HRNet W48 2048px 0.9648 배치 1 · Batch Norm
HRNet W48 1536px 0.9755 배치 1 · Group Norm

해상도를 가장 크게 올린 설정이 가장 좋은 결과를 내지는 않았다. 입력 해상도와 작은 배치에서의 정규화 방식을 함께 봐야 했다. 0.9755는 OCR·HRNet의 fold 0 Validation Dice이고, 해당 단일 모델의 Private Dice는 0.9735였다. 모델링 비교와 최종 제출 점수를 같은 성과로 섞지 않았다.

경계 예측과 작은 배치의 학습 안정성 개선

예측 결과에서는 손등의 겹치는 뼈와 경계가 특히 어려웠다. 손등의 Hard Example을 더 다루려고 학습을 두 단계로 나누고, 후반 파인튜닝에서 BCE·Dice 중심의 손실을 BCE·Lovasz 조합으로 바꾸는 실험을 했다.

경계 예측에는 PointRend를 적용했고, 작은 배치에서의 가중치 변동을 줄이기 위해 EMA를 사용했다. 두 방법을 적용한 실험에서는 Public Dice가 0.9697에서 0.9728로 0.0031 상승했다. 이 값은 두 방법을 적용한 조합의 결과이며, 각각의 기여를 따로 더한 수치가 아니다.

Public과 Validation 점수의 차이도 확인했다. 학습률 재시작이 도움이 될 것이라는 가설로 CosineRestartWarmupScheduler를 비교했고, 적용 후 두 점수의 차이는 0.0028이었다. 이 차이가 줄었다는 관찰만으로 수렴 위치나 과적합 원인을 확정하지는 않았다.

개인 실험 결과와 팀 최종 결과

모델별 Validation 점수만 높다고 제출 성능도 높은 것은 아니었다. 서로 다른 fold의 모델을 섞은 앙상블은 Private Dice 0.9624로 떨어졌고, 팀은 다른 조합도 비교했다. 최종적으로 세 예측 결과를 Hard Voting한 제출의 성능이 가장 높았다.

팀 최종 제출의 모델 조합과 앙상블 결과

확인한 결과 지표 값
SSR 증강 비교 Validation Dice 0.9546 → 0.9566
EMA·PointRend 적용 Public Dice 0.9697 → 0.9728
OCR·HRNet fold 0 Validation Dice 0.9755
OCR·HRNet 단일 모델 Private Dice 0.9735
팀 최종 제출 Public Dice 0.9749
팀 최종 제출 Private Dice 0.9760

팀 최종 제출 점수

각 행은 서로 다른 실험과 평가 조건이다. 증강·모델링·앙상블의 개선 폭을 모두 더한 결과로 읽으면 안 된다. 내가 집중한 부분은 데이터 분포를 확인하고, 전처리·증강·학습 설정을 바꿔가며 효과를 비교하는 일이었다. 최종 제출 점수는 팀의 모델 탐색과 앙상블을 합친 결과다.

이 프로젝트에서 남긴 기준은 분명했다. 이상치처럼 보이는 이미지를 제거하거나 일반적으로 좋은 증강을 추가하기 전에, 같은 조건에서 실제로 무엇이 달라지는지 확인해야 했다. 잘되지 않은 오버샘플링과 CLAHE 실험도 다음 선택의 근거가 됐다.

ProjectSemanticSegmentation

Continue exploring

더 읽어보기

모든 글