케라스(Keras) 모듈을 이용하여 심층학습을 시도할 때, 디스크에 저장된 이미지 파일을 불러오는 것이 가장 간단하며 쉬운 방법이다. 이를 위해서는 image_data_set_from_directory()
함수를 이용하여 섞기 과정과 학습 예제, 입증 예제를 분할하여 예제를 불러올 수 있다.
from tensorflow.keras.preprocessing import image_dataset_from_directory
BATCH_SIZE = 32
IMG_SIZE = (160, 160)
directory = "location/"
train_dataset = image_dataset_from_directory(directory,
shuffle=True,
batch_size=BATCH_SIZE,
image_size=IMG_SIZE,
validation_split=0.2,
subset='training',
seed=42)
입증 예제를 위한다면 subset
을 validation
으로 설정할 수 있다.
학습의 효율을 가장 높이기 위해선 가능한 많은 양의 데이터를 디스크에서 불러와 GPU에 전달을 하는 것이 중요하다. 하지만 이 상황에서 CPU에 부하가 커지고 실제로 원하는 만큼의 데이터 로딩이 어려워 지는 메모리 병목현상(memory bottleneck)이 발생하게 된다.
이를 해결하게 위해서 학습을 진행하면서 배치의 사이즈의 배수 혹은 그보다 많은 양의 데이터를 지속적으로 읽는 과정이 필요하다. 이러한 미리읽기(prefetch) 과정을 시도하여 병목현상을 줄일 수 있다. 위의 사진은 읽기(read)를 하면서 학습(train)을 하고 있는 파이프라이닝(pipelining)의 모습이다.
AUTOTUNE = tf.data.experimental.AUTOTUNE
train_dataset = train_dataset.prefetch(buffer_size=AUTOTUNE)
AUTOTUNE
을 통해서 미리읽기를 하는 양의 데이터를 적절히 조절할 수 있다.
이미지 파일을 증강하여 수정하는 것은 컴퓨터 비전분야에서 흔히 있는 일로, 부족한 데이터의 양을 보완하고 학습할 예제들의 다양성을 증가시킬 수 있는 매우 일반적인 방법에 해당한다. 이미지 파일의 증강은 tf.keras.Sequential([])
을 이용하여 증강 방식을 선택할 수 있다.
def data_augmenter():
data_augmentation = tf.keras.Sequential([])
data_augmentation.add(RandomFlip('horizontal'))
data_augmentation.add(RandomRotation(0.2))
return data_augmentation
위의 코드에서는 수평방향으로 뒤집고 회전을 시키는 증강 방법을 Sequential
에 추가하였다.
모바일 신경망(MobileNetV2)은 이미지넷(ImageNet)에서 학습되고 휴대기기와 같은 저전력 기기에서 사용되도록 최적화된 신경망이다. 모바일 신경망의 특징은 다음과 같이 정리할 수 있다.
모바일 신경망(MobileNetV2)에서는 입력값의 차원이 작은 병목 구조를 가지게 된다. 입력값은 확장 과정에서 채널의 수가 증가하고 이는 계산과정을 풍부하게(richer computation) 하는 결과를 가져오게 된다. 이후 깊이끼리의 합성곱과 점끼리의 합성곱으로 다시 수축을 시켜서 한 층의 연산이 끝나게 된다.
위에서 설명한 병목 구조 외에도 지름길을 만들어서 잔여 블록을 형성하게 된다. 잔여블록의 경우는 잔여 신경망의 기능과 마찬가지로 학습의 속도와 예측의 성능을 높이기 위한 방향으로 진행이 되며, 비선형 활성함수는 적용하지 않는다.
전이 학습에서 미리 학습된 모델을 사용할 시에는 학습된 가중치를 그대로 사용하는 것이 가장 좋은 아이디어이다.
IMG_SHAPE = IMG_SIZE + (3,)
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
include_top=True,
weights='imagenet')
위의 과정은 미리 훈련된 모델을 로드하는 과정이다. base_model.summary()
를 통해서 기반 모델의 정보를 출력할 수 있다. 기반 모델인 모바일 신경망이 어떤식으로 결과물을 산출하는지 확인을 할 필요가 있다.
image_batch, label_batch = next(iter(train_dataset))
feature_batch = base_model(image_batch)
feature_batch
의 차원은 (32, 1000)
으로 배치의 크기와 모델이 사전에 학습한 집단의 수를 나타낸다. 하지만 전이학습을 통해서 구별하고자 하는 데이터의 라벨이 없을 경우가 다반사이기 때문에 일부 층을 수정하여 미세 조정(fine tuning)을 해야한다.
사전의 훈련된 모델을 이요하여 다시 미세하게 훈련을 시킬 필요가 있다. 이를 위해선 다음과 같은 과정이 필요하다.
include_top
을 False
로 설정한다.base_model.trainable=False
base_model(x, training=False)
를 통해 배치 정규화 과정에서 인지를 추적하는 것을 방지한다.def alpaca_model(image_shape=IMG_SIZE, data_augmentation=data_augmenter()):
input_shape = image_shape + (3,)
base_model = tf.keras.applications.MobileNetV2(input_shape=input_shape,
include_top=False, # <== Important!!!!
weights='imagenet') # From imageNet
# Freeze the base model by making it non trainable
base_model.trainable = False
# create the input layer (Same as the imageNetv2 input size)
inputs = tf.keras.Input(shape=input_shape)
# apply data augmentation to the inputs
x = data_augmentation(inputs)
# data preprocessing using the same weights the model was trained on
x = preprocess_input(x)
# set training to False to avoid keeping track of statistics in the batch norm layer
x = base_model(x, training=False)
# Add the new Binary classification layers
# use global avg pooling to summarize the info in each channel
x = tfl.GlobalAveragePooling2D()(x)
#include dropout with probability of 0.2 to avoid overfitting
x = tfl.Dropout(rate=0.2)(x)
# create a prediction layer with one neuron (as a classifier only needs one)
prediction_layer = tfl.Dense(units=1)
outputs = prediction_layer(x)
model = tf.keras.Model(inputs, outputs)
return model
위의 코드는 알파카인지 알파카가 아닌지 분류하는 신경망 모델을 새롭게 만든 것이다.
마지막 단계인 미세 튜닝을 적용할 차례이다. 정확도를 높이기 위해서 마지막 층에 최적화를 한번 더 진행을 하게된다. 특히 학습 비율(learning rate)을 기존의 훈련 방식보다 작게 학습을 할 경우 천천히 새로운 데이터에 대해서 더 정확하게 학습하게 된다.
위의 방식을 적용하기 위해선 마지막 층에대한 해동(unfreezing) 과정을 거쳐서 마지막 층에 대해서 매우 작은 학습 비율로 재학습을 시켜야 한다. 이에 대한 직관적인 이해는 이전 단계에서는 낮은 수준(low-level)에 대한 신경망을 학습이 되어 있고 이후 새로운 데이터로 학습을 하면서 높은 수준(high-level)에 대한 특징들을 학습하게된다. 전이 학습에서는 낮은 수준의 특징은 사전 훈련 모델과 비슷한(같은) 수준으로 유지한 상태(freezing)에서 새로운 데이터에 대한 적응 훈련을 한다고(unfreezing) 이해할 수 있다.
어느 지점 부터 마지막 층으로 볼 것인가는 다소 까다롭고 임의적인 문제이다. 대략적인 추측 정도면 충분하다. 가장 중요한 사실은 이후 층(later layers)들이 해결하려는 문제에 대한 세부적인 특징을 파악하는 부분이라는 점이다.
base_model = model2.layers[4]
base_model.trainable = True
# Fine-tune from this layer onwards
fine_tune_at = 120
# Freeze all the layers before the `fine_tune_at` layer
for layer in base_model.layers[:fine_tune_at]:
layer.trainable = False
loss_function=tf.keras.losses.BinaryCrossentropy(from_logits=True)
optimizer = tf.keras.optimizers.Adam(lr=0.1*base_learning_rate)
metrics=['accuracy']
model2.compile(loss=loss_function,
optimizer = optimizer,
metrics=metrics)