본문 바로가기

AI 인공지능

인공지능 수첩 ( augmentation / Flipping / Gray scale / Saturation / Brightness / Rotation / Center Crop / 아핀 변환 / Random Crop / MedianBlur / ToGray와 MultiplicativeNoise / 텐서플로우 Random Augmentation API / test-time augmentation ..

반응형

 

augmentation

- 갖고 있는 데이터셋을 여러 가지 방법으로 증강시켜 실질적인 학습 데이터셋의 규모를 키우는 방법

- 딥러닝 및 컴퓨터 비전 작업에서 훈련된 모델의 품질을 높이기 위해 사용됨.

- 하드디스크에 저장된 이미지 데이터를 메모리에 로드한 후, 학습시킬 때 변형을 가하는 방법을 사용함.

- augmentation을 통해서 실제 입력값과 비슷한 데이터 분포를 만들어 낼 수 있음.

- augmentation에서는 배열을 이미지의 기본 형태로 사용함.

- augmentation의 기법으로는 Flipping, Gray scale, Saturation, Brightness, Rotation, Center Crop,

Random Crop, Mirroring, Color Shifting 등이 있음.

- GAN을 augmentation에 활용할 수도 있음. 전통적인 색상 변환 대신 '스타일 트랜스퍼 (style transfer)' 모델을 적용해 볼 수도 있음.


TensorFlow 라이브러리

Flipping

- 좌우 또는 상하로 이미지를 대칭 및 반전시키는 방법

- 물체 탐지(detection), 세그멘테이션(segmentation) 등에서는 라벨도 같이 대칭 및 반전을 해줘야 함.

- flip_left_right(좌우 반전)과 flip_up_down(상하 반전) 두 가지 종류가 있음.

flip_lr_tensor = tf.image.flip_left_right(image_tensor)

flip_ud_tensor = tf.image.flip_up_down(image_tensor)


 

Gray scale

- 3가지 채널(channel)을 가진 RGB 이미지를 하나의 채널을 갖도록 해줌.

- RGB 각각의 채널마다 가중치(weight)를 주어 가중합(weighted sum)을 함.

grayscaled = tf.image.rgb_to_grayscale(image)


 

Saturation

- RGB 이미지를 HSV( Hue(색조), Saturation(채도), Value(명도) )의 3가지 성분으로 색을 표현하고, saturation(채도) 채널에 offset을 적용하여 좀더 이미지를 선명하게 만들어 줌.

saturated = tf.image.adjust_saturation( image, 3 )


 

Brightness

- RGB 채널에서 값을 더해주면 밝아지고, 값을 빼면 어두워지는 성질을 이용하여 밝기를 변화시킴.

random_bright_tensor = tf.image.random_brightness( image_tensor, max_delta=128 )

random_bright_tensor = tf.clip_by_value( random_bright_tensor, 0, 255 )


 

Rotation

- 이미지의 각도를 변환해줌.

rotated = tf.image.rot90( image )


 

Center Crop

- 이미지 중앙을 기준으로 확대하여 자른 이미지를 사용함.

- 확대하더라도 원래의 형상을 유지해야 함.

- central_fraction얼마나 확대할지를 조절하는 매개변수임.

- central_fraction이 1.0인 경우는 원본 이미지와 같음.

- central_fraction의 범위는 랜덤하게 조절되도록 해야함.

def random_central_crop( image_tensor, range=(0, 1) ):

central_fraction = tf.random.uniform([1], minval=range[0], maxval=range[1], dtype=tf.float32)

cropped_tensor = tf.image.central_crop( image_tensor, central_fraction )

return cropped_tensor

plt.figure(figsize=(12, 15))

 


albumentations 라이브러리

- augmentation만을 모아서 제공하는 전문 라이브러리

아핀 변환 (Affine transform)

- 이미지 스케일(scale)조절, 평행이동, 회전 등의 변환을 줌.

import albumentations as A

transform = A.Compose([

A.Affine(rotate=(-45, 45),scale=(0.5,0.9),p=0.5)

])


 

Random Crop

transform = A.Compose(

[A.RandomCrop(width=256, height=256)]

)


 

MedianBlur

transform = A.Compose(

[A.MedianBlur( blur_limit=7, always_apply=False, p=0.5 )]

)

 


 

ToGray와 MultiplicativeNoise

transform = A.Compose(

[A.ToGray(p=1),

A.MultiplicativeNoise( multiplier=[0.5, 1.5], elementwise=True, per_channel=True, p=1 )]

)


 

텐서플로우 Random Augmentation API

- random_brightness()

- random_contrast()

- random_crop()

- random_flip_left_right()

- random_flip_up_down()

- random_hue()

- random_jpeg_quality()

- random_saturation()


test-time augmentation

- 여러 결과를 조합하기 위한 앙상블 방법 중 하나로서, 테스트 데이터셋에 augmentation을 적용함.

- 증강된 이미지를 여러번 보여준 다음, 각각의 단계에 대해서 예측값을 평균하고, 이 결과를 최종값으로 사용함.

def normalize_and_resize_img( image, label ):

image = tf.image.resize( image, [224, 224] )

return tf.cast( image, tf.float32 ) / 255., label

def augment( image,label ):

image = tf.image.​random_flip_left_right( image )

image = tf.image.random_brightness( image, max_delta=0.2 )

image = tf.clip_by_value( image, 0, 1 )

return image, label

# with_aug매개변수를 통해 augmentation의 적용여부를 결정함.

def apply_normalize_on_dataset( ds, is_test = False, batch_size = 16, with_aug = False ):

ds = ds.map(

normalize_and_resize_img,

num_parallel_calls=2

)

if not is_test and with_aug:

ds = ds.map(

augment,

num_parallel_calls=2

)

ds = ds.batch(batch_size)

if not is_test:

ds = ds.repeat()

ds = ds.shuffle(200)

ds = ds.prefetch( tf.data.experimental.AUTOTUNE )

return ds

def augment2( image, label ):

image = tf.image.central_crop( image, np.random.uniform(0.50, 1.00) )

image = tf.image.resize( image, [224, 224] )

return image, label

# include_top은 마지막 fully connected layer를 포함할지 여부임.

num_classes = ds_info.features["label"].num_classes

resnet50 = keras.models.Sequential([

keras.applications.resnet.ResNet50(

include_top=False,

weights='imagenet',

input_shape=(224, 224, 3),

pooling='avg',

),

keras.layers.Dense( num_classes, activation='softmax' )

])

# Augmentation을 적용한 데이터셋으로 학습시킬 ResNet

aug_resnet50 = keras.models.Sequential([

keras.applications.resnet.ResNet50(

include_top=False,

weights='imagenet',

input_shape=(224, 224, 3),

pooling='avg',

),

keras.layers.Dense(num_classes, activation='softmax')

])

(ds_train, ds_test), ds_info = tfds.load(

'stanford_dogs',

split=['train', 'test'],

as_supervised=True,

shuffle_files=True,

with_info=True,

)

ds_train_no_aug = apply_normalize_on_dataset( ds_train, with_aug=False )

ds_train_aug = apply_normalize_on_dataset( ds_train, with_aug=True )

ds_test = apply_normalize_on_dataset( ds_test, is_test=True )

EPOCH = 3

resnet50.compile(

loss='sparse_categorical_crossentropy',

optimizer=tf.keras.optimizers.SGD(lr=0.01),

metrics=['accuracy'],

)

history_resnet50_no_aug = resnet50.fit(

ds_train_no_aug, # augmentation 적용하지 않은 데이터셋 사용

steps_per_epoch = int( ds_info.splits['train'].num_examples / 16 ),

validation_steps = int( ds_info.splits['test'].num_examples / 16 ),

epochs=EPOCH,

validation_data=ds_test,

verbose=1,

use_multiprocessing=True,

)

aug_resnet50.compile(

loss='sparse_categorical_crossentropy',

optimizer=tf.keras.optimizers.SGD(lr=0.01),

metrics=['accuracy'],

)

history_resnet50_aug = aug_resnet50.fit(

ds_train_aug, # augmentation을 적용한 데이터셋 사용

steps_per_epoch=int( ds_info.splits['train'].num_examples/16 ),

validation_steps=int( ds_info.splits['test'].num_examples/16 ),

epochs=EPOCH,

validation_data=ds_test,

verbose=1,

use_multiprocessing=True,

)


Cutmix Augmentation

- 이미지 데이터를 자르고 섞는 방법

- 이미지를 섞는 부분과 섞은 이미지에 맞춰 라벨을 섞는 부분을 포함함.

1. 이미지 섞기

import matplotlib.pyplot as plt

for i, (image, label) in enumerate( ds_train_no_aug.take(1) ):

if i == 0:

image_a = image[0]

image_b = image[1]

label_a = label[0]

label_b = label[1]

break

plt.subplot(1,2,1)

plt.imshow(image_a)

plt.subplot(1,2,2)

plt.imshow(image_b)

plt.show()

def get_clip_box( image_a, image_b ):

image_size_x = image_a.shape[1]

image_size_y = image_a.shape[0]

 

x = tf.cast( tf.random.uniform([], 0, image_size_x ), tf.int32 )

y = tf.cast( tf.random.uniform([], 0, image_size_y ), tf.int32 )

width = tf.cast( image_size_x*tf.math.sqrt(1-tf.random.uniform([], 0, 1)), tf.int32 )

height = tf.cast( image_size_y*tf.math.sqrt(1-tf.random.uniform([], 0, 1)), tf.int32 )

 

x_min = tf.math.maximum(0, x-width//2)

y_min = tf.math.maximum(0, y-height//2)

x_max = tf.math.minimum(image_size_x, x+width//2)

y_max = tf.math.minimum(image_size_y, y+width//2)

 

return x_min, y_min, x_max, y_max

x_min, y_min, x_max, y_max = get_clip_box(image_a, image_b)

print('x : ', x_min, x_max)

print('y : ', y_min, y_max)

def mix_2_images( image_a, image_b, x_min, y_min, x_max, y_max ):

image_size_x = image_a.shape[1]

image_size_y = image_a.shape[0]

middle_left = image_a[y_min:y_max, 0:x_min, :] # image_b의 왼쪽 바깥 영역

middle_center = image_b[y_min:y_max, x_min:x_max, :] # image_b의 안쪽 영역

middle_right = image_a[y_min:y_max, x_max:image_size_x, :] # image_b의 오른쪽 바깥 영역

middle = tf.concat([middle_left,middle_center,middle_right], axis=1)

top = image_a[0:y_min, :, :]

bottom = image_a[y_max:image_size_y, :, :]

mixed_img = tf.concat([top, middle, bottom],axis=0)

 

return mixed_img

mixed_img = mix_2_images(image_a, image_b, x_min, y_min, x_max, y_max)

plt.imshow(mixed_img.numpy())

plt.show()

2. 라벨 섞기

# CutMix에서는 면적에 비례해서 라벨을 섞어줌.

# 섞인 이미지의 전체 이미지 대비 비율을 계산해서 두 가지 라벨의 비율로 더해줌.

def mix_2_labels( label_a, label_b, x_min, y_min, x_max, y_max, num_classes=120 ):

image_size_x = image_a.shape[1]

image_size_y = image_a.shape[0]

mixed_area = (x_max-x_min) * (y_max-y_min)

total_area = image_size_x * image_size_y

ratio = tf.cast( mixed_area / total_area, tf.float32 )

if len( label_a.shape )==0:

label_a = tf.one_hot( label_a, num_classes )

if len( label_b.shape )==0:

label_b = tf.one_hot( label_b, num_classes )

mixed_label = (1-ratio) * label_a + ratio*label_b

return mixed_label

mixed_label = mix_2_labels( label_a, label_b, x_min, y_min, x_max, y_max )

mixed_label

# cutmix() 함수 구현

def cutmix(image, label, prob=1.0, batch_size=16, img_size=224, num_classes=120):

mixed_imgs = []

mixed_labels = []

for i in range(batch_size):

image_a = image[i]

label_a = label[i]

j = tf.cast(tf.random.uniform([],0, batch_size),tf.int32)

image_b = image[j]

label_b = label[j]

x_min, y_min, x_max, y_max = get_clip_box(image_a, image_b)

mixed_imgs.append(mix_2_images(image_a, image_b, x_min, y_min, x_max, y_max))

mixed_labels.append(mix_2_labels(label_a, label_b, x_min, y_min, x_max, y_max))

mixed_imgs = tf.reshape(tf.stack(mixed_imgs), (batch_size, img_size, img_size, 3))

mixed_labels = tf.reshape(tf.stack(mixed_labels), (batch_size, num_classes))

return mixed_imgs, mixed_labels


Mixup Augmentation

- 두 개 이미지의 픽셀별 값을 비율에 따라 섞어주는 방식

- CutMix보다 간단하게 이미지와 라벨을 섞어줌.

def mixup_2_images( image_a, image_b, label_a, label_b ):

ratio = tf.random.uniform([], 0, 1)

 

if len(label_a.shape)==0:

label_a = tf.one_hot(label_a, num_classes)

if len(label_b.shape)==0:

label_b = tf.one_hot(label_b, num_classes)

mixed_image= (1-ratio)*image_a + ratio*image_b

mixed_label = (1-ratio)*label_a + ratio*label_b

 

return mixed_image, mixed_label

mixed_img, mixed_label = mixup_2_images(image_a, image_b, label_a, label_b)

plt.imshow(mixed_img.numpy())

plt.show()

print(mixed_label)

def mixup( image, label, prob=1.0, batch_size=16, img_size=224, num_classes=120 ):

mixed_imgs = [ ]

mixed_labels = [ ]

for i in range( batch_size ):

image_a = image[i]

label_a = label[i]

j = tf.cast(tf.random.uniform([],0,batch_size), tf.int32)

image_b = image[j]

label_b = label[j]

mixed_img, mixed_label = mixup_2_images(image_a, image_b, label_a, label_b)

mixed_imgs.append(mixed_img)

mixed_labels.append(mixed_label)

mixed_imgs = tf.reshape(tf.stack(mixed_imgs), (batch_size, img_size, img_size, 3))

mixed_labels = tf.reshape(tf.stack(mixed_labels), (batch_size, num_classes))

return mixed_imgs, mixed_labels

반응형