logo

데이터 증강

데이터 증강 data augmentation

  • 데이터 생성의 어려움: 모델은 복사가 가능하지만, 데이터는 직접 만들어야 하므로 비용이 발생함.
  • 해결책으로서의 데이터 증강: 기존 데이터를 변형하여 데이터 양을 늘리는 기법.
  • 증강 방법 예시: 이미지 회전, 좌우 반전, 색상 변경 등을 통해 하나의 데이터를 여러 개로 불림.
  • 장점: 데이터 수집의 수고 없이 데이터 양을 늘릴 수 있음.

원본 이미지에서 여러 증강 이미지를 만드는 데이터 증강 개념도

데이터 증강의 한계

  • 한계와 주의점: 데이터의 본질적인 의미를 변화시키지 않는 범위에서만 제한적으로 사용해야 함.
  • 부적절한 증강 예시:
    • 나비 종류 분류: 색상이 중요한 특징이므로 임의로 색을 바꾸면 안 됨.
    • 자율 주행: 주행 차선의 좌우 정보가 중요하므로 이미지를 좌우로 뒤집으면 안 됨. 신호등 색상 변경 또한 불가.

실습 준비

  • 예제 파일 열기
from PIL import Image
from torchvision import transforms

img = Image.open('pump_horse.jpg')
  • 여러 이미지 보기
def concat_images(images):
    n = len(images)
    width, height = 128, 128
    total_width = n * width
    new_im = Image.new('RGB', (total_width, height), color='white')
    for i, im in enumerate(images):
        new_im.paste(im.resize((width, height)), (width * i, 0))
    return new_im

RandomPerspective

  • 무작위로 perspective를 바꿈
transform = transforms.RandomPerspective(distortion_scale=0.6, p=1.0)
concat_images([transform(img) for _ in range(4)])

RandomPerspective로 원근을 무작위 변경한 예시

RandomRotation

  • 무작위로 회전
transform = transforms.RandomRotation(degrees=(0, 180))
concat_images([transform(img) for _ in range(4)])

RandomRotation으로 이미지를 무작위 회전한 예시

RandomAffine

  • 무작위 아핀 변환
transform = transforms.RandomAffine(
    degrees=(30, 70), # 회전
    translate=(0.1, 0.3), # 평행 이동
    scale=(0.5, 0.75), # 확대/축소
    shear=(10, 30))  # 기울이기
concat_images([transform(img) for _ in range(4)])

RandomAffine으로 회전, 이동, 확대 축소, 기울이기를 적용한 예시

RandomCrop

  • 무작위로 주어진 크기의 일부 영역을 잘라냄
transform = transforms.RandomCrop(size=(128, 128))
concat_images([transform(img) for _ in range(4)])

RandomCrop으로 임의 위치를 잘라낸 예시

RandomResizedCrop

  • 무작위로 잘라낸 후, 정해진 크기로 변경
transform = transforms.RandomResizedCrop(size=(512, 512))
concat_images([transform(img) for _ in range(4)])

RandomResizedCrop으로 임의 영역을 자르고 크기를 변경한 예시

ColorJitter

  • 무작위로 색을 변경
  • brightness(밝기), contrast(대조), 채도(saturation), hue(색상)
transform = transforms.ColorJitter(brightness=.5, hue=.3)
concat_images([transform(img) for _ in range(4)])

ColorJitter로 밝기와 색상을 무작위 변경한 예시

RandomInvert

  • 무작위로 색 반전
transform = transforms.RandomInvert()
concat_images([transform(img) for _ in range(4)])

RandomInvert로 색을 반전한 예시

RandomPosterize

  • 무작위로 포스터화(색의 비트 수를 낮춤)
transform = transforms.RandomPosterize(bits=2)
concat_images([transform(img) for _ in range(4)])

RandomPosterize로 색의 비트 수를 낮춘 예시

RandomChoice

  • 무작위로 고르기
transform = transforms.RandomChoice([
    transforms.RandomHorizontalFlip(),
    transforms.RandomVerticalFlip(),
    transforms.RandomRotation(180),
])
concat_images([transform(img) for _ in range(4)])

RandomChoice로 여러 변환 중 하나를 고른 예시

RandomApply

  • 변환을 확률 p에 따라 무작위로 적용
transform = transforms.RandomApply(
    transforms.RandomRotation(180),
    p=0.9)
concat_images([transform(img) for _ in range(4)])

RandomApply로 확률에 따라 변환들을 적용한 예시

RandomOrder

  • 무작위 순서로 적용
transform = transforms.RandomOrder([
    transforms.RandomRotation(180),
    transforms.RandomCrop((256, 256)),
])
concat_images([transform(img) for _ in range(4)])

RandomOrder로 변환 순서를 무작위로 적용한 예시

학습과 검증 전처리 분리

  • 무작위 증강은 학습 데이터에만 적용
  • 검증 데이터에는 크기 조정처럼 실행할 때마다 결과가 같은 변환만 적용
  • ToTensor()0~1 텐서를 만든 뒤 마지막에 Normalize()-1~1 범위 조정
  • 고양이와 강아지 사진은 좌우 반전이 자연스럽지만 위아래 반전은 실제 데이터 분포와 다를 수 있어 제외
from torchvision.datasets import ImageFolder

IMAGE_SIZE = 224
RGB_MEAN = (0.5, 0.5, 0.5)
RGB_STD = (0.5, 0.5, 0.5)

train_transform = transforms.Compose([
    transforms.RandomResizedCrop(
        IMAGE_SIZE,
        scale=(0.8, 1.0),  # 원본 면적의 80~100% 사용
    ),
    transforms.RandomHorizontalFlip(p=0.5),  # 절반 확률로 좌우 반전
    transforms.ToTensor(),  # 0~255 이미지를 0~1 텐서로 변환
    transforms.Normalize(RGB_MEAN, RGB_STD),  # 0~1을 -1~1로 조정
])

val_transform = transforms.Compose([
    transforms.Resize(256),  # 짧은 변을 256픽셀로 조정
    transforms.CenterCrop(IMAGE_SIZE),  # 가운데 224×224 영역 추출
    transforms.ToTensor(),
    transforms.Normalize(RGB_MEAN, RGB_STD),
])

train_dataset = ImageFolder(
    root="cats_and_dogs_filtered/train",
    transform=train_transform,
)
val_dataset = ImageFolder(
    root="cats_and_dogs_filtered/validation",
    transform=val_transform,
)

학습 데이터는 에포크마다 다른 영역이나 좌우 방향을 볼 수 있음. 검증 데이터는 항상 같은 중앙 영역을 사용하므로 실행 간 성능 비교 가능.

검증 데이터에는 무작위 증강 제외

검증 데이터에 무작위 자르기나 반전을 적용하면 평가할 때마다 입력이 달라져 성능을 일관되게 비교하기 어려움.

Previous
이미지 로딩