1. 영상분류 - 증식 옵션 완전 제거
수정 파일: vision/references/classification/presets.py
영상분류의 변환은 ClassificationPresetTrain.__init__ 안에 정의되어 있다.
원본에는 훈련 시 적용되는 증식 변환 4가지가 포함되어 있습니다.
원본 코드 (증식 있음):
# 1. RandomResizedCrop: 랜덤한 위치/크기로 잘라내면서 crop_size로 resize (증식 + resize 동시)
transforms.append(T.RandomResizedCrop(crop_size, interpolation=interpolation, antialias=True))
# 2. RandomHorizontalFlip: 50% 확률로 좌우 뒤집기
if hflip_prob > 0:
transforms.append(T.RandomHorizontalFlip(hflip_prob))
# 3. AutoAugment 계열: TrivialAugmentWide 등 복합 증식
if auto_augment_policy is not None:
transforms.append(T.TrivialAugmentWide(interpolation=interpolation))
# 4. RandomErasing: 랜덤한 영역을 지워버리는 증식
if random_erase_prob > 0:
transforms.append(T.RandomErasing(p=random_erase_prob))
수정 후 코드 (증식 제거):
# RandomResizedCrop 대신 고정 크기 Resize + CenterCrop으로 교체
# (증식 없이 크기만 맞추기 위한 필수 전처리만 남김)
transforms.append(T.Resize(crop_size, interpolation=interpolation, antialias=True))
transforms.append(T.CenterCrop(crop_size))
# RandomHorizontalFlip, AutoAugment, RandomErasing 모두 제거
RandomResizedCrop은 단순 resize가 아니라 잘라내는 위치와 크기까지 랜덤하게 결정하는 증식이다. 완전히 제거하면
이미지들의 크기가 제각각이라 배치로 묶을 수 없으므로, 증식 없이 크기만 통일하는 Resize + CenterCrop 조합으로
교체해야 한다. 검증용 preset인 ClassificationPresetEval이 원래 이 방식을 사용한다.
영상분류의 변환 함수는 이미지만 받는다:
def __call__(self, img): # 이미지 1개만 입력
return self.transforms(img)
마스크가 없으므로 증식을 제거해도 마스크 동기화 문제는 발생하지 않는다.
[1번째 열: dataloader에서 꺼낸 훈련 영상 - 증식 제거 전 (RandomResizedCrop 적용, 랜덤하게 잘려진 모습)]
[2번째 열: dataloader에서 꺼낸 훈련 영상 - 증식 제거 후 (Resize+CenterCrop, 원본 비율 유지된 모습)]
과제 2. 객체검출 - 증식 옵션 추가 + 마스크 동기화 확인
수정 파일: vision/references/detection/presets.py
객체검출의 변환은 DetectionPresetTrain.__init__ 안에 data_augmentation 인자값으로 분기되어 기본값은 "hflip"으로,
좌우 뒤집기 하나만 적용된다.
원본 코드 (hflip만 있음):
# data_augmentation="hflip" (train.py의 argparse 기본값)
if data_augmentation == "hflip":
transforms += [T.RandomHorizontalFlip(p=hflip_prob)] # 좌우 뒤집기 하나만
수정 후 코드 (resize + 회전 추가):
if data_augmentation == "hflip":
transforms += [
T.RandomHorizontalFlip(p=hflip_prob), # 기존 좌우 뒤집기 유지
T.RandomShortestSize( # 랜덤 크기 변경 추가
min_size=(480, 512, 544, 576, 608),
max_size=1333
),
]
여기서 객체검출 변환 함수의 시그니처(매개변수)를 주목해야 한다:
def __call__(self, img, target): # 이미지와 target을 함께 입력
return self.transforms(img, target)
target 안에는 bbox 좌표와 마스크가 들어 있고, 변환 함수가 이미지와 target을 쌍으로 받아 처리하기 때문에,
RandomHorizontalFlip이 이미지를 뒤집을 때 bbox 좌표와 마스크도 동일한 방향으로 함께 뒤집힌다.
마찬가지로 RandomShortestSize로 이미지 크기가 바뀔 때도 마스크 크기가 동일하게 따라 바뀐다.
이것이 영상분류의 __call__(self, img)와 결정적으로 다른 점으로, 검출/분할에서는 변환이 이미지와 레이블에 항상
동기화되어 적용된다.
과제 3. 영상분할 - 480×480 resize만 남기고 나머지 제거
수정 파일: pytorch\segmentation\presets.py
영상분할의 변환은 SegmentationPresetTrain.__init__ 안에 정의되어 있으며, train.py의 get_transform에서 base_size=520,
crop_size=480으로 호출된다.
원본 코드 (증식 있음):
# 1. RandomResize: 이미지를 260~1040 사이 랜덤 크기로 resize (0.5*520 ~ 2.0*520)
transforms += [T.RandomResize(min_size=int(0.5 * base_size), # 260
max_size=int(2.0 * base_size))] # 1040
# 2. RandomHorizontalFlip: 50% 확률로 좌우 뒤집기
if hflip_prob > 0:
transforms += [T.RandomHorizontalFlip(hflip_prob)]
# 3. RandomCrop: 랜덤한 위치에서 480×480 잘라내기
transforms += [T.RandomCrop(crop_size)]
수정 후 코드 (480×480 resize만 남김):
# RandomResize의 min/max를 480으로 고정 → 항상 480×480 resize만 수행
transforms += [T.RandomResize(min_size=crop_size, max_size=crop_size)] # 480, 480
# RandomHorizontalFlip 제거
# RandomCrop 제거
RandomResize(260, 1040)은 매 이미지마다 260~1040 사이 랜덤 크기를 골라서 변환하는 증식이고, RandomCrop(480)은 그
이미지에서 480×480 영역을 랜덤 위치에서 잘라내는 증식이다. 두 개 모두 제거하고, RandomResize(480, 480)처럼 고정값을
주면 증식 없이 크기 통일만 수행한다.
480×480을 남기는 이유는 원본 이미지들의 해상도가 제각각이라서 배치로 묶으려면 동일한 크기가 필수이고, DeepLabV3 모델
자체가 고정 크기 입력을 요구하기 때문이다.
영상분할의 변환 함수도 마찬가지로 이미지와 마스크를 함께 받는다:
def __call__(self, img, target): # 이미지와 마스크(target)를 함께 입력
return self.transforms(img, target)
따라서 resize 변환이 이미지에 적용될 때 마스크에도 동일하게 480×480 resize가 적용되어 증식은 없어도 이미지-마스크
동기화는 그대로 유지된다.