본문 바로가기
info : 유용한 정보, 체험기

[딥러닝] 합성곱 신경망 Convolutional Nueral Network CNN 필수 개념 정리와 파이토치와 텐서플로우 코딩 예시 -1

by 퇴근길에 삼남매가 알려드림 2024. 8. 26.

딥러닝에서 자주 보고 듣게 되는 용어 중 하나가 합성공 신경망 즉, 컨볼루션 신경망 (Convolution Neural Network , CNN)입니다. CNN은 이미지 인식과 처리뿐만 아니라 다른 분야에서도 많이 이용되고 있어요. 오늘은 CNN을 실제로 활용하는데 필요한 실전 개념과 용어를 정리해 보도록 하겠습니다. 또한 실제 코드 예시를 통해 CNN을 실제로 어떻게 구현해야 하는지 이해하는데 도움이 되면 좋겠어요.  

 

신경망 개념 정리

합성곱이름대로, 곱해서 더하는 과정이 CNN의 주요 특징입니다. 본 글은 신경망 (Neural Net)에 대한 기본 개념을 아셔야 이해가 편하길 거예요. 아래 내용이 이해가 잘 가신다면 다음 내용을 이해하는데 문제없으실 거예요. (feat. 이런 기본 개념은 인터뷰 질문으로 나올 수도 있어요)

  • 신경망에서 weight(가중치)과 bias(편향)는 중요한 구성 요소로 신경망 학습을 통해서 weight과 bias 값을 정하게 됩니다.
  • 가중치는 해당 요소 (neuron)가 얼마나 중요한지 나타내는 값이고 입력값에 곱해져요. 가중치가 크면 신호가 강하다고 할 수 있어요.
  • 편향값은 상수값으로 더해져요. 그래서 가중치와 더불어서 활성화 함수(activation function)에 들어가는 값에 영향을 끼치죠.
  • 주어진 데이터를 바탕으로  손실함수(Loss function)를 통해 예측 오류를 계산하고, 이 오류를 줄이기 위해서 다양한 최적화 기법을 이용해요. 경사하강법(graident descent)이 가장 대표적이고, 이건 기울기를 통해서 어느 방향으로 값을 변화시켜야 할지 알려주죠. 
  • 학습률 (learning rate)은 가중치와 편향을 얼마나 빠르게 업데이트할지 정해주는 값이에요. 최적화 기법으로 알게 경사 (gradient) 값에 학습률 값을 곱한 값이 기존의 가중치와 편향값에 더해지게 져서 가중치와 편향값이 업데이트가 되는 거예요.
  • 데이터는 여러 가지 숫자로 표현될 수 있는데 이를 데이터를 encoding 한다고 합니다. 이때 encoding dimension은 데이터를 숫자로 나타날 때 생성되는 벡터의 차원을 뜻합니다.
  • 이미지는 RGB (빨간색, 초록색, 파란색)으로 인코딩할 수 있고 이때 색상 채널 사이즈를 표현하는 채널 개수는 3입니다. 이를 feature_size 3 이라고 표현할 수도 있어요.  

 

CNN 핵심 구성 요소 및 원리

합성곱 신경망 주요 구성 요소와 변수는 필터 (filter), 커널 (kernel), 스트라이드(stride), 팽창 (dilation), 패딩(padding)이에요. 각각 신경망이 어떻게 입력값에서 특징을 추출하는지, 결괏값의 크기는 어떤지 결정하는데 영향을 끼쳐요.

 

필터/커널(Filter/Kernel)

우선 필터(filter)가 가장 중요한 요소예요. 실은 필터와 커널은 같은 개념을 의미해요. 필터의 사이즈는 커널(kernel)이라는 변수로 표현되기도 해요. 예를 들어서 3x3 필터라고 하면 kernel size = (3,3)인 필터죠. 파이토치(pytorch), 케라스(keras), 텐서플로우(tensor flow)는 다 kernel size 값이라고 표현돼요.

필터의 각 위치마다 가중치 값이 있고, 각 필터마다 하나의 편향값이 있어요.

이 필터는 입력데이터의 같은 크기 영역에 적용이 돼요. 필터와 입력데이터 영역에서 같은 열과 행 값에 대해서 곱하기를 해요 (element-wise product). 그리고 결과값을 모두 더하고, 필터의 편향값도 더한 게 합성곱 결과물이에요. 즉, 필터가 같은 크기에 적용되었을 때 값이 딱 하나 나오는 거죠. 입력데이터가 필터보다 클 때, 필터가 입력데이터 위를 이동하면서 합성곱 연산을 반복해요. 이때  중요한 변수가 스트라이드(stride) 값이에요.

 

스트라이드(Stride)

스트라이드(Stride) 값은 필터가 입력데이터에서 이동하는 간격이에요. 즉, 다음 연산을 위해서 한 칸만 움직이면 stride=1이고, 두 칸을 움직이면 stride=2이죠. 코딩할 때 따로 정해주지 않으면 1이에요. 만약 Stride 값이 크면, 데이터를 건너뛰니까 그만큼 합성곱을 하는 횟수가 줄겠죠? 그러면 결괏값 크기 (output dimension size)도 줄어들어요. 즉, stride는 출력되는 결과의 크기를 결정하는 중요한 요소예요.

 

팽창(Dilation)

필터사이에 빈 공간을 두어서 적은 연산으로 더 넓은 영역의 관계를 학습할 수 있어요. 만약 dilation=1이면 중간에 빈 공간이 없는 필터예요. 그런데 dilation=2이면, 중간에 필터 사이사이에 한 개의 빈 공간이 생긴 거예요. 즉, 입력데이터 위에 적용되는 필터의 크기가 그만큼 커지는 거죠. 예를 들어서 한 면이 3인 필터에 dilation=2이면, (3-1)*2+1의 크기만큼 입력데이터 위 영역을 계산하는 거예요. 만약 dilation=3이면 각 필터마다 2개씩 의 빈 공간이 있고 이때 (3-1)*3+1의 크기에 해당하는 면적이 입력데이터 위에 겹쳐지겠죠. 물론 합성곱자체는 filter 위치에만 해당되고요. 즉 (커널 사이즈 -1) * 팽창값 +1 이 팽창된 필터 사이즈예요.

 

아래 사진이 이해에 도움이 되면 좋겠어요. 위층의 그림이 합성곱결과물이고요, 아래층 그림이 입력값에 (3,3) 필터가 적용된 거예요. 파란색타일이 필터가 적용돼서 해당되는 요소들이 곱해지는 모습을 뜻해요. dilation 팽창값에 따라서 파란색 필터 사이에 빈 공간들이 있는 게 보이시죠? 그렇기 때문에 stride와 마찬가지로 dilation 역시 결과값의 크기에 영향을 미쳐요.

 

cnn필터와 팽창

 

그런데 이렇게 필터 크기만큼 입력 값 위에서 합성곱을 진행하면, 입력 데이터 가장자리 값은 계산이 덜 되죠. 정보 손실이 일어나는데요, 기를 방지하는 방법이 바로 패딩 (padding)이에요. 

 

패딩(Padding)

데이터 가장자리에 추가로 값을 넣어서 (보통 0), 입력데이터의 가장자리에 있는 값도 추가로 합성곱에 이용되도록 해요. padding도 다양한 방법이 있어요. 이때 패딩은 네 가지 목표 및 결과로 나뉠 수 있어요. 'full' padding은 입력데이터의 모든 요소들이 같은 횟수만큼 합성곱에 이용되는 것이에요. 아래 사진에서 왼쪽 경우와 같아요. "same" padding은 합성곱 결과물 크기가 입력데이터와 같은 크기가 되도록 하는 거예요. valid padding은 패딩을 추가하지 않고 계산하는 거예요. 결과물은 입력데이터보다 작아지죠. 그리고 이 세 가지 모두에 적용되지 않는 패딩도 있어요. 정보손실을 다른 정도로 조절하는 거죠. 이유는 필터 크기와 stride, dialtion 값에 따라서 결과값에 영향을 미치는 padding 사이즈가 결정되기 때문이에요.

 

참고로 padding에서 0을 넣는 게 보통이지만 다른 방법들도 있어요. 주변 가장자리 와 같은 값을 넣는다던가 하는 거요. 이건 코딩에서 padding_mode로 설명되어요. 참, padding을 할 때는 입력한 데이터의 모든 테두리에 적용돼요. 즉, 아래 사진에서 왼쪽그림은 padding =2인 거고 오른쪽 그림은 padding=1인 거예요.

 

 

패딩원리
패딩 원리

 

패딩값 1을 이차원 (2D) 데이터에 적용하면, 각 변의 양쪽에 각각 한 줄씩 패딩이 추가돼요. 그래서 원래 데이터 크기 + 2*padding 이 각 행과 열에 패딩을 적용한 결과예요. (4,5)의 데이터면 (6,7)이 되죠.

 

아래 파이토치의 Conv2d의 경우 어떤 요소가 입력변수인지, 결과값의 크기는 어떤지 정리되어있어요. 지금까지 개념을 정리한 kernel_size, stride, padding, padding_mode, dilation가 보이시죠? 이때 padding='same' 을 넣으실 수도 있고 padding=3 이렇게 숫자를 넣으실 수도 있어요. 그런데 padding='same'은 stride=1일 경우, 즉 한 칸씩만 미끄러질 경우에만 적용돼요. 

 

pytorch conv2d
Pytorch conv2d 정보

 

CNN 결과값 크기 계산

CNN에서 가장 중요한 것 중 하나는 합성곱 연산 결과물 크기를 계산하는 거예요. 그래야 다음 레이어에서 어떤 사이즈의 필터를 쓸 수 있는지 알 수 있죠. 앞서 말한 원리들을 생각하면 쉽게 공식을 생각할 수 있어요. 위 pytorch 설명에도 공식이 있어요. 입력값의 각 차원(dimension)에 동일하게 적용돼요. 공통 계산 공식은 아래와 같아요. 계산에 대한 설명도 아래 추가했어요.

 

출력크기 = (입력값 크기 + 2*패딩크기-팽창값(dilation)*(필터크기-1)-1)/스트라이드 + 1의 내림한 값

 

  • 패딩을 적용해서 필터가 움직이는 면적이 "입력값 크기 + 2*패딩크기" 에요 - 식(1)
  • 입력값 위를 움직이는 필터의 크기는 "팽창값(dilation)*(필터크기-1)+1" 에요 - 식(2)
  • 이때 가중치의 개수는 팽창값과 상관이 없어서 kernel_size에 의해서 결정된 필터크기 값과 같아요. 
  • 패딩을 적용한 후 입력값 크기 (식 1)에서 필터크기 (식 2)를 빼면, 필터가 몇 번 움직일 수 있는지 알 수 있어요.
    • 입력값 크기 + 2*패딩크기- (팽창값(dilation)*(필터크기-1)+1) = 입력값 크기 + 2*패딩크기- "팽창값(dilation)*(필터크기-1)-1". 에요
  • 파란색으로 된 부분을 보시면 왜 -1이 되는지 확인할 수 있어요.
  • 물론 총 움직이는 횟수는 미끄러지는 정도(stride)로 나누어야 하고, 움직이는 횟수는 정수여야 하니 내림으로 계산해요.
  • 움직이는 것 외에 맨 처음의 계산도 합해서 (+1) 최종 결과물로 위의 식이 나온 거죠.

 

출력크기 = (입력값 크기 + 2*패딩크기-팽창값(dilation)*(필터크기-1)-1)/스트라이드 + 1의 내림한 값

 

 

1D 일 경우 해당 계산이 한번 너비(Width)에 대해 이루어지고, 2D의 경우 입력값의 Height(높이), Width(너비) 또는 행과 열)에 적용돼요. 3D는 깊이(depth), Height(높이), Width(너비)에 적용되고요. 이 결과값은 feature map 이라고도 해요.

 

채널  뜻

데이터는 다양한 정보로 표현될 수 있어요. 이미지처럼 R, G, B 세 개의 색상 채널로 표현될 수 있고, One-hot encoding처럼 특정 정보가 있느냐 없느냐로도 표현될 수 있어요. 이때 데이터를 표현하는 방법의 수를 feature_size라고도 하고 input_channel_size입력 채널 개수라고도 표현해요. 코딩에서는 보통 in_channels라는 변수로 나타내구요.

 

이미지는 가로와 세로 또는 높이와 너비가 있는 이차원 정보값이에요. 그래서 Cov2d 를 이용하구요. 그런데 이렇게 여러 채널이 있으면 실은 삼차원인 거잖아요. 이럴 때 합성곱 연산은 어떻게 할까요?

 

필터가 입력데이터와 같은 사이즈의 채널을 가져요. 그리고 똑같이 같은 영역에 해당하는 입력값과 weigth 가중값을 곱해서 다 더하게 돼요. 결과적으로 합성곱을 하면 인풋채널 개수에 상관없이 각 필터 연산 한 번에 결과는 1개예요. 그래서 feature map크기는 인풋채널 사이즈에 영향을 받지 않아요. 같은 이유로 패딩을 할 때에도 입력데이터의 높이와 너비 등에는 영향을 미치는데 인풋채널 방향으로는 영향을 미치지 않아요!

반대로 필터를 여러 개 만들 수 있어요. 이때 필터 개수를 output_channel이라고 해요. 각 필터는 물론 kernel_size와 입력값의 input_channel값과 같은 크기구요. 각 필터마다 가중치와 변수가 다르기 때문에 입력 데이터의 다른 정보를 배운다고 할 수 있어요.

 

이제 어떻게 합성곱을 계산하는지 알려드릴게요. Conv1d, Conv3d도 같은 방식이라서 Conv2d를 예시로 가지고 왔어요. 

 

Conv 2d - Pytorch 파이토치 예시

파이토치 (Pytorch)를 예시로 합성곱을 하면 아래와 같습니다. 

여기서 입력 데이터는 (배치사이즈, height, width, input_channel) 의 형태입니다. 

Conv2d에는 인풋채널의 크기, 아웃풋채널의 크기, 커널사이즈 이렇게 세 가지가 필수 입력값이에요. 커널 사이즈는 2d의 경우에 (3,5) 이렇게 두 값으로 표현할 수 있어요. 만약 3x3의 필터면 그냥 3 이라고만 적어도 돼요. 

import torch
import torch.nn as nn

# 총 20개의 입력데이터가 있고, 각 데이터는 높이 16, 너비 50으로 100개의 (input_channel) 특징으로 구성되어 있어요 
input = torch.randn(20, 16, 50, 100)


# 입력데이터가 높이 16, 너비 50의 이차원 데이터니까 2차원 합성곱신경망 (Conv2d)을 이용해요.
#Conv2d는 필터가 높이 3, 너비 5이고, 높이 방향으로는 2씩, 너비 방향으로는 1씩 이동합니다. 패딩도 각각 4,2입니다. dilation도 3,1이구요.
m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1))

# 합성곱 결과는 아래와 같이 계산할 수 있어요.
output = m(input)

# 만약 (3,3) 필터라면 아래와 같이 표현해도 된다
m = nn.Conv2d(16, 33, 3, stride=(2, 1), padding=(4, 2), dilation=(3, 1))

 

 

Conv2d - tensorflow.keras  예시

텐서플로우는 입력데이터가 (배치사이즈, height, width, input_channel) 일 수도 있고 (배치사이즈, input_channel, height, width) 일 수도 있습니다. data_format="channels_last"

 (배치사이즈, height, width, input_channel) 로 파이토치와 같은 형태이고  data_format="channels_first" 는 (배치사이즈, input_channel, height, width) 입니다. 

 

예전에는  Keras/텐서플로우에서 channels_first 가 기본값이어서 파이토치 형식으로 데이터를 바꿔야 할 때 주의를 해서 코드를 변환했어야 했어요. 요즘에는 channel_last 가 기본값인 것 같지만, 항상 데이터 형식을 확인하시길 바랄게요! 

 

Conv2d에는 아웃풋채널의 크기, 커널사이즈 이렇게 두 가지가 필수 입력값이에요. 

 

import tensorflow as tf
from tensorflow.keras.layers import Conv2D
import numpy as np

#

input = np.random.rand(20, 16, 50, 100)
# 파이토치와는 다르게 activation function을 바로 지정해줄 수 있죠
keras.layers.Conv2D(33,(3,5), activation='relu')(input)

# 만약 (3,3) 필터면 역시 그냥 3만 써도 되요
keras.layers.Conv2D(33,3, activation='relu')(input)

 

파이토치와 차이점

위의 파이토치와는 다르게 인풋채널은 입력값에 넣지 않아요. 또한 파이토치와 다르게 activation 활성함수를 바로 지정할 수 있어요. 기본값은 ReLU 함수예요. 이와 관련된 자세한 내용은 다음에 다룰게요.

본 내용은 다양한 글과 강의를 참고하였는데 Couresa 강의와 비숍 교수님 Deep Learning 책이 주 참고자료에요.

copyright. 2024. 퇴근길 afterwork-fyi. All rights reserved.
copyright. 2024. 퇴근길 afterwork-fyi. All pictures cannot be copied without permission.
모든 포스트 내 사진과 글은 무단도용, 영리목적의 사용, 저작물의 변경 등을 허용하지 않습니다.

728x90
반응형

댓글


TOP