CNN
1. 개요
- deep MLP는 각 MLP 층 사이의 연결이 완전연결인데, 이 경우 학습이 매우 느리고 과적합이 발생할 수 있다. 각 MLP 층과 층 사이에 연결이 완전연결이 아니라 일부 노드끼리만 연결되는 neural network를 만들 수도 있으며 이러한 neural network를 CNN(convolutional neural network)라 한다.
- CNN의 경우 격자 구조를 갖는 데이터(데이터를 구성하는 성분들이 서로 인접했을 때 더 연관성이 높은 경우 등. 이미지 인식 등)를 다룰 때 적합하다.
- CNN에서는 한 MLP에서 다음 MLP로 넘어갈 때 convolution 연산을 수행한다.
2. CNN에서 쓰이는 연산 및 개념
-
convolution 연산: 주어진 입력 행렬을 커널 행렬과 연산하면, 입력 행렬의 지정된 영역과 커널 행렬의 각 성분을 서로 곱한 후 그 결과값을 다 더한 스칼라값을 얻는다. 이처럼 커널 행렬과의 연산을 통해 스칼라값 하나를 얻는 연산을 convolution 연산이라 한다.
-
커널 행렬: convolution 연산을 수행할 때 사용하는 행렬. 각 Conv층이 갖는 일종의 파라미터로서, 경사하강법을 통해 더 나은 값을 갖도록 값이 바뀌게 된다. 경사하강법을 통해 찾은 각 Conv층의 커널 행렬은 그 Conv층에서 추출된 입력 데이터의 특징이다. (한편, 커널 행렬의 크기를 줄이면 신경망의 깊이가 더 깊어지는데도 전체 매개변수 개수는 줄어든다.)
-
stride: 예를 들어 입력 행렬의 가장 왼쪽 위부터 \(n \times n\) 크기의 영역과 \(n \times n\) 크기의 커널 행렬과 convolution 연산을 했고, 또 그 convolution 연산을 했던 입력 행렬의 위치에서 얼만큼 떨어진 위치에서부터 다시 \(n \times n\) 크기의 영역과 convolution 연산을 할 때, 처음 convolution 연산을 했던 위치와 그 다음으로 convolution 연산을 하는 위치 사이의 간격을 stride라 한다.
-
padding: 입력 행렬의 모든 성분에 대해 convolution 연산을 하면 결과값으로서 입력 행렬의 크기와는 크기가 다른 행렬을 얻게 되는데, 이 결과 행렬의 크기를 필요에 따라 더 크게 얻고자 할 때 입력 행렬의 상하좌우에 0을 채워 입력 행렬의 크기를 키우는 경우가 있다. 이때 0을 채우는 연산을 padding이라 한다.
-
pooling: 입력 행렬을 여러 블록으로 나눈 후, 각 블록에 있는 숫자들을 이용하여 각 블록에 숫자 하나를 할당하여 입력 행렬보다 크기가 작은 새로운 행렬을 구하는 연산을 pooling이라 한다. 각 블록에 있는 숫자들 중 최댓값을 각 블록의 대표값으로 하는 행렬을 구하는 것을 max pooling, 각 블록에 있는 숫자들의 평균을 각 블록의 대표값으로 하는 행렬을 구하는 것을 average pooling이라 한다.
3. CNN의 구조
1) 특징을 추출하는 층
-
Conv층: convolution 연산을 수행한다.
-
ReLU층: ReLU 연산을 수행한다.
-
Pool층: pooling 연산을 수행한다. 연산의 양을 줄인다는 점에서 유용하나 최근에는 사용하지 않는 추세다.
2) 분류/회귀를 수행하는 MLP
-
FC(fully connected)층: 각 MLP끼리 서로 완전히 연결돼 있다. Conv층이 아무리 많아도 Conv층에서 필요한 파라미터 개수 다 합한 것보다 FC층 하나에 필요한 파라미터가 훨씬 크다. 최근에는 사용하지 않는 추세다.
-
결과 예측 층: 분류를 수행하는 CNN이라면, 가장 마지막 층에서는 softmax 연산(=0과 1사이 입력에 대하여, 분자를 지수승하여 1에 가까운 수는 더 가깝게 크기를 키우는 연산)을 수행한다.
4. 여러 CNN 모델들
1) LeNet
- Yann LeCun이 1998년 제시한 알고리즘. \(32 \times 32\) 크기의 이미지를 입력으로 받아, 3개의 Conv층, 2개의 Pool층, 1개의 FC층, 1개의 아웃풋층을 거쳐 결과를 얻는다. 활성함수로는 tanh를 사용했다. 약 6만 개의 파라미터를 훈련시켜야 한다.
-
첫 번째 Conv층: \(32 \times 32\) 크기의 입력에 대하여 \(5 \times 5\) 크기의 필터 6개로 convolution 연산을 수행하여 \(28 \times 28\) 크기의 결과 행렬을 6개 얻는다.
-
첫 번째 Pool층: \(28 \times 28\) 크기의 입력에 대하여 \(2 \times 2\) 크기, stride 2로 average pooling 연산을 수행하여 \(14 \times 14\) 크기의 결과 행렬을 얻는다.
-
두 번째 Conv층: \(14 \times 14\) 크기의 입력 6개에 대하여 \(5 \times 5\) 크기의 필터 여러 개로 convolution 연산을 여러 번 수행하여 최종적으로 \(10 \times 10\) 크기의 결과 행렬을 16개 얻는다.
-
두 번째 Pool층: \(10 \times 10\) 크기의 입력에 대하여 \(2 \times 2\) 크기, stride 2로 average pooling 연산을 수행하여 \(5 \times 5\) 크기의 결과 행렬을 얻는다.
-
세 번째 Conv층: \(5 \times 5\) 크기의 입력 16개에 대하여 \(5 \times 5\) 크기의 필터 여러 개로 convolution 연산을 여러 번 수행하여 최종적으로 \(1 \times 1\) 크기의 결과 행렬을 120개 얻는다.
-
FC층: \(1 \times 1\) 크기의 입력 120개를 84개의 유닛에 연결하여 10개의 결과 행렬을 얻는다.
2) AlexNet
- Alex Khrizevsky가 2012년 제시한 알고리즘. \(227 \times 227 \times 3\) 크기의 이미지를 입력으로 받아, 5개의 Conv층, 3개의 FC층을 거쳐 결과를 얻는다. 활성함수로는 ReLU를 사용했으며, FC층에서 dropout을 수행했다. 약 6천만 개의 파라미터를 훈련시켜야 한다.
-
첫 번째 Conv층: \(227 \times 227 \times 3\) 크기의 입력에 대하여 \(11 \times 11 \times 3\) 크기의 커널 96개, stride 4로 convolution 연산을 수행하여 \(55 \times 55\) 크기의 결과 행렬을 96개 얻는다.
-
첫 번째 Pool층: \(55 \times 55\) 크기의 입력 96개에 대하여 \(3 \times 3\) 크기, stride 2로 overlapping max pooling 연산을 수행하여 \(27 \times 27\) 크기의 결과 행렬 96개를 얻는다.
-
두 번째 Conv층: \(27 \times 27\) 크기의 입력 96개에 대하여 \(5 \times 5 \times 48\) 크기의 커널 256개, stride 1로 convolution 연산을 수행하여 \(27 \times 27\) 크기의 결과 행렬을 256개 얻는다.
-
두 번째 Pool층: \(27 \times 27\) 크기의 입력 256개에 대하여 \(3 \times 3\) 크기, stride 2로 overlapping max pooling 연산을 수행하여 \(13 \times 13\) 크기의 결과 행렬 256개를 얻는다.
-
세, 네, 다섯 번째 Conv층: \(13 \times 13\) 크기의 입력 256개에 대하여 \(3 \times 3\) 크기의 커널 여러 개로 convolution 연산을 수행하여 최종적으로 \(13 \times 13\) 크기의 결과 행렬을 256개 얻는다.
-
세 번째 Pool층: \(13 \times 13\) 크기의 입력 256개에 대하여 \(3 \times 3\) 크기, stride 2로 overlapping max pooling 연산을 수행하여 \(6 \times 6\) 크기의 결과 행렬 256개를 얻는다.
-
첫 번째 FC층: \(6 \times 6\) 크기의 입력 256개를 9216차원의 벡터로 펼쳐 4096개의 퍼셉트론과 연결한다.
-
두 번째 FC층: 4096차원의 입력 벡터를 1000개의 퍼셉트론과 연결한다.
-
세 번째 FC층: 출력값에 softmax 함수를 적용한다.
- 테스트 단계에서 앙상블(테스트 입력에 augmentation을 수행해 이들 데이터로 예측 결과들을 얻고 그 평균을 최종 결과로 출력)을 수행했다. (현재는 AlexNet보다 훨씬 더 깊은 neural network를 사용하여 훈련 단계에서만 data augmentation을 수행하고 테스트 단계에서의 앙상블은 생략한다.)
3) VGGNet
- 옥스포드 대학의 VGG팀이 2014년 제시한 알고리즘. Conv층을 깊게 만들 때 알고리즘 성능이 어떻게 변하는지를 확인하기 위해 convolution 연산에 사용하는 커널 행렬의 크기를 \(3 \times 3\)으로 줄였다. (커널 행렬의 크기가 크면 Conv층을 거치며 입력의 크기가 빠르게 작아지기 때문에 신경망을 깊게 만들 수 없다.) 13개의 Conv층, 3개의 FC층을 거쳐 결과를 얻는다.
-
Conv층: \(227 \times 227 \times 3\) 크기의 입력에 대하여 \(3 \times 3\) 크기의 커널 여러 개로 convolution 연산을 여러 번 수행하여 최종적으로 \(7 \times 7\) 크기의 결과 행렬을 512개 얻는다.
-
첫 번째 FC층: \(7 \times 7\) 크기의 입력 512개를 25088차원의 벡터로 펼쳐 4096개의 퍼셉트론과 연결한다.
-
두 번째 FC층: 4096차원의 입력 벡터를 1000개의 퍼셉트론과 연결한다.
-
세 번째 FC층: 출력값에 softmax 함수를 적용한다.
4) GoogLeNet
- Christian Szegedy 등의 연구에 구글이 참여하여 2014년 제시한 알고리즘. 총 22개의 층을 거쳐 결과를 얻는다. \(1 \times 1\) 크기의 커널을 사용했다는 점, ‘inception’ 이라는 이름의 모듈을 여러 번 사용했다는 점 등의 특징이 있다.
-
\(1 \times 1\) 커널: 예를 들어 \(14 \times 14 \times 480\) 크기의 입력을 \(5 \times 5 \times 480\) 크기의 커널 48개로 convolution 연산을 수행하면 \(14 \times 14\) 크기의 결과 행렬 48개를 얻는다. 이 결과 행렬을 얻는 데 수행한 연산의 총 횟수는 약 1억 회이다. 한편 만약 \(14 \times 14 \times 480\) 크기의 입력을 \(1 \times 1 \times 480\) 크기의 커널 16개로 convolution 연산을 수행하면 \(14 \times 14\) 크기의 결과 행렬 16개를 얻고, 이에 대하여 \(5 \times 5 \times 16\) 크기의 커널 48개로 convolution 연산을 수행하면 \(14 \times 14\) 크기의 결과 행렬 48개를 얻는다. 이 결과 행렬을 얻는 데 수행한 연산의 총 횟수는 약 5백만 회이다. 이처럼 \(1 \times 1\) 크기의 커널과 convolution 연산을 수행하는 과정을 넣으면 연산 횟수를 비약적으로 줄일 수 있다.
-
inception 모듈: GoogLeNet에서는 전 단계에서 들어온 입력에 대하여 커널의 크기가 \(1 \times 1\), \(3 \times 3\), \(5 \times 5\)로 각각 다른 커널들로 convolution 연산을 수행하고 그 결과들을 모아 다음 단게의 입력으로 넘기는 층을 ‘inception’이라는 이름을 붙여 총 9개 사용하는 신경망을 구성하였다. 커널의 크기를 다양하게 사용하는 만큼 입력 이미지로부터 더 다양한 특성을 추출할 수 있다.
-
global average pooling: GoogLeNet에서는 FC층을 사용하지 않고, 주어진 입력 여러 개를 평균을 내 결과를 얻었다. 예를 들어 \(7 \times 7\) 크기의 입력이 1024개가 들어온다면 FC층으로 이를 처리하려면 이를 50176차원의 벡터로 펼친 후 1024개의 노드와 완전연결을 해야 하고 이 경우 이 FC층이 필요로 하는 파라미터의 개수가 약 5천만 개가 된다. 그러나 global average pooling을 하면 파라미터가 전혀 필요하지 않다.
5) ResNet
- 마이크로소프트의 Kaiming He 등이 2015년 제시한 알고리즘. 총 152개의 층을 거쳐 결과를 얻는다. 기존 LeNet부터 VGGNet까지 했던 것과 같이 신경망을 마냥 깊게 한다고 무조건 성능이 향상되는 게 아님을 알고(20개 층 알고리즘이 56개 층 알고리즘보다 성능이 나았다) residual block이라는 구조를 사용했다는 점이 특징이다.
- residual block: 주어진 입력(\(x\))이 두 개의 Conv층을 거친 결과값(\(F(x)\))에 그 두 Conv층에 주어졌던 입력(\(x\))을 더하여 그 더한 결과(\(F(x)+x\))를 다음 Conv층의 입력으로 넘기는 신경망 블록을 residual block이라 한다. 기존 방식이 ‘\(x\)라는 입력이 \(y\)라는 결과를 갖게 하는 함수 \(H(x)\) 구하기’가 목표라면, ResNet은 ‘\(H(x)\)(=\(F(x) + x\))를 최소로 하는 \(F(x)\) 구하기’가 목표다. \(H(x)\)가 최소가 될 때는 \(F(x)\)가 최소가 될 때와 같다. (\(F(x) = H(x) - x\) 이므로 \(F(x)\)를 residual이라 한다.) 기존 방식으로는 역전파 때 gradient가 얕은 신경망으로 올라가면서 사라지는 문제가 나타나는데, 신경망 블록의 결과값에 신경망 블록에의 입력값을 더해주는 식으로 결과값을 구해 다음 신경망으로 넘기는 식으로 처리하면(\(F(x)+x\)) gradient가 얕은 신경망으로 올라가더라도 사라질 리 없다. residual block은 이와 같은 방식으로 gradient 소멸 문제를 해결하기 위해 고안된 개념이다.
6) DenseNet
- Gao Huang 등의 연구에 페이스북이 참여하여 2017년 제시한 알고리즘. 신경망의 깊이가 깊어지는 경우 gradient vanishing 문제가 생겨 ResNet의 경우 residual block이라는 구조를 사용해 이 문제를 해결했는데, DenseNet의 경우 dense block이라는 구조를 사용해 이 문제를 해결했다는 차이가 있다.
- dense block: ResNet의 residual block이 앞 layer에서 만들어진 결과값에 그 layer의 인풋으로 들어간 데이터를 더해 다음 layer의 입력으로 했다면, dense block은 앞 layer에서 만들어진 결과값에 그 layer의 인풋으로 들어간 데이터를 결합하여(concatenate) 다음 layer의 인풋으로 한다. 다만 이렇게 하면 feature map의 채널이 무한정 늘어날 수 있으므로 중간마다 \(1 \times 1\) convolution 연산, pooling 연산을 수행한다. (이러한 연산을 수행하는 층을 transition layer라 한다.)