본문 바로가기

AI/Computer Vision

[실전! 컴퓨터 비전을 위한 머신러닝] 06. 전처리

728x90
반응형

전처리를 하는 이유

모양 변환, 데이터 품질 변환, 모델 품질 향상 등의 이유
 
모양 변환

tf.keras.layers.Flatten(input_shape =(512,256,3))

모든 레이어는 생성 당시에 정한 명세에 맞는 이미지를 필요로함
512*256*3=393,216개의 입력 노드를 갖도록 만들어짐
 
데이터 품질 개선
햇빛이 비치는곳 / 그렇지 않은 곳 = > 밝기에 차이 생김
지구 상의 지점에 비치는 햇빛을 고려해 각 픽셀값을 정규화 할 수 있음
 
모델 품질 향상
픽셀값 조정, 데이터셋 증강


크기와 해상도

5장의 꽃 이미지 사용
이미지 크기가 제각각임

 
케라스 전처리 레이어 사용
입력레이어에서 예상하는 모양에 맞게 바꾸는 전처리를 진행해야 함

img = tf.image.resize(img, [IMG_HEIGHT, IMG_WIDTH])

 
Resizing 전처리 레이어 사용
순차형(Sequential) 모델 만들 수 있음

preproc_layers = tf.keras.Sequential([
    tf.keras.layers.experimental.preprocessing.Resizing(
        height=IMG_HEIGHT, width=IMG_WIDTH,
        input_shape=(None, None, 3))
    ])

 
케라스 순차형 모델 - 4차원 텐서를 예상

def apply_preproc(img, label):
    # add to a batch, call preproc, remove from batch
    x = tf.expand_dims(img, 0)
    x = preproc_layers(x)
    x = tf.squeeze(x, 0)
    return x, label

첫번째 축에서 이미지에 차원을 추가하고 sqeeze()를 사용해 결과에서 배치 차원을 제거함

▶ 모든 이미지의 크기가 같아짐
꽃이 납작해지거나, 길쭉해지는 문제점 생김
 
 
텐서플로 이미지 모듈 사용
이미지를 원하는 종횡비로 잘라내고 크기를 조정하면서 종횡비를 유지할 수 있음

def apply_preproc(img, label):
    return tf.image.resize_with_pad(img, 2*IMG_HEIGHT, 2*IMG_WIDTH), label

원하는 종횡비로 자르고, 가장자리를 0으로 채우는 패딩 적용
 

이미지의 너비와 높이가 2배로 커짐 & 검은색 테두리 생김
 
중앙 크롭 진행!
 
케라스와 텐서플로를 혼용
텐서플로 resize_with_pad()와 케라스의  CenterCrop 기능 혼용

preproc_layers = tf.keras.Sequential([
    tf.keras.layers.Lambda(lambda img:
                           tf.image.resize_with_pad(
                               img, 2*IMG_HEIGHT, 2*IMG_WIDTH),
                           input_shape=(None, None, 3)),
    tf.keras.layers.experimental.preprocessing.CenterCrop(
        height=IMG_HEIGHT, width=IMG_WIDTH)
    ])

종횡비를 유지하면서 이미지 크기 통일
 
모델 훈련
입력 이미지 크기가 다르기 때문에 배치 처리 수행 전에 수집 파이프라인에서 전처리를 거침
모바일넷 전이 학습 모델 사용

결과가 더 나빠 보임


 

훈련-서빙 왜곡

훈련 중에 똑같은 연산 세트를 이미지에 대해 수행해야 함
추론 파이프 라인에서 클라이언트가 제공한 이미지에 대해 모든 연산을 수행해야 함
 

  1. 훈련 및 추론 파이프라인에서 모두 호출되는 함수를 전처리에 넣는다.
  2. 전처리를 모델 자체에 통합한다.
  3. tf.transform으로 아티팩트를 만들고 재사용한다.

 
함수 재사용
재사용되는 함수로 모든 전처리를 모아서 _Preprocessorr라는 클래스에 넣을 수 있음

def create_preproc_image(filename):
    preproc = _Preprocessor()
    img = preproc.read_from_jpegfile(filename)
    return preproc.preprocess(img)

전처리는 이미지를 일정한 크기로 변환하고, 배치에 넣고, 전처리 레이어를 호출하고, 배치에서 결과를 얻어내는 과정으로 이루어짐

    def preprocess(self, img):
        # add to a batch, call preproc, remove from batch
        x = tf.expand_dims(img, 0)
        x = self.preproc_layers(x)
        x = tf.squeeze(x, 0)
        return x

 
 
모델 내 전처리
_Preprocessor 클래스에서 중앙 크롭 기능을 가져와 모델 자체로 이동함

    layers = [
      tf.keras.layers.experimental.preprocessing.CenterCrop(
          height=IMG_HEIGHT, width=IMG_WIDTH,
          input_shape=(2*IMG_HEIGHT, 2*IMG_WIDTH, IMG_CHANNELS),
      ),
      hub.KerasLayer(...),
      tf.keras.layers.Dense(...),
      tf.keras.layers.Dense(...)
    ]

 
tf.transform 사용
효울성을 위해 이미 크롭한 이미지를 TFRecord에 기록함

 

더보기

tf.transformd을 사용하기 위해서는 데이터를 빔 파이프라인 형식으로 바꿔야함

https://www.tensorflow.org/tfx/transform/get_started#data_formats_and_schema

빔 파이프라인 작성
JPEG 파일을 텐서플로 레코드로 변환하는 데 사용한 파이프라인과 유사함
차이점 - 텐서플로 확장 기능을 활용해 CSV 리더를 생성

    IMG_BYTES_METADATA = tft.tf_metadata.dataset_metadata.DatasetMetadata(
        tft.tf_metadata.dataset_schema.schema_utils.schema_from_feature_spec({
            'img_bytes': tf.io.FixedLenFeature([], tf.string),
            'label': tf.io.FixedLenFeature([], tf.string),
            'label_int': tf.io.FixedLenFeature([], tf.int64)
        })
    )

 
데이터 변환
데이터를 변환하기 위해 원본 데이터와 메타데이터를 tft_preprocess()라고 하는 함수에 전달함

            transformed_dataset, transform_fn = (
                raw_dataset | 'tft_img' >> tft_beam.AnalyzeAndTransformDataset(tft_preprocess)
            )

 
변환 저장
변환 함수

                transform_fn | 'write_tft' >> tft_beam.WriteTransformFn(
                    os.path.join(OUTPUT_DIR, 'tft'))

예측시 모든 크기 조정 및 전처리 연산이 로드되어 클라이언트에서 보낸 이미지 바이트에 적용됨
 
tf.transform의 장점
tf.data 파이프라인에 전처리 코드를 넣거나 모델의 일부로 포함하는 것과 같은 절충안을 피할 수 있음
 

  • 전처리는 한 번만 발생한다.
  • 훈련 파이프라인은 이미 전처리된 이미지를 읽기 때문에 빠르다. 
  • 전처리 함수들은 모델 아티팩트에 저장된다.
  • 서빙 함수는 모델 아티팩트를 로드하고 모델을 호출하기 전에 전처리를 적용할 수 있다.

 
 
데이터 증강
원하는 모델이 클수록 데이터가 더 많이 필요함
 
데이터 부족 문제에 대한 데이터 공간 솔루션. 학습 데이터 세트의 크기와 품질을 향상하는 일련의 기술
 
무작위 줌, 크롭, 뒤집기, 회전 등과 같은 공간적 변환
밝기, 색조 등을 변경하는 색상 왜곡
이미지의 다른 부분을 무작위로 마스킹하거나 지우는 것과 같은 정보 삭제
 
공간적 변환
이미지를 뒤집거나 회전해도 이미지의 본질은 바뀌지 않음
실제 데이터에서 이러한 종류의 변형을 처리할 수 있는 보다 강건한 모델을 만들 수 있음
 
※ 자동차 사진을 수직으로 뒤집는 것은 그리 좋지 않음
1. 프로덕션에서 모델이 거꾸로 된 이미지를 올바르게 분류할 것으로 기대하지 않음
2. 사진이 수직으로 뒤집혀져 있어 머신러닝 모델이 수직 대칭이 아닌 운전석 등의 특징을 식별하기 어려움
 
RandomFlip 레이어
- 훈련 중에 무작위로 이미지를 뒤집거나 원래 방향으로 유지함

      tf.keras.layers.experimental.preprocessing.RandomFlip(
          mode='horizontal',
          name='random_lr_flip/none'
      ),

mode - 허용되는 뒤집기 유형을 제어함
 
RandomCrop 레이어
- 훈련시 무작위 크롭을 수행하고, 추론시 CentorCrop처럼 작동함

    layers = [
      tf.keras.layers.experimental.preprocessing.RandomCrop(
          height=IMG_HEIGHT//2, width=IMG_WIDTH//2,
          input_shape=(IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS),
          n

 
정확도 0.85 -> 0.86 으로 개선됨 & 안정적인 훈련 곡선


 

색상 왜곡

각 이미지는 조명 측면에서 달라질 수 있음. 훈련 이미지의 밝기, 대비, 채도 등을 무작위로 변경해 데이터를 증강시킴

class RandomColorDistortion(tf.keras.layers.Layer):
    def __init__(self, contrast_range=[0.5, 1.5], 
                 brightness_delta=[-0.2, 0.2], **kwargs):
        super(RandomColorDistortion, self).__init__(**kwargs)
        self.contrast_range = contrast_range
        self.brightness_delta = brightness_delta

 
훈련 모드가 아닌 경우, 단순히 원본 이미지를 반환함
훈련 모드일 경우 난수 2개를 생성함 (하나는 이미지 내의 대비 조정, 하나는 밝기 조정)

 

    layers = [
      tf.keras.layers.experimental.preprocessing.RandomCrop(
          height=IMG_HEIGHT//2, width=IMG_WIDTH//2,
          input_shape=(IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS),
          name='random/center_crop'
      ),
      tf.keras.layers.experimental.preprocessing.RandomFlip(
          mode='horizontal',
          name='random_lr_flip/none'
      ),
      RandomColorDistortion(name='random_contrast_brightness/none'),

 
정확도가 0.86 -> 0.88로 개선됨. 훈련 및 검증 곡선이 거의 비슷하게 유지됨
 
정보 삭제
이미지에서 정보를 삭제함으로써 훈련 과정이 더욱 탄력적이 되게 하고 모델이 이미지의 중요한 특징에 주목하게 도움
 
컷아웃
- 훈련 중에 입력의 정사각형 영역을 무작위로 마스킹함 
모델이 이미지의 정보가 없는 부분을 무시하고 구별되는 부분에 주목하는 방법을 배우는데 도움이 됨
 
믹스업
- 한쌍의 훈련 이미지를 선형으로 보간하고 해당 보간 된 라벨 값을 라벨로 할당함
 
컷믹스
- 컷아웃 + 믹스업의 조합. 
서로 다른 훈련 이미지에서 패치를 잘라서 패치들의 영역에 비례해 실측 라벨을 섞음
 
그리드마스크
- 삭제 된 영역의 밀도와 크기를 제어하면서 균일하게 분포된 정사각형 영역을 삭제함
균일하게 분포된 정사각형 영역은 배경이 되는 경향이 있음
 

def augment_mixup(img, label):
    fracn = np.rint(MIXUP_FRAC * len(img)).astype(np.int32)
    wt = np.random.uniform(0.5,0.8)

 
fracn 파라미터 - 믹스업해야 하는 배치의 이미지 수
wt 가중치 - 함수에서 이미지 쌍을 보간하기 위함
 
보간하려면 이미지 쌍 필요

    img1, label1 = img[:n], label[:n]
    img2, label2 = img[1:n+1], label[1:n+1] # offset by one
    interp_img = _interpolate(img1, img2, t)
    interp_label = _interpolate(label1, label2, t)

 
 
shuffle() 단계를 추가하여 배치가 각 에포크에서 서로 다른지 확인함

shuffle(8*batch_size).batch(batch_size, drop_remainder=True).map(augment_mixup)

 
라벨을 희소 정수로 유지하면 라벨에 삽입하는 것이 불가능함
read_from_tf() 메서드에서 원핫 인코딩 수행
손실함수를 CategoricalCrossentropy()로 변경
 

정확도와 손실이 훈련 정확도보다 나음
- 훈련 데이터셋이 검증 데이터셋보다 더 어렵다는 것을 인식할 때 논리적임


 

입력 이미지 형성

전처리 파이프 라인을 사용해 각 입력을 여러 이미지로 나눈 다음 훈련 및 추론을 위해 모델에 제공하는 것이 도움이 될 수 있음
 
모델에 입력되는 이미지를 형성하는 방법
- 타일링(tiling)
매우 큰 이미지가 있고 큰 이미지의 일부에 대해 예측을 수행한 다음 조합할 수 있는 모든 분야에서 유용함
 
ex. 지구 이미지에서 산불을 식별
 
개별 픽셀에 산불이 포함되어 있는지 예측함
모델에 대한 입력은 예측할 픽셀을 바로 둘러싸고 있는 원본 이미지의 일부인 타일이 됨
 
화재 위치를 가져와 감지된 이미지 크기의 이미지에 매핑

fire_label = np.zeros((338, 600))
for loc in fire_locations:
    fire_label[loc[0]][loc[1]] = 1.0

 
타일 생성

    tiles = tf.image.extract_patches(
        images=images,
        sizes=[1, TILE_HT, TILE_WD, 1],
        strides=[1, TILE_HT//2, TILE_WD//2, 1],
        rates=[1, 1, 1, 1],
        padding='VALID')

 
이미지 타일의 라벨은 해당 라벨 타일 내에서 최댓값을 찾아 얻음

 
 
 

728x90
반응형