본문 바로가기
Deep Learning/Hands On Machine Learning

17.4 Convolutional AutoEncoder

by 대소기 2021. 12. 26.

Convolutional AutoEncoder

* 이미지를 다룰 경우 convolution layer를 사용해야 더 좋은 결과를 얻을 수 있다는 것을 알고 있을 것이다. 2D 이미지를 1차원 vector로 flatten해서 Dense layer에 집어넣어 연산하는 것 보다 이미지를 그대로 사용해(정확히는 그대로 사용하는 것 처럼 처리) 공간상의 정보를 보존하는 방법을 사용하는 것이 훨씬 결과가 좋다.

* autoencoder도 마찬가지로 convolution layer를 사용하는 것이 이미지 처리에서 성능이 좋다. 이미지 처리를 위한 encoder decoder를 구성할 때 encoder부분에서는 일반적인 CNN 구조를 사용한다. convolution layer를 통과하고 maxpooling layer를 통과하며 이미지의 width, height는 점점 작아지고, depth는 늘어난다. 하지만 decoder 부분에서는 encoder의 작업을 반대로 해줘야 한다. 이미지의 depth를 점점 줄이고, width, height는 원본 이미지까지 늘려야 한다. 이 decoder부분에서의 연산은 keras.layers의 Conv2DTranspose로 구현 가능하다. 조금 더 이해를 돕기 위해서 이 decoder 부분의 연산에 대해 알아보자.

 

출처 : https://zzsza.github.io/data/2018/06/25/upsampling-with-transposed-convolution/

 

* 일반적인 convolution layer의 연산은 위와 같다. kernel을 sparse marix로 변환한 뒤 Input vector에 곱해서 output을 도출해낸다.

 

출처 : https://zzsza.github.io/data/2018/06/25/upsampling-with-transposed-convolution/

 

* 이 과정을 반대로 하기 위해서는 convolution matrix를 transpose하여 사용한다. 이를 통해 다시 원본 크기의 vector를 추출할 수 있다. 이렇게 특징 vector로부터 원본 vector를 추출해내는 것을 Transposed Convolution 이라고 한다. 우리가 사용할 decoder에서는 이 transpose convolution을 이용하게 된다. 주의할 점은 17.3에서 살펴봤던 가중치 묶기와 같이 encoder의 가중치를 decoder의 가중치와 동일하게 구성하는 것은 아니다. 반대 연산을 시행하지만, 가중치는 독립적으로 훈련된다는 점을 유의하자.

 

conv_encoder = keras.models.Sequential([
    keras.layers.Reshape([28, 28, 1], input_shape=[28, 28]),
    keras.layers.Conv2D(16, kernel_size=3, padding="SAME", activation="selu"),
    keras.layers.MaxPool2D(pool_size=2),
    keras.layers.Conv2D(32, kernel_size=3, padding="SAME", activation="selu"),
    keras.layers.MaxPool2D(pool_size=2),
    keras.layers.Conv2D(64, kernel_size=3, padding="SAME", activation="selu"),
    keras.layers.MaxPool2D(pool_size=2)
])
conv_decoder = keras.models.Sequential([
    keras.layers.Conv2DTranspose(32, kernel_size=3, strides=2, padding="VALID", activation="selu",
                                 input_shape=[3, 3, 64]),
    keras.layers.Conv2DTranspose(16, kernel_size=3, strides=2, padding="SAME", activation="selu"),
    keras.layers.Conv2DTranspose(1, kernel_size=3, strides=2, padding="SAME", activation="sigmoid"),
    keras.layers.Reshape([28, 28])
])
conv_ae = keras.models.Sequential([conv_encoder, conv_decoder])

conv_ae.compile(loss="binary_crossentropy", optimizer=keras.optimizers.SGD(learning_rate=1.0),
                metrics=[rounded_accuracy])
history = conv_ae.fit(X_train, X_train, epochs=5,
                      validation_data=(X_valid, X_valid))

# Epoch 1/5
# 1719/1719 [==============================] - 24s 8ms/step - loss: 0.3018 - rounded_accuracy: 0.9187 - val_loss: 0.2847 - val_rounded_accuracy: 0.9290
# Epoch 2/5
# 1719/1719 [==============================] - 13s 8ms/step - loss: 0.2756 - rounded_accuracy: 0.9414 - val_loss: 0.2730 - val_rounded_accuracy: 0.9453
# Epoch 3/5
# 1719/1719 [==============================] - 13s 8ms/step - loss: 0.2708 - rounded_accuracy: 0.9462 - val_loss: 0.2697 - val_rounded_accuracy: 0.9496
# Epoch 4/5
# 1719/1719 [==============================] - 13s 8ms/step - loss: 0.2682 - rounded_accuracy: 0.9491 - val_loss: 0.2685 - val_rounded_accuracy: 0.9492
# Epoch 5/5
# 1719/1719 [==============================] - 13s 8ms/step - loss: 0.2664 - rounded_accuracy: 0.9510 - val_loss: 0.2673 - val_rounded_accuracy: 0.9506

 

 

conv_encoder.summary()
conv_decoder.summary()

Model: "sequential_10"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
reshape_2 (Reshape)          (None, 28, 28, 1)         0         
_________________________________________________________________
conv2d (Conv2D)              (None, 28, 28, 16)        160       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 14, 14, 16)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 14, 14, 32)        4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 7, 7, 32)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 7, 7, 64)          18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 3, 3, 64)          0         
=================================================================
Total params: 23,296
Trainable params: 23,296
Non-trainable params: 0
_________________________________________________________________
Model: "sequential_11"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_transpose (Conv2DTran (None, 7, 7, 32)          18464     
_________________________________________________________________
conv2d_transpose_1 (Conv2DTr (None, 14, 14, 16)        4624      
_________________________________________________________________
conv2d_transpose_2 (Conv2DTr (None, 28, 28, 1)         145       
_________________________________________________________________
reshape_3 (Reshape)          (None, 28, 28)            0         
=================================================================
Total params: 23,233
Trainable params: 23,233
Non-trainable params: 0
_________________________________________________________________
show_reconstructions(conv_ae)
plt.show()