본문 바로가기
Computer Technology 기록부/인공지능 학습 기록부

딥러닝 레이어 이해하기 linear, Convolution

by Amins 2022. 2. 8.

딥러닝 모델 속 각 레이어(Linear, Convolution)의 동작 방식을 이해하고, 텐서플로우로 정의해보자


데이터

  • 이미지 데이터 표기법 -  Channel, Width, Height의 이니셜로 (C, W, H), (W, H, C)와 같이 표기

 

해당 컬러 그림,의 데이터는 (1920,1920,3)으로 표현가능


Layer?

하나의 물체가 여러 개의 논리적인 객체들로 구성되어 있는 경우, 이러한 각각의 객체를 layer라 칭함

 

Linear layer?

(Fully Connected Layer, Feedforward Neural Network, Multilayer Perceptrons, Dense Layer... 등 다양한 이름으로 불림)

  • 역할 - 선형 변환을 이용해 데이터를 특정 차원으로 변환
    • 저차원화 : 데이터 집약
    • 고차원화 : 데이터 더 풍부하게 표현
A -> B로선형 변환

변환 코드(Tensorflow)

import tensorflow as tf

batch_size = 64
boxes = tf.zeros((batch_size, 4, 2))     # Tensorflow는 Batch를 기반으로 동작하기에,
                                         # 우리는 사각형 2개 세트를 batch_size개만큼
                                         # 만든 후 처리를 하게 됩니다.
print("1단계 연산 준비:", boxes.shape)

first_linear = tf.keras.layers.Dense(units=1, use_bias=False) 
# units은 출력 차원 수를 의미합니다.
# Weight 행렬 속 실수를 인간의 뇌 속 하나의 뉴런 '유닛' 취급을 하는 거죠!

first_out = first_linear(boxes)
first_out = tf.squeeze(first_out, axis=-1) # (4, 1)을 (4,)로 변환해줍니다.
                                           # (불필요한 차원 축소)

print("1단계 연산 결과:", first_out.shape)
print("1단계 Linear Layer의 Weight 형태:", first_linear.weights[0].shape)

print("\n2단계 연산 준비:", first_out.shape)

second_linear = tf.keras.layers.Dense(units=1, use_bias=False)
second_out = second_linear(first_out)
second_out = tf.squeeze(second_out, axis=-1)

print("2단계 연산 결과:", second_out.shape)
print("2단계 Linear Layer의 Weight 형태:", second_linear.weights[0].shape)

결과

1단계 연산 준비: (64, 4, 2) --> (4,2)행렬 64개 
1단계 연산 결과: (64, 4)
1단계 Linear Layer의 Weight 형태: (2, 1)

2단계 연산 준비: (64, 4)
2단계 연산 결과: (64,)
2단계 Linear Layer의 Weight 형태: (4, 1)

 

 

 

 

변환 과정시 문제점

  • A,B에 대해 w 행렬 연산을 실시하니 값이 같아짐! (문제발생) - w 2개로는 역부족

 

w 파라미터 갯수를 늘려보자

파라미터 갯수를 늘려 보자

  • 각 사각형에 대해 독립적 정보가 생겨나기 시작함!

 

해당 그림 코드로 작성(Tensorflow)

import tensorflow as tf

batch_size = 64
boxes = tf.zeros((batch_size, 4, 2))

print("1단계 연산 준비:", boxes.shape)

first_linear = tf.keras.layers.Dense(units=3, use_bias=False)
first_out = first_linear(boxes)

print("1단계 연산 결과:", first_out.shape)
print("1단계 Linear Layer의 Weight 형태:", first_linear.weights[0].shape)

print("\n2단계 연산 준비:", first_out.shape)

second_linear = tf.keras.layers.Dense(units=1, use_bias=False)
second_out = second_linear(first_out)
second_out = tf.squeeze(second_out, axis=-1)

print("2단계 연산 결과:", second_out.shape)
print("2단계 Linear Layer의 Weight 형태:", second_linear.weights[0].shape)

print("\n3단계 연산 준비:", second_out.shape)

third_linear = tf.keras.layers.Dense(units=1, use_bias=False)
third_out = third_linear(second_out)
third_out = tf.squeeze(third_out, axis=-1)

print("3단계 연산 결과:", third_out.shape)
print("3단계 Linear Layer의 Weight 형태:", third_linear.weights[0].shape)

total_params = \
first_linear.count_params() + \
second_linear.count_params() + \
third_linear.count_params()

print("총 Parameters:", total_params)

결과

1단계 연산 준비: (64, 4, 2)
1단계 연산 결과: (64, 4, 3)
1단계 Linear Layer의 Weight 형태: (2, 3)

2단계 연산 준비: (64, 4, 3)
2단계 연산 결과: (64, 4)
2단계 Linear Layer의 Weight 형태: (3, 1)

3단계 연산 준비: (64, 4)
3단계 연산 결과: (64, 1)
3단계 Linear Layer의 Weight 형태: (4, 1)
총 Parameters: 13

Q. Parameter가 많은 것이 항상 높은 성능을 담보하는가?

A :  그렇지는 않다. 지나치게 많은 parameter는 overfitting을 야기한다. 

Bias(편향)

  • 선형변환된 값에 편향 파라미터 b를 더해주는 것으로 표현.
  • y=Wx+b 속의 b 가 바로 편향
  • Wx 에 단순히 더하기 때문에 편향 값은 형태가 (선형변환 결과 차원, ) 인 한 줄짜리 Weight로 정의
  • 보다 정확하게 함수를 데이터에 근사시키기 위해 고안

Convolution layer

가령 linear layer로 이미지 데이터를 받으면, 각 레이어 당 이미지의 픽셀값x깊이 + 편향치 만큼의 파라미터가 필요

이는 어마어마한 학습cost를 소모할 것이다 -> 이를 위한 해결책으로 convolution layer 등판!

커널을 통해 연산하는 컨볼루션 레이어

 

3 x 3 사이즈의 필터(커널)를 선언한 후, 이미지를 필터로 훑으며 각 픽셀을 곱하여 더하는 Convolution 연산을 표현

 

컨볼루션을 통해 이미지 데이터의 형태 변경됨!

 

  • stride? = image를 필터로 훑을 때, 한 칸씩 이동하며 훑을 수도 있지만, 두 칸, 세 칸씩 이동하며 훑기도 가능. 그것을 결정하는 값
  • padding? = 입력 이미지 가장자리에 0을 추가해 입력 이미지 크기를 키워 연산 후에도 이미지의 크기를 유지시키는 기법

 

  • Convolution layer의 filter의 역할
    • 목적에 도움이 되는 정보는 선명하게, 그렇지 않은 정보는 흐리게 만들기!(학습 통해 찾기)
    • 하나의 필터가 아닌 수십 개의 필터를 중첩
    • Convolution 레이어가[필터의 개수 x 필터의 가로 x 필터의 세로]로 이루어진 Weight를 가짐

linear layer보다 훨씬 적은 수의 weight 개수

필터크기는 추출하고 싶은 부분의 사이즈를 고려해 설정해야한다. 해당 그림에서 손을 찾는 필터를 만든다고 할 때, 인풋 크기에 비해 필터 사이즈가 작은 감이 있다. 필터 사이즈를 높이거나, 인풋 사이즈를 줄여야한다.

후자가 연산면에서 나을 듯하다. 인풋 이미지를 다운샘플링하는 방식으로 진행하면 더 효과적으로 검출이 가능할 것이다. 전자처럼 필터를 키우면 파라미터 수가 기하급수적으로 늘어 연산량이 매우 증가하고, 만약 극단적으로 필터 사이즈를 이미지 사이즈와 동일하게 한다면 그 순간 Convolution 레이어는 완벽하게 Linear 레이어와 같아진다.

accuracy에도 부적절한 영향을 미친다.

 

코드

import tensorflow as tf

batch_size = 64
pic = tf.zeros((batch_size, 1920, 1080, 3))

print("입력 이미지 데이터:", pic.shape)
conv_layer = tf.keras.layers.Conv2D(filters=16,
                                    kernel_size=(5, 5),
                                    strides=5,
                                    use_bias=False)
conv_out = conv_layer(pic)

print("\nConvolution 결과:", conv_out.shape)
print("Convolution Layer의 Parameter 수:", conv_layer.count_params())

flatten_out = tf.keras.layers.Flatten()(conv_out)
print("\n1차원으로 펼친 데이터:", flatten_out.shape)

linear_layer = tf.keras.layers.Dense(units=1, use_bias=False)
linear_out = linear_layer(flatten_out)

print("\nLinear 결과:", linear_out.shape)
print("Linear Layer의 Parameter 수:", linear_layer.count_params())

결과

입력 이미지 데이터: (64, 1920, 1080, 3)

Convolution 결과: (64, 384, 216, 16)
Convolution Layer의 Parameter 수: 1200

1차원으로 펼친 데이터: (64, 1327104)

Linear 결과: (64, 1)
Linear Layer의 Parameter 수: 1327104

Pooling layer

  • Receptive Field? : 인풋 데이터에 대하여 kernel(filter)가 한번에 볼 수 있는 크기 - 필터의 크기와 동일

해당 그림에서 각각의 색깔로 테두리된 부분이 필터가 한번 이미지를 살필때의 수용 영역

 

  • Max pooling? : max pooling 필터 내부 값 중 가장 큰 값만 추출하는 기법
Q . 만약 위와 같이 2 X 2 max pooling 레이어를 통해 위에 언급한 빨간색 포인트가 선택되었다면 max pooling 레이어의 Output에서의 Receptive Field의 크기는 얼마일까요?

A .  위 그림에서는 빨간색, 녹색, 파란색, 하얀색 포인트가 가지는 Receptive Field의 영역 전체가 max pooling 레이어의 한 포인트의 Receptive Field가 되므로 이때는 5 X 5의 Receptive Field를 가지게 된다.

max pooling의 필터 크기는 2x2로, 아웃풋에서 빨간 포인트를 선택하면 빨강,초록,파랑, 흰색의 포인트 즉 4칸의 output 영역이 선택된다. Receptive Field는 인풋 이미지에서 필터가 보는 영역을 의미하므로, 아웃풋의 해당 4칸의 인풋에서의 수용영역을 살피면 된다. stride가 2이므로 해당 필터들의 Reception field가 조금씩 겹쳐서, 5x5칸이 Receptive Field가 된다. 이 부분을 봐야 4칸의 output 이미지를 산출 할 수 있고, 여기에 max pooling이 적용된다.
 
Max pooling의 장점
  • translational invariance 효과
    • 이미지는 약간의 상하좌우 시프트가 생긴다고 해도 내용상 동일한 특징이 있는데, Max Pooling은 인접한 영역 중 가장 특징이 두드러진 영역 하나를 뽑는다. 이는 오히려 약간의 시프트 효과에도 불구하고 동일한 특징을 안정적으로 잡아낼 수 있는 긍정적 효과가 있다. 즉, Max pooling은 object 위치에 대한 오버피팅을 방지하고 안정적인 특징 추출 효과를 낳는다.
  • Non-linear 함수와 동일한 피처 추출 효과
    • Relu와 같은 Non-linear 함수도 마찬가지로 많은 하위 레이어의 연산 결과를 무시하는 효과를 발생시키지만, 그 결과 중요한 피처만을 상위 레이어로 추출해서 올려줌으로써 결과적으로 분류기의 성능을 증진시키는 효과를 가짐. Min/Max Pooling도 이와 동일한 효과
  • Receptive Field 극대화 효과
    • Max Pooling이 없이도 Receptive Field를 크게 하려면 Convolutional 레이어를 아주 많이 쌓아야 한다. 그 결과 큰 파라미터 사이즈로 인한 오버피팅, 연산량 증가, Gradient Vanishing 등의 문제를 감수해야 한다. 이런 문제를 효과적으로 해결하는 방법으로 꼽히는 두 가지 중 하나가 Max Pooling 레이어 사용이다. 다른 하나로는 Dilated Convolution이 있다.

Deconvolution 레이어

컨볼루션을 통해 집약된 정보가 다시 원본으로 복원될 수 있을까? 코드로 알아보기

 

순서

  • 패키지 임포트 및 MNIST 데이터셋 로딩
  • AutoEncoder 모델 구성
  • AutoEncoder 모델 훈련
  • AutoEncoder Reconstruction Test

 

 

  • 패키지 임포트 및 MNIST 데이터셋 로딩
import numpy as np
from tensorflow.keras.layers import Input, Dense, Conv2D, MaxPooling2D, UpSampling2D
from tensorflow.keras.models import Model
from tensorflow.keras.datasets import mnist
import json
import matplotlib.pyplot as plt #for plotting

# MNIST 데이터 로딩
(x_train, _), (x_test, _) = mnist.load_data()    # y_train, y_test는 사용하지 않습니다.

x_train = np.expand_dims(x_train, axis=3)
x_test = np.expand_dims(x_test, axis=3)

x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.

결과

Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
11501568/11490434 [==============================] - 0s 0us/step

 y_train, y_test를 사용하지 않음 -  AutoEncoder가 수행하는 Image Reconstruction Task는 x_train의 라벨이 바로 x_train 자신이 되기 때문(복원에 쓰이는 라벨이기 때문)

 

 

  • AutoEncoder 모델 구성
# AutoEncoder 모델 구성 - Input 부분
input_shape = x_train.shape[1:]
input_img = Input(shape=input_shape)

# AutoEncoder 모델 구성 - Encoder 부분
encode_conv_layer_1 = Conv2D(16, (3, 3), activation='relu', padding='same')
encode_pool_layer_1 = MaxPooling2D((2, 2), padding='same')
encode_conv_layer_2 = Conv2D(8, (3, 3), activation='relu', padding='same')
encode_pool_layer_2 = MaxPooling2D((2, 2), padding='same')
encode_conv_layer_3 = Conv2D(4, (3, 3), activation='relu', padding='same')
encode_pool_layer_3 = MaxPooling2D((2, 2), padding='same')

encoded = encode_conv_layer_1(input_img)
encoded = encode_pool_layer_1(encoded)
encoded = encode_conv_layer_2(encoded)
encoded = encode_pool_layer_2(encoded)
encoded = encode_conv_layer_3(encoded)
encoded = encode_pool_layer_3(encoded)

# AutoEncoder 모델 구성 - Decoder 부분
decode_conv_layer_1 = Conv2D(4, (3, 3), activation='relu', padding='same')
decode_upsample_layer_1 = UpSampling2D((2, 2))
decode_conv_layer_2 = Conv2D(8, (3, 3), activation='relu', padding='same')
decode_upsample_layer_2 = UpSampling2D((2, 2))
decode_conv_layer_3 = Conv2D(16, (3, 3), activation='relu')
decode_upsample_layer_3 = UpSampling2D((2, 2))
decode_conv_layer_4 = Conv2D(1, (3, 3), activation='sigmoid', padding='same')

decoded = decode_conv_layer_1(encoded)   # Decoder는 Encoder의 출력을 입력으로 받습니다.
decoded = decode_upsample_layer_1(decoded)
decoded = decode_conv_layer_2(decoded)
decoded = decode_upsample_layer_2(decoded)
decoded = decode_conv_layer_3(decoded)
decoded = decode_upsample_layer_3(decoded)
decoded = decode_conv_layer_4(decoded)

# AutoEncoder 모델 정의
autoencoder = Model(input_img, decoded)
autoencoder.summary()

결과

Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 28, 28, 1)]       0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 28, 28, 16)        160       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 14, 14, 16)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 14, 14, 8)         1160      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 7, 7, 8)           0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 7, 7, 4)           292       
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 4, 4, 4)           0          #여기까지가 인코더(압축)
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 4, 4, 4)           148        #여기서부터 디코더(복원)
_________________________________________________________________
up_sampling2d (UpSampling2D) (None, 8, 8, 4)           0          
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 8, 8, 8)           296       
_________________________________________________________________
up_sampling2d_1 (UpSampling2 (None, 16, 16, 8)         0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 14, 14, 16)        1168      
_________________________________________________________________
up_sampling2d_2 (UpSampling2 (None, 28, 28, 16)        0         
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 28, 28, 1)         145       
=================================================================
Total params: 3,369
Trainable params: 3,369
Non-trainable params: 0
_________________________________________________________________
  • Conv2D 레이어는 shape를 변화시키지 않음
  • 인코더에서 Output shape를 변화시키는 것은 오롯이 MaxPooling2D 뿐
  • 디코더에서 Output shape를 변화시키는 것은 오롯이 UpSampling2D 뿐

 

 

AutoEncoder를 훈련

autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')

autoencoder.fit(x_train, x_train,
                epochs=50,
                batch_size=256,
                shuffle=True,
                validation_data=(x_test, x_test))

 loss로 주로 분류 모델에서 사용되는 binary_crossentropy를 사용한 이유?

- 모든 dim에서 0~1 사이의 값을 가지는 입력 데이터와 출력 데이터(마지막 출력이 sigmoid로 제어되고 있음에 유의) 사이의 분포가 최대한 유사하기를 바라기 때문

 

결과

Epoch 50/50
235/235 [==============================] - 2s 7ms/step - loss: 0.3296 - val_loss: 0.3299

 

 

 

학습된 AutoEncoder가 MNIST 이미지를 제대로 생성하는지를 확인

x_test_10 = x_test[:10]       # 테스트 데이터셋에서 10개만 골라서
x_test_hat = autoencoder.predict(x_test_10)    # AutoEncoder 모델의 이미지 복원생성
x_test_imgs = x_test_10.reshape(-1, 28, 28)
x_test_hat_imgs = x_test_hat.reshape(-1, 28, 28)

plt.figure(figsize=(12,5))  # 이미지 사이즈 지정
for i in range(10):  
    # 원본이미지 출력
    plt.subplot(2, 10, i+1)
    plt.imshow(x_test_imgs[i])
    # 생성된 이미지 출력
    plt.subplot(2, 10, i+11)
    plt.imshow(x_test_hat_imgs[i])

결과

만족스럽진 않다. 

 

 

 

실험 한계

  • 위에서 Decoder에서 이미지 복원을 위해 사용했던 Convolution 레이어는 실은 Encoder에서 사용했던 것과 동일한 레이어
  • 크기도 변하지 않은 채 채널 개수만 2배로 늘림 ->  Convolution 레이어를 거쳐 정보가 집약되는 것이 아니라 오히려 정보량이 많아지고 있음
  • Deconvoluti이 아닌 정방향의 Convolution 연산을 통해 원본 이미지와 가까운 이미지를 재생해 내는 효과를 구현
  • Convolution 레이어를 활용해서 Transposed Convolution를 흉내 낸 것에 불과

 

실제로는  Image Reconstruction을 위해 Transposed Convolution 애용됨


Upsampling layer

방식(pooling layer 의 소실값 복원)

  • Nearest Neighbor : 복원해야 할 값을 가까운 값으로 복제
  • Bed of Nails : 복원해야 할 값을 0으로 처리
  • Max Unpooling : Max Pooling 때 버린 값을 실은 따로 기억해 두었다가 그 값으로 복원

Transposed Convolution

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

 

Up-sampling with Transposed Convolution 번역

Naoki Shubuya님의 Up-sampling with Transposed Convolution을 허락받고 번역한 글입니다. 번역이 어색한 경우엔 영어 표현을 그대로 사용했으며, 의역이 존재할 수 있습니다. 피드백 언제나 환영합니다!

zzsza.github.io

 

Transposed Convolution을 활용해 작성한 인코더 코드

from tensorflow.keras.layers import Conv2DTranspose

# Conv2DTranspose를 활용한  AutoEncoder 모델
# AutoEncoder 모델 구성 - Input 부분
input_shape = x_train.shape[1:]
input_img = Input(shape=input_shape)

# AutoEncoder 모델 구성 - Encoder 부분
encode_conv_layer_1 = Conv2D(16, (3, 3), activation='relu')
encode_pool_layer_1 = MaxPooling2D((2, 2))
encode_conv_layer_2 = Conv2D(8, (3, 3), activation='relu')
encode_pool_layer_2 = MaxPooling2D((2, 2))
encode_conv_layer_3 = Conv2D(4, (3, 3), activation='relu')

encoded = encode_conv_layer_1(input_img)
encoded = encode_pool_layer_1(encoded)
encoded = encode_conv_layer_2(encoded)
encoded = encode_pool_layer_2(encoded)
encoded = encode_conv_layer_3(encoded)

# AutoEncoder 모델 구성 - Decoder 부분  - 
decode_conv_layer_1 = Conv2DTranspose(4, (3, 3), activation='relu', padding='same')
decode_upsample_layer_1 = UpSampling2D((2, 2))
decode_conv_layer_2 = Conv2DTranspose(8, (3, 3), activation='relu', padding='same')
decode_upsample_layer_2 = UpSampling2D((2, 2))
decode_conv_layer_3 = Conv2DTranspose(16, (3, 3), activation='relu')
decode_upsample_layer_3 = UpSampling2D((2, 2))
decode_conv_layer_4 = Conv2DTranspose(1, (3, 3), activation='sigmoid', padding='same')

decoded = decode_conv_layer_1(encoded)   # Decoder는 Encoder의 출력을 입력으로 받습니다.
decoded = decode_upsample_layer_1(decoded)
decoded = decode_conv_layer_2(decoded)
decoded = decode_upsample_layer_2(decoded)
decoded = decode_conv_layer_3(decoded)
decoded = decode_upsample_layer_3(decoded)
decoded = decode_conv_layer_4(decoded)

# AutoEncoder 모델 정의
autoencoder = Model(input_img, decoded)
autoencoder.summary()


autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy') 
autoencoder.fit(x_train, x_train, epochs=50, batch_size=256, shuffle=True, validation_data=(x_test, x_test))

x_test_10 = x_test[:10] # 테스트 데이터셋에서 10개만 골라서 
x_test_hat = autoencoder.predict(x_test_10) # AutoEncoder 모델의 이미지 복원생성 

x_test_imgs = x_test_10.reshape(-1, 28, 28) 
x_test_hat_imgs = x_test_hat.reshape(-1, 28, 28) 

plt.figure(figsize=(12,5)) # 이미지 사이즈 지정 
for i in range(10): 
    # 원본이미지 출력 
    plt.subplot(2, 10, i+1) 
    plt.imshow(x_test_imgs[i]) 
    # 생성된 이미지 출력 
    plt.subplot(2, 10, i+11) 
    plt.imshow(x_test_hat_imgs[i])

결과

Model: "model_4"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_8 (InputLayer)         [(None, 28, 28, 1)]       0         
_________________________________________________________________
conv2d_26 (Conv2D)           (None, 26, 26, 16)        160       
_________________________________________________________________
max_pooling2d_15 (MaxPooling (None, 13, 13, 16)        0         
_________________________________________________________________
conv2d_27 (Conv2D)           (None, 11, 11, 8)         1160      
_________________________________________________________________
max_pooling2d_16 (MaxPooling (None, 5, 5, 8)           0         
_________________________________________________________________
conv2d_28 (Conv2D)           (None, 3, 3, 4)           292       
_________________________________________________________________
conv2d_transpose_24 (Conv2DT (None, 3, 3, 4)           148       
_________________________________________________________________
up_sampling2d_21 (UpSampling (None, 6, 6, 4)           0         
_________________________________________________________________
conv2d_transpose_25 (Conv2DT (None, 6, 6, 8)           296       
_________________________________________________________________
up_sampling2d_22 (UpSampling (None, 12, 12, 8)         0         
_________________________________________________________________
conv2d_transpose_26 (Conv2DT (None, 14, 14, 16)        1168      
_________________________________________________________________
up_sampling2d_23 (UpSampling (None, 28, 28, 16)        0         
_________________________________________________________________
conv2d_transpose_27 (Conv2DT (None, 28, 28, 1)         145       
=================================================================
Total params: 3,369
Trainable params: 3,369
Non-trainable params: 0
_________________________________________________________________
Epoch 1/50
235/235 [==============================] - 2s 7ms/step - loss: 0.6931 - val_loss: 0.6924
Epoch 2/50
235/235 [==============================] - 1s 6ms/step - loss: 0.6917 - val_loss: 0.6909
Epoch 3/50
235/235 [==============================] - 2s 6ms/step - loss: 0.6900 - val_loss: 0.6891
Epoch 4/50
235/235 [==============================] - 1s 6ms/step - loss: 0.6880 - val_loss: 0.6870
Epoch 5/50
235/235 [==============================] - 2s 6ms/step - loss: 0.6857 - val_loss: 0.6844
Epoch 6/50
235/235 [==============================] - 1s 6ms/step - loss: 0.6827 - val_loss: 0.6811
Epoch 7/50
235/235 [==============================] - 1s 6ms/step - loss: 0.6790 - val_loss: 0.6768
Epoch 8/50
235/235 [==============================] - 1s 6ms/step - loss: 0.6739 - val_loss: 0.6708
Epoch 9/50
235/235 [==============================] - 1s 6ms/step - loss: 0.6666 - val_loss: 0.6622
Epoch 10/50
235/235 [==============================] - 1s 6ms/step - loss: 0.6559 - val_loss: 0.6493
Epoch 11/50
235/235 [==============================] - 1s 6ms/step - loss: 0.6401 - val_loss: 0.6305
Epoch 12/50
235/235 [==============================] - 1s 6ms/step - loss: 0.6180 - val_loss: 0.6064
Epoch 13/50
235/235 [==============================] - 1s 6ms/step - loss: 0.5937 - val_loss: 0.5846
Epoch 14/50
235/235 [==============================] - 1s 6ms/step - loss: 0.5751 - val_loss: 0.5710
Epoch 15/50
235/235 [==============================] - 1s 6ms/step - loss: 0.5643 - val_loss: 0.5631
Epoch 16/50
235/235 [==============================] - 1s 6ms/step - loss: 0.5569 - val_loss: 0.5563
Epoch 17/50
235/235 [==============================] - 1s 6ms/step - loss: 0.5502 - val_loss: 0.5497
Epoch 18/50
235/235 [==============================] - 1s 6ms/step - loss: 0.5436 - val_loss: 0.5430
Epoch 19/50
235/235 [==============================] - 1s 6ms/step - loss: 0.5368 - val_loss: 0.5362
Epoch 20/50
235/235 [==============================] - 1s 6ms/step - loss: 0.5298 - val_loss: 0.5289
Epoch 21/50
235/235 [==============================] - 1s 6ms/step - loss: 0.5224 - val_loss: 0.5212
Epoch 22/50
235/235 [==============================] - 1s 6ms/step - loss: 0.5144 - val_loss: 0.5128
Epoch 23/50
235/235 [==============================] - 1s 6ms/step - loss: 0.5057 - val_loss: 0.5035
Epoch 24/50
235/235 [==============================] - 1s 6ms/step - loss: 0.4960 - val_loss: 0.4932
Epoch 25/50
235/235 [==============================] - 2s 6ms/step - loss: 0.4851 - val_loss: 0.4814
Epoch 26/50
235/235 [==============================] - 1s 6ms/step - loss: 0.4726 - val_loss: 0.4677
Epoch 27/50
235/235 [==============================] - 1s 6ms/step - loss: 0.4581 - val_loss: 0.4519
Epoch 28/50
235/235 [==============================] - 1s 6ms/step - loss: 0.4415 - val_loss: 0.4340
Epoch 29/50
235/235 [==============================] - 1s 6ms/step - loss: 0.4232 - val_loss: 0.4145
Epoch 30/50
235/235 [==============================] - 1s 6ms/step - loss: 0.4038 - val_loss: 0.3947
Epoch 31/50
235/235 [==============================] - 1s 6ms/step - loss: 0.3848 - val_loss: 0.3761
Epoch 32/50
235/235 [==============================] - 1s 6ms/step - loss: 0.3676 - val_loss: 0.3597
Epoch 33/50
235/235 [==============================] - 1s 6ms/step - loss: 0.3528 - val_loss: 0.3461
Epoch 34/50
235/235 [==============================] - 1s 6ms/step - loss: 0.3407 - val_loss: 0.3351
Epoch 35/50
235/235 [==============================] - 1s 6ms/step - loss: 0.3311 - val_loss: 0.3265
Epoch 36/50
235/235 [==============================] - 1s 6ms/step - loss: 0.3236 - val_loss: 0.3199
Epoch 37/50
235/235 [==============================] - 1s 6ms/step - loss: 0.3178 - val_loss: 0.3147
Epoch 38/50
235/235 [==============================] - 2s 7ms/step - loss: 0.3133 - val_loss: 0.3106
Epoch 39/50
235/235 [==============================] - 1s 6ms/step - loss: 0.3096 - val_loss: 0.3073
Epoch 40/50
235/235 [==============================] - 1s 6ms/step - loss: 0.3065 - val_loss: 0.3044
Epoch 41/50
235/235 [==============================] - 1s 6ms/step - loss: 0.3038 - val_loss: 0.3019
Epoch 42/50
235/235 [==============================] - 1s 6ms/step - loss: 0.3014 - val_loss: 0.2996
Epoch 43/50
235/235 [==============================] - 1s 6ms/step - loss: 0.2993 - val_loss: 0.2976
Epoch 44/50
235/235 [==============================] - 1s 6ms/step - loss: 0.2973 - val_loss: 0.2957
Epoch 45/50
235/235 [==============================] - 1s 6ms/step - loss: 0.2955 - val_loss: 0.2941
Epoch 46/50
235/235 [==============================] - 2s 6ms/step - loss: 0.2939 - val_loss: 0.2925
Epoch 47/50
235/235 [==============================] - 1s 6ms/step - loss: 0.2924 - val_loss: 0.2910
Epoch 48/50
235/235 [==============================] - 1s 6ms/step - loss: 0.2909 - val_loss: 0.2896
Epoch 49/50
235/235 [==============================] - 1s 6ms/step - loss: 0.2896 - val_loss: 0.2883
Epoch 50/50
235/235 [==============================] - 1s 6ms/step - loss: 0.2883 - val_loss: 0.2871

기존 인코더 보다는 나은 결과를 산출

댓글