뮤트 개발일지

AIFFEL 아이펠 27일차 본문

AIFFEL

AIFFEL 아이펠 27일차

박뮤트 2022. 2. 10. 14:10

딥러닝 레이어의 이해

데이터의 차원 변화를 좇으며, 각기 다른 신경망들이 갖는 weight의 특성을 살펴보자.

오늘은 Linear레이어, Convolution레이어를 다룰 것 (다음 시간에는 Embedding, Recurrent레이어를 다룰 예정)

 

Linear 레이어

선형대수학의 선형변환(Linear Transform)과 동일한 기능을하는 레이어

https://www.youtube.com/watch?v=kYB8IZa5AuE 

https://www.youtube.com/watch?v=vVvjYzFBUVk 

기능: 선형 변환을 활용해 데이터를 특정 차원으로 변환한다.

 

Convolution 레이어

사진 필터에서 Counvolution 연산을 활용한다. 선명하게 하는 필터, 흐리게 하는 필터 등이 Convolution을 위한 행렬로 정의되어 있다.

아래 링크에서 Convolution 연산 효과를 시각적으로 확인 가능

https://en.wikipedia.org/wiki/Kernel_(image_processing) 

 

Kernel (image processing) - Wikipedia

In image processing, a kernel, convolution matrix, or mask is a small matrix used for blurring, sharpening, embossing, edge detection, and more. This is accomplished by doing a convolution between the kernel and an image. Details[edit] The general expressi

en.wikipedia.org

필터는 다른 말로 커널이라고 부르기도 한다. 이미지를 필터로 훑을 때, 몇 칸씩 이동하며 훑을지 결정하는 값을 Stride라고 한다.

이 때, Convolution연산은 입력의 형태를 변형시키는데, 예를 들어 [0, 1, 2, 3, 4] 데이터를 [-1, 0, 1]로 1칸씩(stride=1) 훑을 때 3번 밖에 연산을 진행하지 못한다. 입력은 (5, ) 형태였는데, 출력은 (3, )인 [3, 2, 2]가 되는 것이다. 이를 방지하기 위해 Padding을 사용한다. 입력의 테두리에 0을 추가해 [0, 0, 1, 2, 3, 4, 0]으로 만들어줘 입력의 형태를 유지할 수 있게 하는 테크닉이다.

 

Convolution 레이어는 이미지 입력을 처리할 때 Linear 레이어 대비 적은 파라미터로 효과적으로 중요한 이미지 피처를 뽑아낸다. Linear 레이어는 입력 피처 전체가 매 출력에 미치는 영향의 가중치를 모든 입력 미처 사이에 전부 고려한다. 이미지처럼 지역성locality이 중요한 정보가 되는 경우, Linear레이어는 중요한 정보가 소실된 채 큰 파라미터 속에서 입력과 출력 사이 관계의 가중치를 찾아내야 하는 어려운 문제를 풀어야 한다. 그러나 Convolution 레이어는 필터 구조 안에 지역성 정보가 온전히 보존된다. 인접한 픽셀들 사이에서의 패턴만 추출할 수 있다는 것 자체만으로도 불필요한 파라미터 및 연산량을 제거하고 정확하며 효율적으로 정보를 집약시킬 수 있게 되는 것이다.

 

그러나 필터가 너무 작거나 찾아야하는 물체의 경계선에 필터가 걸리는 경우 정확하게 물체를 찾아내지 못할 수 있다는 문제점이 있다. 그렇다고 필터를 크게 하는 경우 Convolution 레이어는 Linear 레이어와 같아지게 된다. 즉 필터 사이즈를 키우면 파라미터 사이즈와 연산량이 커질 뿐 아니라 정확도도 떨어지게 될 가능성이 높다.

 

Pooling 레이어

그렇다면 우리는 필터 사이즈가 아닌 수용영역Receptive Field를 크게 할 수 있다. Neural Network의 출력부가 충분한 정보를 얻기 위해 커버하는 입력 데이터의 수용영역이 충분히 커서 그 안에 찾아야할 object의 특성이 충분히 포함되어 있어야 정확한 탐지가 가능하다. 어쩌면 수용영역의 크기는 거의 이미지 크기 전체가 되는 경우도 있을 수 있다. 

* 문제로 위의 이미지와 같이  2*2 max pooling 레리어를 통해 빨간색 포인트가 선택되었다면 output에서의 수용영역의 크기는 얼마일까라는 문제가 나왔는데, 답은 5*5였다.. 그러나 그 이유를 모르겠다...

 

max pooling 레이어의 의미

효과적으로 수용영역을 키우고, 정보 집약 효과를 극대화할 수 있다. 이러는 동안 늘어난 파라미터 사이즈는? 0이다. 

2*2 max pooling 레이어란 2*2 영역 안에서 가장 큰 값을 뽑고 나머지는 무시하는 역할을 한다. Convolution 레이어에서 힘들게 연산한 결과의 3/4를 버리는데, 이런 정보 손실이 가져오는 정확도 저하 효과는 없는걸까?

Max pooling이 좋은 이유에 대한 명확한 설명은 없으나, 다음과 같은 효과에 대한 설명이 있다.

 

- translation invariance 효과: 이미지는 약간의 상하좌우 이동이 생긴다고 해도 내용상 동일한데, max pooling을 통해 인접한 영역 중 가장 특징이 두드러진 영역 하나를 뽑는 것은 오히려 약간의 이동에도 불구하고 동일한 특징을 안정적으로 잡아낼 수 있어 오히려 object 위치에 대한 오버피팅을 방지하고 안정적인 특징 추출 효과를 가져온다.

 

- Non-linear 함수와 동일한 피처 추출 효과: Relu와 같은 Non-linear 함수도 마찬가지로 많은 하위 레이어의 연산 결과를 무시하는 효과를 발생시키지만, 중요한 피처만을 상위 레이어로 추출해서 올려줌으로써 결과적으로 분류기의 성능을 증진시키는 효과를 가진다. min/max pooling도 이와 동일한 효과를 가진다.

 

- Receptive Field 극대화 효과: max pooling 없이도 수용영역을 크게 하려면 Convolution 레이어를 아주 많이 쌓아야한다. 그러면 큰 파라미터 사이즈로 인한 오버피팅, 연산량 증가, 경사 소실 등의 문제를 감수해야 한다. 이런 문제를 효과적으로 해결하는 방법으로는 max pooling 레이어를 사용하거나, Dilated Convolution이 있다.

https://m.blog.naver.com/sogangori/220952339643

 

receptive field(수용영역, 수용장)과 dilated convolution(팽창된 컨볼루션)

receptive field 는 출력 레이어의 뉴런 하나에 영향을 미치는 입력 뉴런들의 공간 크기이다. 그림 1) 일반...

blog.naver.com

 

Deconvolution 레이어

Convolution 레이어를 통해 원본 데이터의 정보를 너무 많이 손실한 것은 아닐까? 그래서 Convolution 결과를 역재생해 원본 이미지와 최대한 유사한 정보를 복원해내는 Auto Encoder에 대해 알아보고자 한다. 

MNIST 데이터셋을 입력으로 받아 그대로 복원하는 Auto Encoder를 다음의 순서로 만들어보자.

- 패키지 임포트 및 MNIST 데이터셋 로딩

- AutoEncoder 모델 구성

- AutoEncoder 모델 훈련

- AutoEncoder Reconstruction Test

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는 사용하지 않는다.
# AutoEncoder가 수행하는 Image Reconstruction Task는 x_train의 라벨이 바로 x_train 자신이 되기 때문

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.
# 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
_________________________________________________________________
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])

 

읽을거리...

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

'AIFFEL' 카테고리의 다른 글

AIFFEL 아이펠 29일차  (0) 2022.02.10
AIFFEL 아이펠 28일차  (0) 2022.02.10
AIFFEL 아이펠 26일차  (0) 2022.02.04
AIFFEL 아이펠 25일차  (0) 2022.02.03
AIFFEL 아이펠 24일차  (0) 2022.02.03