04_오토인코더
Python/인공지능

04_오토인코더

728x90

오토인코더는 대표적인 비지도학습을 위한 인공신경망 구조이다. 지도학습과는 다르게 데이터의 숨겨진 구조를 발견하는 것이 목표인 학습방법이다!

 

좀더 구체적으로 간면 오토인코더는 출력층의 노드 개수와 입력층의 노드 개수가 동일한 구조의 인공신경망이다. 

 

 

구조는 입력층, 은닉층, 출력층으로 전에 배웠던 ANN과 비슷한 구조를 가지고 있다.

 

특히 오토인코더는 입력층을 재구축하여 거의 비슷한 원본이 나오게 된다.

 

 

그렇다면 이런 재구축이 어떤 용도가 있을까? 자세히보면 은닉층의 노드 개수가 더 적은것을 확인할 수 있는데, 결과적으로 은닉층의 출력은 원본 데이터에서 불필요한 특징은 제거된, 압축된 특징을 학습하게 된다.

 

예를 들어 동물들의 길이, 몸무게, 주인의 나이, 주인의 몸무게 4개의 특징이 주어진 상황에서 데이터가 개인지 고양이인지 가정해야 한다고 생각해보자.

 

잘 생각해보면 주인의 나이와 주인의 몸무게는 잉여 특징임을 알 수 있다. 조금 극단적으로 생각하면, 오토 인코더 은닉층의 출력값은 학습을 거쳐 이과정을 자동적으로 거치게 되어 동물의 길이, 동물의 몸무게 2개의 특징만을 가지게 되는 것이다. 이러한 압축된 특징을 나타내는 은닉층의 출력값을 원본데이터 대신에 분류기의 입력에 넣는다면, 보다 좋은 성능을 기대할 수 있다.

 

 

자 그렇다면 이 오토인코더를 활용해서 MNIST데이터 재구축을 하는 과정을 진행해 보자

 

 

# -*- coding: utf-8 -*-
# AutoEncoder를 이용한 MNIST Reconstruction - Keras API를 이용한 구현

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

# MNIST 데이터를 다운로드 합니다.
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
# 이미지들을 float32 데이터 타입으로 변경합니다.
x_train, x_test = x_train.astype('float32'), x_test.astype('float32')
# 28*28 형태의 이미지를 784차원으로 flattening 합니다.
x_train, x_test = x_train.reshape([-1, 784]), x_test.reshape([-1, 784])
# [0, 255] 사이의 값을 [0, 1]사이의 값으로 Normalize합니다.
x_train, x_test = x_train / 255., x_test / 255.

# 학습에 필요한 설정값들을 정의합니다.
learning_rate = 0.02
training_epochs = 50    # 반복횟수
batch_size = 256        # 배치개수
display_step = 1        # 손실함수 출력 주기
examples_to_show = 10   # 보여줄 MNIST Reconstruction 이미지 개수
input_size = 784        # 28*28
hidden1_size = 256 
hidden2_size = 128

# tf.data API를 이용해서 데이터를 섞고 batch 형태로 가져옵니다.
train_data = tf.data.Dataset.from_tensor_slices(x_train)
train_data = train_data.shuffle(60000).batch(batch_size)

 

 

이 부분은 이제 익숙해졌을 것이다. 반복횟수나 인풋, 히든레이어 들의 사이즈 등을 정해준다.

 

def random_normal_intializer_with_stddev_1():
  return tf.keras.initializers.RandomNormal(mean=0.0, stddev=1.0, seed=None)

# tf.keras.Model을 이용해서 Autoencoder 모델을 정의합니다.
class AutoEncoder(tf.keras.Model):
  def __init__(self):
    super(AutoEncoder, self).__init__()
    # 인코딩(Encoding) - 784 -> 256 -> 128
    self.hidden_layer_1 = tf.keras.layers.Dense(hidden1_size,
                                                activation='sigmoid',
                                                kernel_initializer=random_normal_intializer_with_stddev_1(),
                                                bias_initializer=random_normal_intializer_with_stddev_1())
    self.hidden_layer_2 = tf.keras.layers.Dense(hidden2_size,
                                                activation='sigmoid',
                                                kernel_initializer=random_normal_intializer_with_stddev_1(),
                                                bias_initializer=random_normal_intializer_with_stddev_1())
    # 디코딩(Decoding) 128 -> 256 -> 784
    self.hidden_layer_3 = tf.keras.layers.Dense(hidden1_size,
                                                activation='sigmoid',
                                                kernel_initializer=random_normal_intializer_with_stddev_1(),
                                                bias_initializer=random_normal_intializer_with_stddev_1())
    self.output_layer = tf.keras.layers.Dense(input_size,
                                                activation='sigmoid',
                                                kernel_initializer=random_normal_intializer_with_stddev_1(),
                                                bias_initializer=random_normal_intializer_with_stddev_1())

  def call(self, x):
    H1_output = self.hidden_layer_1(x)
    H2_output = self.hidden_layer_2(H1_output)
    H3_output = self.hidden_layer_3(H2_output)
    reconstructed_x = self.output_layer(H3_output)

    return reconstructed_x

 

그후 인코딩 디코딩을 거쳐서

 

784 차원으로 풀어진걸 256,128개로 줄였다가 다시 늘리면서 오토인코더를 적용시킨다.

 

 

# MSE 손실 함수를 정의합니다.
@tf.function
def mse_loss(y_pred, y_true):
  return tf.reduce_mean(tf.pow(y_true - y_pred, 2)) # MSE(Mean of Squared Error) 손실함수

# 최적화를 위한 RMSProp 옵티마이저를 정의합니다.
optimizer = tf.optimizers.RMSprop(learning_rate)

# 최적화를 위한 function을 정의합니다.
@tf.function
def train_step(model, x):
  # 타겟데이터는 인풋데이터와 같습니다.
  y_true = x
  with tf.GradientTape() as tape:
    y_pred = model(x)
    loss = mse_loss(y_pred, y_true)
  gradients = tape.gradient(loss, model.trainable_variables)
  optimizer.apply_gradients(zip(gradients, model.trainable_variables))

# Autoencoder 모델을 선언합니다.
AutoEncoder_model = AutoEncoder()

# 지정된 횟수만큼 최적화를 수행합니다.
for epoch in range(training_epochs):
  # 모든 배치들에 대해서 최적화를 수행합니다.
  # Autoencoder는 Unsupervised Learning이므로 타겟 레이블(label) y가 필요하지 않습니다.
  for batch_x in train_data:
    # 옵티마이저를 실행해서 파라마터들을 업데이트합니다.
    _, current_loss = train_step(AutoEncoder_model, batch_x), mse_loss(AutoEncoder_model(batch_x), batch_x)
  # 지정된 epoch마다 학습결과를 출력합니다.
  if epoch % display_step == 0:
    print("반복(Epoch): %d, 손실 함수(Loss): %f" % ((epoch+1), current_loss))

# 테스트 데이터로 Reconstruction을 수행합니다.
reconstructed_result = AutoEncoder_model(x_test[:examples_to_show])
# 원본 MNIST 데이터와 Reconstruction 결과를 비교합니다.
f, a = plt.subplots(2, 10, figsize=(10, 2))
for i in range(examples_to_show):
  a[0][i].imshow(np.reshape(x_test[i], (28, 28)))
  a[1][i].imshow(np.reshape(reconstructed_result[i], (28, 28)))
f.savefig('reconstructed_mnist_image.png')  # reconstruction 결과를 png로 저장합니다.
f.show()
plt.draw()
plt.waitforbuttonpress()

그이후는 같다. 여기서 MSE손실함수를 구할때 정답이미지는 본인 자신으로 본다 즉, 입력값과 얼마나 같은지가 기준임을 생각하면 될 것 같다.

728x90