CNN은 Convolution Neural Network의 약자로, 이미지 recognition 분야에서 대표적으로 쓰이는 기법이다.
이번 스터디에 ResNet 논문 읽고 구현하기가 포함됐던 만큼, 이번 장이 핵심 내용인 셈이다.
7.1 CNN의 전체 구조
지금까지 봤던 Neural Network의 경우, 인접한 계층의 모든 뉴런과 연결되어 있었는데 이를
Fully connected layer(완전 연결 계층), Affine 계층 이라 칭한다.
Affine 계층의 경우 다음에 활성화 함수 레이어(ReLU 또는 Sigmoid)가 이어지므로
Input - Affine - ReLU - Affine - ReLU - ... - Affine - Softmax(출력층)
의 형태로 네트워크를 구성할 수 있다.
CNN에서는 Convolutional layer(합성곱 계층), Pooling layer(풀링 계층)이 새롭게 추가된다.
Input - Conv - ReLU - Pooling - Conv - ReLU - Affine - ReLU - Affine - Softmax(출력층)
의 형태로 네트워크를 구성할 수 있는데,
Conv - ReLU - (Pooling) 형태의 layer들이 새롭게 추가됨을 알 수 있다.
*그렇다고 원래의 affine layer를 사용하지 않는 것은 아니다.
출력 계층, 혹은 출력에 가까운 층에서는 기존의 affine layer를 사용하는 것이
일반적인 CNN에서 흔하게 볼 수 있는 구성이다.
7.2 Convolutional Layer
7.2.1 fully-connected layer의 문제점
- 학습해야하는 parameter가 너무 많다.
- 많은 학습 데이터가 필요하다.
- 데이터의 형상이 무시된다.
e.g. 이미지는 일반적으로 가로, 세로, 채널(R, G, B)의 3차원 데이터이다.
3차원 형상 데이터의 경우, 가까운 픽셀이나 각 채널은 밀접하게 연관되어 있을 것이다.
fully-connected layer에서는 이러한 데이터를 flatten하기 때문에
공간적 정보는 무시하고 모든 input data를 같은 차원의 뉴런으로 취급한다.
7.2.2 Convolution 연산
- fully connected layer와 달리 conv layer는 데이터의 형상을 유지한다. (뒤에서 자세히 살펴보자.)
CNN에서는 Conv layer의 입력 데이터를 input feature map, 출력 데이터를 output feature map이라고도 한다.
그렇다면 convolutional layer에서 처리하는 convolution 연산이란 무엇일까?
convolution 연산은 이미지 처리에서 말하는 필터 연산에 해당한다.

convolution 연산을 필터를 특정한 간격으로 이동시키면서, 위치에 대응하는 원소끼리 곱한 후 그 총합을 구한다.
위위 경우 녹색으로 표시한 부분에 필터 연산을 적용한다고 한다면, 결과 좌측 위 칸의 15가 결과가 된다.
(이 계산을 단일 곱셈-누산, Fused Multiply Add라 한다.)
현재 예시에서는 해당 연산을 간격(stride) 1로 수행한 것이다.
7.2.3 Padding
convolution 연산을 수행하기 전에 데이터에 패딩을 두기도 한다.
(4, 4) shape를 가진 데이터에 패딩 1을 추가하면 (6,6) 형태의 데이터가 된다.
* 패딩을 적용하는 이유는 주로 출력 크기 문제 때문이다.
앞서 보았듯이 데이터에 convolution 연산을 적용할 때 출력이 입력의 크기보다 줄어들게 된다.
위와 같은 간단한 예제에서는 괜찮지만 Deep neural network의 경우 데이터 출력이 매우 작아질 수 있어 문제가 크다.
따라서 공간적 크기를 고정한 채로 다음 계층에 전달하기 위해 패딩을 사용한다.
7.2.4 출력의 크기
height_out = (height + 2 * padding - height_of_filter) / stride + 1
width_out = (width + 2 * padding - width_of_filter) / stride + 1
7.2.5 ~ 7.2.6 3차원 데이터의 convolution
지금까지 2차원 예시를 봤지만, 사실 이미지는 R, G, B 세 개의 채널로 이루어진 3차원 데이터이다.

실제로는 각 채널에 필터 연산을 적용하여 연산 결과를 합한다.
이러한 3차원 데이터는 높이가 존재하는 블록으로 생각할 수 있다.


기존에 우리가 본 예제는 output 맵이 1개인 경우였다. 만약 output 맵을 여러 개 만들고 싶다면?
단순하게 필터를 여러개 적용하면 된다.
위의 그림에서는 FN개의 output 맵을 만들기 위해 C개의 채널을 가진 필터 FN개를 사용하였다.
이러한 측면에서 필터의 가중치는 (출력 채널 수, 입력 채널 수, 높이, 너비)의 4차원 데이터이다.

실제로는 convolution 연산에도 bias(편향)을 사용하기 때문에, 위와 같은 모습이 된다.
bias의 경우, 각 채널 당 값 1개를 가지고 있기 때문에 (FN, 1, 1)의 shape를 갖는다.
7.3 Pooling Layer
풀링이란 간단하게 공간을 줄이는 연산이라고 생각하면 편하다.
대표적으로는 특정 영역의 최댓값을 구하는 max pooling, 최솟값을 구하는 min pooling 연산이 존재한다.

- 풀링 계층은 학습해야 할 매개변수, parameter가 없다.
- 입력 데이터의 채널 수를 바꾸지 않는다. 즉, 채널마다 pooling을 수행한다.
- 입력 데이터의 변화에 영향이 적다.
7.4 Convolutional Layer, Pooling Layer 구현하기
convolution 연산의 데이터는 (배치 내 데이터 수, 채널 수, 높이, 너비) 의 4차원 데이터로 볼 수 있다.
im2col을 사용하여 4차원 데이터를 2차원 형태로 만들어 구현한다.

class Convolution:
def __init__(self, W, b, stride=1, pad=0):
self.W = W
self.b = b
self.stride = stride
self.pad = pad
def forward(self, x):
FN, C, FH, FW = self.W.shape
N, C, H, W = x.shape
out_h = int(1 + (H + 2*self.pad - FH) / self.stride)
out_w = int(1 +(W + 2*self.pad - FW) / self.stride)
col = im2col(x, FH, FW, self,stride, self.pad)
col_W = self.W.reshape(FN, -1).T # -1 -> 750개 원소를 10개, 즉 (10, 75)로 변환
out = np.dot(col, col_W) + self.b
out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
return out
7.4.4 Pooling Layer 구현하기
풀링 계층의 경우 채널이 서로 독립적이라는 점이 다르다.
class Pooling:
def __init__(self, pool_h, pool_w, stride=1, pad=0):
self.pool_h = h
self.pool_w = w
self.stride = stride
self.pad = pad
def forward(self, x):
N, C, H, W = x.shape
out_h = int(1 + (H - self.pool_h) / self.stride)
out_w = int(1 + (W - self.pool_w) / self.stride)
col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad_
col = col.reshape(-1, self.pool_h * self.pool_w)
out = np.max(col, axis=1)
out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 2, 1)
return out
7.5 CNN 구현하기

우리가 구현한 클래스를 이용해 위와 같은 CNN network를 구현해보자.
그동안 구현한 클래스를 이용해 아래와 같이 네트워크의 기본 parameters와 layer를 세팅한다.
class ConvNet:
def __init__(self, input_dim = (1, 28, 28),
conv_param={'filter_num':30, 'filter_size':5,
'pad':0, 'stride':1},
hidden_size=100, output_size=10, weight_init_std=0.01):
# ...initialize convolution params
# filter_num, filter_size, filter_pad, filter_stride, input_size(채널),
# conv_output_size, pool_output_size
# initialize params
self.params = {}
self.params['W1'] = weight_init_std * np.random.randn(filter_num, input_dim[0],
filter_size, filter_size)
self.params['b1'] = np.zeros(filter_num)
self.params['W2'] = weight_init_std * np.random.randn(pool_output_size,
hidden_size)
self.params['b2'] = np.zeros(hidden_size)
self.params['W3'] = weight_init_std * np.random.randn(hidden_size, output_size)
self.params['b3'] = np.zeros(output_size)
# construct layers
self.layers = OrderedDict()
self.layers['Conv1'] = Convolution(self.params['W1'],
self.params['b1'],
conv_param['stride'],
conv_param['pad']),)
self.layers['Relu1'] = Relu()
self.layers['Pool1'] = Pooling(pool_h=2, pool_w=2, stride=2)
self.layers['Affine1'] = Affine(self.params['W2'], self.params['b2'])
self.layers['Relu2'] = Relu()
self.layers['Affine2'] = Affine(self.params['W3'], self.params['b3'])
self.last_layer = SoftmaxWithLoss()
그 다음, 학습과 매개변수 업데이트를 하는 코드를 다음과 같이 작성한다.
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
def loss(self, x, t):
y = self.predict(x)
return self.last_layer.forward(y, t)
def gradient(self, x, t):
self.loss(x, t)
dout = self.last_layer.backward(dout)
for layer in list(self.layers.values()).reverse():
dout = layer.backward(dout)
grads = {}
grads['W1'] = self.layers['Conv1'].dW
# ...
return grads
7.6 CNN 시각화 하기
- 학습 전의 필터, 학습 후의 필터를 시각화 했을 때, 학습 후의 필터는 blob(덩어리)와 edge를 보고 있음을 알 수 있다.
- 예를 들어, 왼쪽이 흰색, 오른쪽이 검은색인 필터는 세로 방향 edge에 반응하고, 가로로 나누어진 필터는 가로 방향 edge에 반응한다.
7.6.2 층 깊이에 따른 추출 정보 변화
- 계층이 깊어질 수록 추출되는 정보는 더 추상화된다.

7.7 대표적인 CNN
7.1 LeNet

LeNet은 1998에 제안된 네트워크로,
원본 논문은 Yann LeCun의 'Gradient-Based Learning Applied to Document Recognition'이며, MNIST 데이터셋에 사용되었다. (Written digit classification)
MNIST Dataset
LeCun 교수가 만든 데이터 셋으로, 약 60,000개의 train set과 10,000개의 test set으로 이루어져있다.
손글씨 이미지는 28x28의 이미지이다.
숫자 0~9의 총 10개의 정답 레이블을 가진다.
LeNet-1부터 LeNet5까지 다섯가지의 버전이 존재한다. 여기에서는 LeNet-5의 구조만 살펴볼 예정이다.
그림에서 알 수 있듯이 다음과 같은 구성 요소가 존재한다.
- 3개의 Convolution Layer(C1, C3, C5)
- 2개의 Subsampling Layer(S2, S4)
- 1개의 Fully connected Layer (F6)
Feature Extractor
C1
32x32x1 입력을 5x5 필터(kernel) 6개와 stride 1인 컨볼루션 연산을 한다.
28x28x6 의 feature map을 얻는다.
파라미터 개수: (5x5 + num_of_bias) * num_of_filter = (25 + 1) * 6 = 156
S2
얻은 feature map에 대해 Average Pooling(2x2, stride 2)을 하여 14x14x6의 feature map을 얻는다.
파라미터 개수: (1 + 1) * 6 = 12
* 풀링 이후 weight를 곱하고 bias를 더하는 과정이 있기 때문에 훈련시킬 파라미터가 존재한다.

C3
이전 레이어의 결과 값에 5x5 필터 16개로 stride 1인 컨볼루션 연산을 한다. 10x10x16의 feature map을 얻는다.
* 위의 그림을 보면, 모든 S2의 feature map이 C3의 feature map과 연결된 것이 아니다.
연결의 개수를 제한시키면서 서로 다른 입력을 가지도록 하여 각각이 서로 다른 feature를 추출하도록 하기 위함이다.
파라미터 개수: (5*5*3 + 1)*6 + ... + (5*5*6 + 1)*1 = 1516
S4 : 얻은 feature map에 대해 Average Pooling(2x2, stride 2)을 하여 5x5x16의 feature map을 얻는다.
파라미터 개수: 2 * 16 = 32
C5: 이전 레이어의 결과 값에 5x5 필터 120개로 stride 1인컨볼루션 연산을 한다. 1x1x120의 feature map을 얻는다.
파라미터 개수: (5*5*16 + 1)*120 = 48120
Classifier
F6: 입력 유닛 120개, 출력 유닛 84개의 fully-connected layer이다. Activation function으로 tanh 함수를 사용한다.
파라미터 개수: (120 + 1) * 84 = 10164
F7: 입력 크기 84, 출력 크기 10의 RBF(Euclidean Radia Basis Function unit)를 Activation function으로 사용하는 output layer이다. 여기서 출력 크기가 10인 이유는 MNIST 데이터 셋을 사용했기 때문이다.
=> 총 학습 파라미터 수는 60000개이다.
첫 CNN이라는 점에서 최근의 CNN 아키텍처와 차이가 있다.
- 활성화 함수로 ReLU가 아닌 tanh를 사용한다.
- Max Pooling이 아니라Average Pooling을 사용한다.
파이토치로 LeNet 간단하게 구현해보기
7.2 AlexNet
LeNet과 비교했을 때 최근인 2012년에 발표된 논문으로, 구성은 기본적으로 LeNet과 비슷하다.
원래 논문 제목은 'ImageNet Classification with Deep Convolutional Neural Networks'로, 저자 이름을 따 AlexNet이라 한다.
ILSVRC 대회의 우승을 차지한 모델로, ImageNet 데이터셋에 사용되었다.(Object classification)

ImageNet Dataset
- 22,000개의 label이 존재하고, 약 1,500만개의 고해상도 이미지가 포함되어 있다.
- ILSVRC 대회에서는 ImageNet의 subset, 약 1000개의 클래스에 대해 각 1000개의 이미지를 포함한다.
- 총 백만 개가 넘는 데이터 셋으로, 120만개 정도는 training에, 5만개 정도를 validation에, 15만개를 test에 사용한다.
먼저 구조를 보기 전에, AlexNet에서 성능을 높이기 위해 사용한 다양한 방법들을 살펴보자.
Preprocessing
Resizing
ImageNet의 이미지는 동일한 크기를 가지고 있지 않다.
따라서 이미지의 한 변을 227 크기에 맞춘 후, 가운데를 기준으로 crop한다.
이렇게 하는 이유는 Fully-connected layer의 입력 크기를 고정시켜 여러 hyperparameter를 고정시키기 위함이다.
또한 이미지가 단일 채널인 경우, 해당 채널을 복제하여 3채널로 만들어준다.
Normalization
평균 이미지(mean image)를 각 픽셀 값에서 빼주어서 normalization한다.
예를들어 [32, 32, 3] image가 있으면 mean image도 [32, 32, 3]이다.
Data augmentation


AlexNet의 경우 parameter의 수가 약 6천만개인데, 데이터 셋의 크기는 약 120만개이다.
따라서 Overfitting을 막기 위해 Data augmentation을 적용하여 데이터 셋의 양을 늘렸다.
원본 데이터를 좌우 대칭(Mirror)시키거나, 랜덤으로 크롭하는 방법을 사용한다.
Activation Function
Tanh가 아닌 ReLU 함수를 사용하였다. 이전 챕터에서 본 것 처럼 ReLU는 tanh에 비해 빠르게 수렴한다는 장점이 있다.
Overlapping Max Pooling
LeNet과 다르게 Max Pooling을 사용하고, stride를 kernel의 사이즈보다 작게하여 overlapping pooling을 적용했다.
Drop Out
Overfitting을 방지하기 위해 train시 dropout을 사용했다.
Dropout이란 Fully-connected layer의 뉴런들을 랜덤하게 비활성화 시켜,
특정 뉴런의 영향력이 너무 커지는 것을 방지하는 방법을 의미한다.
Local Response Normalization(LRN)
현재는 주로 LRN말고 Batch normalization을 사용한다.
신경 생물학에서 Lateral Inhibitation이란,
간단하게 강하게 활성화된 뉴런이 주변 뉴런의 약한 자극 전달을 막는 현상을 의미한다.
학습 과정에서도 비슷하게 ReLU를 활성화 함수로 사용한다면 saturation은 없더라도,
특정 한 뉴런의 값이 매우 높다면 그 값이 그대로 내보내져 비슷한 문제가 생길 것이다.
따라서 AlexNet에서는 이를 막기 위해 첫 번째, 두 번째 Convolution Layer에서 LRN을 사용한다.

수식을 보면 여러 다른 kernel을 position x, y에 적용한 값을 가지고 정규화를 하는 것을 알 수 있다.
즉, 다른 map의 같은 위치에 있는 요소끼리 정규화를 해준다고 생각하면 된다.
Architecture

LeNet이 약 6만개의 파라미터를 가진 것에 반해, AlexNet은 약 6천만개의 파라미터, 60만개 이상의 뉴런을 가진다.
따라서 이러한 많은 양의 파라미터를 학습시키기 위해, 2개의 GPU를 병렬로 사용하였다.
AlexNet은 5개의 컨볼루션 레이어와 3개의 Fully-connected layer로 이루어져 있다.
첫 번째, 두 번째, 다섯 번째 convolution layer 다음에 Max pooling을 이용한다.

Input
227x227 사이즈의 RGB 이미지. 세 개의 채널을 가지므로 filter의 depth도 따라서 3이 된다.
(그림의 224 표기는 잘못된 표기라고 한다.)
Convolution Layer 1
- 11 x 11 x 3 필터 96개를 stride 4로 각 GPU 당 48개씩 병렬 처리 한다.
- 55 x 55 x 96 feature map이 생성된다.
- 결과에 Activation function으로 ReLU를 사용한다.
- 이후 3 x 3의 overlapping max pooling을 stride 2로 시행한다.
- 27 x 27 x 96 feature map이 생성된다.
- LRN을 시행한다. 차원은 변화하지 않는다.
Convolution Layer 2
- 5 x 5 x 48 필터 256개를 stride 1, padding 2로 사용하여 이전 단계의 feature map을 처리한다.
- 실제로는 GPU 채널 당 128개씩 병렬 처리를 한다.
- 27 x 27 x 256의 feature map이 생성된다.
- 결과에 Activation function으로 ReLU를 사용한다.
- 이후 Layer 1과 똑같이 pooling과 LRN을 시행한다.
- 13 x 13 x 256의 feature map이 생성된다.
Convolution Layer 3
- 3 x 3 x 256 필터 384개를 stride 1, padding 1로 사용하여 이전 단계의 feature map을 처리한다.
- 이 단계의 레이어는 이전 단계까지 병렬 처리한 GPU 채널에 모두 연결되어 있다.
- 13 x 13 x 384의 feature map을 얻는다.
- 채널 당 13 x 13 x 192의 결과가 나온다.
- 결과에 Activation function으로 ReLU를 사용한다.
Convolution Layer 4
- 3 x 3 x 192 필터 384개를 stride 1, padding 1로 사용하여 이전 단계의 feature map을 처리한다.
- 13 x 13 x 384의 feature map을 얻는다.
- 채널 당 13 x 13 x 192가 나온다.
- 결과에 Activation function으로 ReLU를 사용한다.
Convolution Layer 5
- 3 x 3 x 192 필터 256개를 stride 1, padding 1로 사용하여 이전 단계의 feature mapdmf cjflgksek.
- 13 x 13 x 256의 feature map을 얻는다
- 채널 당 13 x 13 x 128이 나온다.
- 결과에 activation function으로 ReLU를 사용한다.
- 이후 3 x 3의 overlapping max pooling을 stride 2로 시행한다.
- 6 x 6 x 256 feature map이 생성된다.
Fully Connected Layer 6
- 앞 레이어에서 나온 feature map을 flatten 시켜 n = 9216인 벡터로 만든다.
- 4096개의 뉴런을 가지고 있으며, activation function으로 ReLU를 사용한다.
Fully Connected Layer 7
- 4096개의 뉴런을 가지고 있으며, activation function으로 ReLU를 사용한다.
Fully Connected Layer 8
- 1000개의 뉴런을 가지고 있으며, 출력을 위해 Softmax를 사용한다.
AlexNet의 구조는 오늘날 사용하는 많은 아키텍처들에 영향을 주었다.
ReLU와 같은 활성화 함수는 Leaky ReLU와 같은 방법이 나오기도 했고,
특히 multi-GPU를 사용하여 병렬처리를 하는 것이 일반적인 방법론이 되었다.
참고자료
- 사이토 고키저, "밑바닥부터 시작하는 딥러닝", 한빛미디어
- Krizhevsky Alex, Sutskever Ilya, and Hinton Geoffrey E.. 2012. Imagenet classification with deep convolutional neural networks
Understanding AlexNet | LearnOpenCV #
Understand the AlexNet architecture that won the ImageNet Visual Recognition Challenge in 2012 and started the Deep Learning revolution.
learnopencv.com
[CNN 알고리즘들] AlexNet의 구조
LeNet-5 => https://bskyvision.com/418 AlexNet => https://bskyvision.com/421 VGG-F, VGG-M, VGG-S => https://bskyvision.com/420 VGG-16, VGG-19 => https://bskyvision.com/504 GoogLeNet(inception v1) => https://bskyvision.com/539 ResNet => https://bskyvision.co
bskyvision.com
'Deep Learning' 카테고리의 다른 글
| CH 8 - 딥러닝(Deep Learning) (WIP) (2) | 2023.10.15 |
|---|---|
| CH6 - 학습 관련 기술들 (2) | 2022.07.20 |
| CH5 - Back Propagation (0) | 2022.07.13 |
| CH4 - 신경망 학습(Training Neural Network) (0) | 2022.07.06 |
| CH3 - 신경망(Neural Network) (0) | 2022.06.30 |