[Computer Vision] 7(2). 데이터셋 보강

3 minute read


데이터셋 보강


개요


데이터셋을 ‘보강’한다는 것은 각 이미지에서 다르게 보이는 이미지를 얻기 위해 그 콘텐츠에 갠덤 변환을 적용하는 것을 뜻한다.


왜 데이터셋을 보강하는가?

데이터 보강은 너무 작은 훈련 세트를 다루기 위한 가장 일반적이고 단순한 기법이다.

이 다양한 버전은 ‘크기 변경’, ‘무작위 반전’, ‘회전’, ‘색 변경’ 등과 같은 랜덤 변환의 조합을 적용함으로써 얻는다. 부수적으로 데이터 보강은 작은 이미지 집합에서 큰 모델을 훈련할 때 일반적으로 발생하는 ‘과적합을 방지’할 수 있다.


데이터 보강은 훈련 이미지가 충분할 때에도 여전히 고려해 볼 만하다. 이는 ‘편향’ 문제 때문이다.

예를 들어 이진 분류기를 위한 모델을 훈련시킨다고 가정하자. 훈련 데이터셋에서 한 클래스의 이미지 데이터가 다른 클래스의 데이터보다 노이즈가 많고 어둡다. 신경망은 모든 시각적 신호를 사용해 훈련되기 때문에 이 모델은 순수하게 객체 표현(모양이나 질감 같은) 에 초점을 맞추는 대신 이러한 조명/노이즈 같은 요소에 영향을 받게 된다. 이렇게 훈련된 모델은 실제 추론 시에 형편 없어질 수도 있다.

이런 경우에 노이즈를 사진에 무작위로 추가하거나 밝기를 부작위로 조정함으로써 데이터셋의 ‘편향성을 보완’하고 네트워크가 ‘불필요한 시각적 차이에 영향 받지 않게’ 해준다.


데이터 보강 기법을 사용하면 ‘타깃 이미지의 가변성에 맞게 데이터셋을 더 잘 준비’할 수 있게 되는 것이다.



고려사항

데이터 보강은 여러 형식을 취할 수 있으며 이 절차를 추가할 때 여러 옵션을 고려해야한다.


우선 데이터셋 보강은 ‘오프라인’이나 ‘온라인’으로 이루어질 수 있다.

‘오프라인’ 보강은 훈련이 시작되기 전에 모든 이미지를 변환하고 저장해두는 것을 말하고, ‘온라인’ 보강은 훈련 입력 파이프라인 내부에서 새로운 배치를 생성할 때마다 변환을 적용하는 것을 말한다.

오프라인과 온라인 보강 중 무엇을 선택할 지는 ‘사용할 수 있는 기기의 메모리/처리 용량’과 원하는 ‘가변성’에 따라 달라진다. 가변성이란 어떠한 변환을 적용할지에 대한 것이다.


따라서 데이터 보강을 할 때 처음으로 할 일은 관련 변환(해당되는 경우 매개변수도)을 후보로 올리는 것이다. 이는 기기의 용량이나 연산의 시간도 고려해야겠지만, 무엇보다 ‘타깃 데이터에 알맞는 적절한 변환을 선택하는 것’이 중요하다.

또한 자르기나 밝기 조정같은 일부 변환은 신중하게 제대로 매개변수화해야 한다. 이미지가 콘텐츠를 식별할 수 없을 만큼 너무 어둡거나 밝아지면, 또는 핵심 요소가 잘려나가면 모델은 이 편집된 사진에서 어떤 것도 학습할 수 없고, 심지어 네트워크에 혼란을 줄 수 있다. 따라서 의미론적 콘텐츠를 보존하면서 데이터셋에 의미있는 변환을 추가하는 변환을 후보로 올리고 매개변수화하는 것이 중요하다.


또한 데이터 보강이 데이터 부족을 완전히 보완해줄 수 없다는 사실도 알아두는 것이 중요하다. 따라서 분류하고자 하는 각 클래스의 데이터들을 최대한 모을 필요가 있다.


마지막으로, 해당되는 경우 레이블을 적절히 변환하는 것을 잊지 말아야 한다. 이는 특히 기하학적 변환이 수행될 때 탐지 및 분할 레이블에 있어 중요하다. 이미지 크기가 조정되거나 회전되면 관련 레이블 맵이나 경계 상자도 그에 맞추기 위해 동일한 연산을 거쳐야 한다.



텐서플로로 이미지 보강하기


이미지를 변환하기 위해 일부 구체적인 예와 함께 텐서플로가 제공하는 유용한 도구를 알아보자.


텐서플로 이미지 모듈

일반적 이미지 프레임워크

  • OpenCV
  • Python Image Library(PIL)

머신러닝 특화 이미지 패키지

  • imgaug
  • Augmentor
  • ImageDataGenerator(케라스)

텐서플로 자체 이미지 처리 모듈

  • tf.image


tf.image는 tf.data 파이프라인과 자연스럽게 통합될 수 있다.

tf.image의 함수는 대부분 쌍으로 존재한다. 즉, 하나는 연산을 고정된 버전으로 구현하고(예: central_crop(), flip_left_right(), adjust_jpeg_quality() 등) 다른 하나는 랜덤 버전으로 구현한다(예: random_crop(), random_flip_left_right(), random_jpeg_quality() 등).

랜덤 버전 함수는 일반적으로 어떤 변환이 무작위로 샘플링되는지부터 값의 범위를 인수로 취한다(tf.image.random_jpeg_quality()의 경우 매개변수로 min_jpeg_quality와 max_jpeg_quality와 같이).

tf.image 함수는 이미지 텐서에 직접 적용될 수 있으므로 tf.data 파이프라인 내에서 온라인 보강을 위해 사용하는 것이 좋다.



예제: 자율 주행 애플리케이션을 위한 이미지 보강

다음은 이미지 쌍에 동일한 일련의 기하학적 변환이 적용되게 하는 예이다.

img_dim, img_ch = tf.shape(img)[-1:-3], tf.shape(img)[-1]
# channel 축 따라 이미지 쌍을 결합/연결
stacked_imgs = tf.concat([img, tf.cast(gt_img, img.dtype)], -1)
# 랜덤 연산, 예를 들어 좌우 반전을 적용:
stacked_imgs = tf.image.random_flip_left_right(stacked_imgs)
# ...또는 무작위로 자르기(예를 들어, 이미지의 80~100% 유지):
rand_factor = tf.random.uniform([], minval=0.8, maxval=1.)
crop_shape = tf.cast(tf.cast(img_dim, tf.float32) * rand_factor, tf.int32)
crop_shape = tf.concat([crop_shape, tf.shape(stacked_imgs)[-1]], axis=0)
stacked_imgs = tf.image.random_crop(stacked_imgs, crop_shape)
# [...] (추가로 기하학적 변환을 적용)
# 두 개의 보강된 텐서를 복원하기 위해 연결 해제:
img = stacked_imgs[..., :img_ch]
gt_img = tf.cast(stacked_imgs[..., img_ch:], gt_img.dtype)
# 예를 들어, 픽셀 도메인에서 다른 변환을 적용:
img = tf.image.random_brightness(image, max_delta=0.15)


tf.image의 기하학적 함수 대부분은 이미지가 가질 수 있는 채널 수에 관련한 제약 사항이 전혀 없기 때문에 이미지에 동일한 기하 연산을 수행하기 위해 간단히 ‘사전에 채널 축을 따라 이미지를 연결’하기만 하면 된다.

또한 이 예제는 모든 변환이 입력 이미지와 그 레이블 맵 모두에 적용돼야 하는 것은 아니라는 것을 말해준다. 레이블 맵의 밝기나 채도를 조정하려고 하는 일은 이치에 맞지 않는다.


마지막으로 다시 한번 강조하지만, 항상 데이터 보강 절차를 고려해야 한다. 대규모 데이터셋에서 훈련할 대도 이미지를 보강하면 모델을 더 견고하게 만들 수 있다. 단, 랜덤 변환을 신중하게 선택하고 적용한다면 말이다.

Leave a comment