본 포스팅은 Applied Deep Learning - Autoencoders 편을 참고하였습니다.
오토인코드는 입력과 출력이 동일한 피드포워드 신경망의 한 유형입니다.
입력을 의도적으로 낮은 차원의 '코드'로 압축, 이후에 이 압축된 '코드'라는 표현을 기반으로 출력을 재구성(reconstruct)합니다.
코드는 입력의 간결한 "요약" 또는 "압축"이며 '잠재 공간 포현 (latent space representation)'이라고도 합니다.
오토인코더의 구성요소
오토 인코더는 [ 인코더, 코드, 디코더 ] 총 3개의 요소로 구성되어 있습니다.
인코더는 입력을 압축하고,
압축된 입력은 코드가 됩니다.
디코더는 이 코드를 사용하여 입력을 재구성합니다.
아래는 오토인코더의 동작 예시입니다.
MNIST의 데이터인 숫자 4가 Encoder를 통과 -> code라는 직관적으로 알 수 없게 표현 -> 디코더가 code를 해독 & 재구성하여 출력 -> 4라는 숫자로 출력되는 것을 볼 수 있음
오토인코더를 구축하기 위해서는 크게 3개가 필요합니다 --> [ 인코딩 방법, 디코딩 방법, 입출력을 비교할 손실함수(loss function) ]
오토인코더의 특징
오토인코더를 왜 사용할까요?
어차피 입력과 출력이 동일한 것을 의미하고, 비슷한 형태라면 굳이 사용해야할 필요가 있을까요?
오토인코더는 차원을 축소하는데 의미가 있습니다. 차원은 추후 모델링을 할 때 연산시간을 비롯한 모델링의 전반적인 부분에 큰 영향을 미치는 요소입니다. 따라서 저차원일 경우 어느정도 정보 손실은 있겠지만, 경우에 따라서 효율적으로 실무에 맞는 속도로 데이터를 학습할 수 있습니다.
또한 오토인코더는 노이즈를 제거하는 효과가 있습니다.
아래와 같이 노이즈가 가득한 이미지를 선명하게 만들 수 있습니다.
위에서는 "오토인코더가 어떻길래 사용하고 배워야하는가?"에 대해서 다뤘다면, 아래는 오토인코더 자체에 대한 특징입니다. 크게 세가지 포인트를 말할 수 있습니다.
- 데이터 특정(data speicific): 오토인코더는 학습된 데이터와 유사한 데이터를 의미 있게 압축할 수 있습니다. 주어진 학습 데이터에 대해 특징을 학습하기 때문에 gzip과 같은 표준 데이터 압축 알고리즘과 다릅니다. 따라서 손글씨 숫자로 학습한 오토인코더가 풍경 사진을 압축하는 것을 기대할 수는 없습니다.
- 손실(Loss): 오토인코더의 출력은 입력과 정확히 동일하지 않을 것입니다. 출력은 가까운 대신에 손상된 표현이 될 것입니다. 완전한 손실 없는 압축을 원한다면 오토인코더는 적합하지 않습니다.
- 비지도 학습(unsupervised): 오토인코더를 학습시키기 위해서는 별다른 방법이 필요하지 않습니다. 입력 데이터를 그냥 넣어주기만 하면 됩니다. 오토인코더는 명시적인 라벨이 필요하지 않으므로 비지도 학습 기술로 간주됩니다. 하지만 더 정확하게 말하면, 오토인코더는 학습 데이터로부터 자체 라벨을 생성하기 때문에 자기 지도 학습 기술입니다.
오토인코더 코드 구현
Tensorflow를 활용하여 오토인코더를 구현해보겠습니다.
우선 아래 코드를 추가하여 필요 라이브러리와 함수를 불러오겠습니다.
# 시각화를 위한 matplotlib 호출
# 연산을 위한 numpy 호출
import matplotlib.pyplot as plt
import numpy as np
# autoencoder 결과물 플롯 함수
def plot_autoencoder_outputs(autoencoder, x, n, dims):
"""
:param autoencoder: 학습된 autoencoder 모델
:param x: image를 그려볼 데이터
:param n: 몇 개의 image를 그릴것인지 n을 통해 전달
:param dims: 이미지 plot을 위해 1차원 배열을 2차원으로 바꿔주어야 함
:return:
"""
decoded_imgs = autoencoder.predict(x)
plt.figure(figsize=(10, 5))
for i in range(n):
# plot original image
ax = plt.subplot(2, n, i + 1)
plt.imshow(x[i].reshape(*dims))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# ax.set_title('Original Images')
if i == n // 2:
ax.set_title('Original Images')
# plot reconstruction
ax = plt.subplot(2, n, i + 1 + n)
plt.imshow(decoded_imgs[i].reshape(*dims))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# ax.set_title('Reconstructed Images')
if i == n // 2:
ax.set_title('Reconstructed Images')
plt.show()
데이터는 mnist를 이용할 것입니다.
Tensorflow 내장 데이터셋을 활용하겠습니다.
# 내장 데이터셋 호출을 위한 함수 호출
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# 데이터 차원 확인
print(X_train.shape) # (60000, 28, 28)
print(y_train.shape) # (60000, )
print(X_test.shape) # (10000, 28, 28)
print(y_test.shape) # (10000, )
예제 데이터를 하나 그려보겠습니다.
plt.imshow(X_train[1])
숫자 0이 플롯된 것을 확인할 수 있습니다.
Autoencoder는 기본적으로 Dense Layer를 활용합니다.
해당 레이어를 활용하기 위해서 2차원 데이터를 1차원 데이터인 배열로 바꿔주어야 합니다.
Tensorflow를 활용하면 Flatten을 통해서 펼쳐줄 수 있지만, 여기서는 numpy 를 통해 reshape을 진행하고, 데이터를 float 형태로 바꿔주겠습니다.
# 데이터 형태 및 유형 변경
X_train = X_train.reshape(-1, 28 * 28).astype(np.float32) / 255.0
X_test = X_test.reshape(-1, 28 * 28).astype(np.float32) / 255.0
이젠 모델링을 진행하겠습니다.
from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.models import Model
# dimension 설정
input_dim = X_train.shape[-1]
hidden_dim = 128
code_dim = 32
# 함수형 API 활용
input_img = Input(shape=(input_dim, ))
hidden_1 = Dense(hidden_dim, activation='relu')(input_img)
code = Dense(code_dim, activation='relu')(hidden_1)
hidden_2 = Dense(hidden_dim, activation='relu')(code)
output_img = Dense(input_dim, activation='sigmoid')(hidden_2)
# input과 output을 모델로 전달
autoencoder = Model(inputs=input_img, outputs=output_img)
# 모델 compile
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')
# 모델 학습 진행
autoencoder.fit(X_train, X_train, epochs=5)
학습 진행 시, 입력데이터와 출력데이터가 X_train으로 동일한 것에 주목합시다.
이젠 결과물을 plot 해보겠습니다.
plot_autoencoder_outputs(autoencoder, X_test, 5, (28, 28))
이렇게 거의 비슷한 결과물을 출력하는 것을 볼 수 있습니다.
Denoising Autoencoder
다음은 디노이징 오토인코더에 대해서 코드 구현해보겠습니다.
# 원본 데이터에 노이즈 추가
noise_factor = 0.4
X_train_w_noise = X_train + noise_factor * np.random.normal(size=X_train.shape)
X_test_w_noise = X_test + noise_factor * np.random.normal(size=X_test.shape)
# clip 함수를 통해 min, max 값을 넘어서는 값은 min과 max로 치환
X_train_w_noise = np.clip(X_train_w_noise, 0.0, 1.0)
X_test_w_noise = np.clip(X_test_w_noise, 0.0, 1.0)
# 노이즈 인풋과 원본 인풋 확인
n = 5
plt.figure(figsize=(10, 4.5))
for i in range(n):
# plot original image
ax = plt.subplot(2, n, i + 1)
plt.imshow(X_test[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
if i == n // 2:
ax.set_title('Original Images')
# plot image with noise
ax = plt.subplot(2, n, i + 1 + n)
plt.imshow(X_test_w_noise[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
if i == n // 2:
ax.set_title('Images with Noise')
위처럼 원본 데이터에 노이즈가 제대로 추가된 것을 확인할 수 있습니다.
이젠 오토인코더를 구현하여 노이즈를 제거한 상태로 이미지를 재구성해보겠습니다.
# 사실상 모델 구조는 일반 오토인코더와 동일합니다.
input_img = Input(shape=(input_dim, ))
hidden_1 = Dense(hidden_dim, activation='relu')(input_img)
code = Dense(code_dim, activation='relu')(hidden_1)
hidden_2 = Dense(hidden_dim, activation='relu')(code)
output_img = Dense(input_dim, activation='sigmoid')(hidden_2)
autoencoder = Model(inputs=input_img, outputs=output_img)
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')
autoencoder.fit(X_train_w_noise, X_train, epochs=5) # 입력은 노이즈, 출력은 원본
유일한 차이점은 오토인코더를 데이터에 학습(fit)시킬 때, 인풋을 노이즈 인풋으로 넣고, 출력을 원본 데이터로 넣는 것이 다르다고 할 수 있습니다.
마찬가지로 원본 - 노이즈 - 재구성 데이터를 플롯해보자면..
n = 5
plt.figure(figsize=(10, 7))
images = autoencoder.predict(X_test_w_noise)
for i in range(n):
# plot original image
ax = plt.subplot(3, n, i + 1)
plt.imshow(X_test[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
if i == n // 2:
ax.set_title('Original Images')
# plot noisy image
ax = plt.subplot(3, n, i + 1 + n)
plt.imshow(X_test_w_noise[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
if i == n // 2:
ax.set_title('Noisy Input')
# plot noisy image
ax = plt.subplot(3, n, i + 1 + 2*n)
plt.imshow(images[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
if i == n // 2:
ax.set_title('Autoencoder Output')
이렇게 노이즈를 제거한 채로 출력을 하였습니다.
'딥러닝 > VISION' 카테고리의 다른 글
[딥러닝] 합성곱 신경망 - 직관적으로 convolutional layer 깊이에 대해 이해하기 (0) | 2023.02.23 |
---|