- cs231n 5강의 내용을 정리한 글입니다.

- 최대한 쉽게, cs231n 강의를 스스로 다시 이해하며, 처음 딥러닝을 공부하는 사람들도 쉽게 이해할 수 있게 정리해보았습니다.

- 저도 초보인지라 틀리는 부분이 있을 수 있고, 이해가 안 되는 부분이 있을 수 있습니다. 만약 틀린 부분이 있거나 잘 이해가 되지 않는 부분이 있다면, 바로 댓글로 질문해 주세요! 내용과 관련된 질문은 최대한 아는 선에서 대답해 드리겠습니다!

- 보는 중에 사진들에 나오는 수식 같은 경우는, 따로 설명하지 않았다면 건너뛰어도 됩니다.

- 이해가 잘 되지 않은 설명이거나, 설명이 명확하지 않은 것 같으면, 댓글로 피드백 부탁드립니다.

 

 

 

 

이번 시간에는 Convolutional Neural Network(컨볼루셔널 신경망, 줄여서 CNN)에 대해서 배워보겠습니다!

 

 

 

저번 시간에 인공 신경망에 대해 *간단히* 이야기했었던 거, 기억나시죠?

위 사진을 보고 기억이 나지 않는 것이 있다면, 저번 강좌로 돌아가서 다시 한번 봐주세요!

 

 

위의 사진은 CNN의 기본 구조입니다!

물론 지금 당장 이게 뭔지는 따로 해보신 분들이 아니라면 잘 모르시겠지만, 그냥 대강 구조가 이렇구나! 하고 넘기시면 됩니다.

다만, 이번 강좌가 끝난 후 다시 보았을 때에는 정확한 이해를 해야 합니다..!

 

자, 그래서 CNN이 뭔지, 이제부터 알아보도록 하겠습니다.

 

 

 

우선, 저번 강좌에서 배웠던 Fully Connected Layer을 다시 불러와 봅시다.

32*32*3의 이미지가 있으면, 그것을 3072*1의 크기로 변환시킨 것을 input값으로 했었죠?

그리고, Wx값들은 (클래스 개수인) 10 * 3072로 만들어 둔 뒤에,

input과 Weight를 곱하여 10개의 output 값들을 만들어 내는 layer가 바로 Fully connected layer였습니다.

 

 

 

그렇다면, Convolution Layer은 Fully connected layer와 어떤 점이 다를까요?

일단, Convolution layer에서의 input값은 fully-connected layer와는 다르게 원형을 보존한 상태로 둡니다.

32*32*3 크기의 사진이라면, 그 사진의 크기 그대로 보존해 놓습니다.

 

 

그리고, 우리의 weight값은 위의 자그마한 filter가 될 것입니다. 이번 예에서는, 5*5*3의 크기네요.

가로와 세로의 크기는 input의 크기보다 크지만 않으면 되지만, 이 filter의 깊이는 무조건 input값과 같아야 합니다. (여기서는 3)

 

그리고, 이렇게 마련된 5*5*3의 크기의 filter를, 왼쪽 위부터 모든 부분마다 점곱을 해줍니다.

그러니깐.. input 사진의 왼쪽 위 5*5*3 부분에 필터를 곱한 후에, 더하는 것이죠!

그 후에, 한 칸 왼쪽으로 옮겨서 해주고.. 맨 위쪽 끝까지 갔다면 한 칸 아래로 내려서 다시 해주고.. 하는 것이죠.

 

 

마치, filter를 image에다가 대고, 쭉 밀어준다는 느낌인 것이죠.

강아지들 털 밀듯이.. 왼쪽 위부터 오른쪽으로 쭉- filter를 image에 갔다 대고 나서,

조금 더 아랫부분도 쭉- 밀어주고.. 하는 과정이죠.

아무튼 그런 뒤에, 그런 뒤에, bias까지 더해줍니다.

bias가 뭔지는.. 기억 나시죠? 편향 값이요!

 

위의 계산에 대해 조금 더 자세히는 아래에서 나옵니다!

 

 

그렇게 하나하나 다 곱해주다 보면, 다음과 같이 28*28*1 크기의 output이 나옵니다.

간단히 계산을 해본다면.. 왜 이런 결과가 나오는지도 계산이 가능합니다.

그리고, 이 output값은 activation map이라고 하는데, 이는 이 사진이 가지는 특징을 나타내어 주는 역할을 하게 됩니다.

 

 

그런데, CNN을 사용할 때에는, filter를 하나만 사용하지는 않습니다!

다양한 필터를 사용하여, 필터마다 다른 특징을 나타내게 만들 것이기 때문이죠.

 

저번 linear classification 시간에, 막 '자동차' 클래스의 weight는 빨간 자동차만을 나타내는 그림이 있었고, 말은 머리가 두 개 달려있었고.. 그랬었죠? 하지만, CNN은 그러한 단점을 없애기 위하여 다양한 필터를 사용합니다. 그만큼 다양한 특징들을 추출해 내기 위함이죠.

 

그리고, 필터를 두 개 사용했으니 activation map은 두 개가 나오겠죠?

각각의 필터마다 점과 계산을 해 줄 테니 말이죠.

 

 

그러면, filter 6개를 사용한다면? 당연히 6개의 activation map이 나오겠죠!

그리고, 이것을 쌓아본다면.. 우리는 28*28*6 크기의 output, 즉 activation map을 얻은 것입니다!

 

 

그런데, CNN은 위의 Convolutional Layer과 activation function의 조합이 연달아 나오는 형식의 Network입니다!

잠깐! activation function이 뭔지, 저번 시간에 언급한 적이 있는데, 기억나시나요?

Non-Linear 하고, 대표 격으로는 ReLU가 있었던, 바로 그 function입니다.

더-욱 자세한 내용은 다음 강좌 때 나올 것이니, 뭔지 모르겠어도 그냥 그러한 function을 지난다라고만 알아 두시면 될 것 같습니다.

 

아무튼, Convolutional Layer을 한번 지나고, ReLU까지 지난다면, 28*28*6의 크기의 activation map이 생길 것입니다.

 

 

근데 아까 뭐라고 했었죠?

이 조합을 반복하면서 진행할 것이라고 했었죠?

 

그렇다면, 28*28*6의 이미지에 필터를 씌우려면, 크기는 어때야 할까요?

네, 가로, 세로는 별로 상관이 없겠지만, 적어도 깊이가 6이어야겠죠? input값의 깊이가 6이니깐요!

 

그렇다면, 28*28*6의 이미지에 5*5*6의 필터 10개가 들어간다면 다음 activation map의 크기는?

위의 사진에서처럼, 24*24*10의 크기가 되겠지요!

필터 1번에 activation map 1겹이 쌓인다고 생각한다면, 이해가 쉽겠죠?

 

 

그런데, 우리가 지금까지 오면서 무엇을 한 걸까요?

바로, 각각의 '뉴런'들이 찾고자 하는 특징들을 뽑아낸 것입니다.

 

filter는, 각각의 사진에서 어떠한 부분들이 있는지에 대한 정보를 얻어냅니다.

위의 사진에서 보이듯, 처음에는 약간 low-level, 즉 간단한 특징들을 얻어낸다면,

가면 갈수록 더욱더 복잡하고, 정교한 특징들을 얻어내는 것이죠.

 

 

그리고, 각 필터마다 위와 같은 activation map이 생길 것이고, 이는 사진의 어떠한 부분의 특징을 나타내 주는 역할을 합니다.

가령, 위에서 파랗게 네모 쳐져있는 필터와 activation map을 보시면, filter는 사진의 주황색 부분을 가리키고 있고,

그에 따라 activation map은 차의 백라이트 부분의 구조(특징)를 나타내고 있는 것을 확인할 수 있습니다.

 

 

자, 조금 더 들어가기에 앞서, 큰 그림을 한번 봐봅시다.

위의 사진은 CNN의 기본적인 구조입니다.

지금까지는 CONV와 RELU까지 했다면, 이제부터는 저 POOL이나 FC 같은 것들에 대해서 알아가 보도록 합시다.

 

 

 

그럼 조금 더 들어가 봅시다!

아까 전에, filter를 밀어주는 것에 대해서 더욱 자세하게 가본다고 했었죠?

 

 

일단, 7*7의 input이 들어오고, 우리는 3*3의 filter로 이를 밀어준다? 고 생각해봅시다.

그러면 위의 사진과 같이 왼쪽 위부터 시작해서..

 

 

 

 

 

이렇게 쭉- 밀어나가며 점곱을 하게 될 것입니다.

한 칸, 한 칸씩 오른쪽으로 옮겨주면서 계산하는 것이죠.

그러면, output의 사이즈는 5*5의 사이즈가 될 것입니다. 한번 계산에 숫자 하나씩이라 하고 한번 밀어보시면 되겠습니다.

 

 

그런데, 이번엔 한 칸씩 밀지 않고, 두 칸씩 밀어보면 어떨까요?

 

 

바로 이렇게 말이죠! (아래로도 두 칸씩 내려갑니다!)

그러면, output의 크기는.. 3*3의 크기가 되겠죠? 맨 위쪽 세 개, 중간쪽 세개, 아래쪽 세 개.. 의 숫자가 나올 테니깐요.

 

이와 같이, 미는 정도를 stride라고 합니다.

가령, stride가 1이라면, 1칸씩 민다는 것이고..

stride가 2라면, 2칸씩 민다는 것이죠!

 

 

그런데, 7*7 사이즈의 이미지에 3*3 사이즈의 필터를, 3칸씩 밀면서 옮길 수 있을까요..?

그렇지 못하겠죠! 세 칸씩 움직여버리면, 7*7의 이미지에 3*3의 필터가 낄 수가 없으니깐요.

 

위의 초록색 3*3 블록을 오른쪽으로 세 칸 움직이면, 오른쪽에 딱 한 줄만 남게 되는데, 그러면 오른쪽 한 줄이 손실되는 결과를 가져오기 때문이죠.

 

 

이것은 산술적으로도 생각할 수 있습니다.

우리의 Output size는 (N-F) / stride + 1로 계산할 수 있습니다. (N=이미지 크기, F=필터 크기)

N=7이고 F=3일 때..

stride가 1이라면, output의 사이즈는 (7-3)/1 + 1 = 5,

stride가 2라면, output의 사이즈는 (7-3)/2 + 1 = 3이 되지만..

stride가 3이라면, (7-3)/3+1 = 2.33이 되므로, output의 사이즈가 성립하지 않습니다!

크기는 당연히 자연수여야 하니깐요.

 

 

 

그리고, Padding에 대해서도 알아봅시다.

Padding이란, 이미지의 가장자리 부분에 어떠한 숫자들을 채워 넣는 것을 의미합니다.

마치, 우리 이미지에 노스페이스 패딩을 입혀준다! 고 생각하면.. 편하지 않을까요? 아무튼 우리의 이미지가 더 두꺼워지는 거니깐요 ㅎㅎ...

그리고, 실제 우리가 작업을 할 때에는, 가장자리에 0을 넣는, zero pad를 하는 것이 일반적이라고 합니다.

 

그렇다면, 7*7 이미지에, stride가 1이고, padding을 1픽셀만 가장자리에 덮어씌워준다면, output의 크기는 어떻게 될까요?

아까 전에, output size는 (N-F)/stride + 1이라고 하였으므로,

이번엔 N=9 (엄밀히는 7+2), F=3, stride=1이니..

(9-3)/1 + 1 = 7이 됩니다.

즉, output은 7*7의 크기가 되는 것이죠.

 

그리고, 일반적인 경우엔 convolutional layer은 stride는 1로 하고,

filter 크기가 F*F라면 padding은 (F-1)/2의 크기만큼 한다고 합니다.

 

아니 잠시만요!! 근데 이거 왜 하는 거임;;

저거 한다고 뭐가 좋아지나요??

 

 

자, 아까 전의 슬라이드로 한번 돌아가 봅시다.

32*32*3 크기의 이미지에다가 계속해서 filter를 적용시켰을 때, 크기가 (깊이 빼고) 32*32에서 28*28, 24*24...로 빠르게 줄어드는 것을 확인할 수 있습니다. 그러면, CONV를 한 5번만 해도, 크기가 12*12 정도로 줄어들 터인데, 이는 사진의 부분적인 요소들을 보존하자는 취지를 생각하면.. 별로 좋지 못하다는 것을 쉽게 알 수 있습니다.

 

 

 

그런데, 만약 10개의 5*5 사이즈의 filter에 (5-1)/2, 즉 2의 크기인 pad를 해준다면, output의 사이즈는?

 

 

아까 전의 공식인, (N-F)/stride + 1을 가져온다면,

N은 패딩 때문에 32+2*2인 36이 되었으므로,

(36-5)/1+1 = 32가 되고,

filter의 개수는 10개였으니깐, 총크기는 32*32*10의 크기가 될 것입니다!

 

오잉?? 크기가 보존되었죠?

이것이 바로 padding을 사용하는 이유입니다.

계속되는 convolution 과정에서, 사진의 크기를 그대로 유지시켜 주는 역할을 padding이 해 주는 것이죠.

 

 

 

그렇다면 여기서 질문!

32*32*3 사이즈의 input에다가, 10개의 5*5 필터를, stride 1, pad 2로 해놓는다면

위 layer에서의 parameters (더 간단하게는, filter에 있는 수의 개수)는 몇 개나 될까요?

 

 

일단 filter는 5*5*3이어야 하는데, 여기에 bias값까지 더해주어야 하므로, 각각의 필터에는 5*5*3+1개, 즉 76개의 parameter들이 있습니다.

그리고, filter는 10개라고 하였으므로, 총크기는 76 * 10 = 760개가 되겠죠?

 

 

자, 여기까지 왔습니다!

위의 슬라이드를 보며 지금까지의 내용을 정리해봅시다!

만약 W1*H1*D1의 크기인 input을 받았다면, (W=가로, H=세로, D=깊이)

필터의 개수인 K, 그들의 크기인 F, stride의 크기인 S, 그리고 padding의 수인 P가 각각 필요할 것입니다.(모두 hyperparameter입니다.)

그리고 이러한 필터들이 들어간다면

W2 = (W1 - F + 2*P) / S + 1,

H2 = (H2 - F + 2*P) / S + 1,

D2=K인 output값 W2*H2*D2가 만들어질 것입니다.

(기본적인 output size공식이 (N-F)/stride + 1 이기 때문이죠.)

 

그리고, Paramters의 개수는 (F*F*D1)*K + K가 될 것입니다.

 

그리고, 일반적인 세팅을 조금 말씀드리자면..

K는 2의 제곱, 일반적으로는 32,64,128,512 등이 사용되고,

F=3, S=1, P=1

F=5, S=1, P=2

F=5, S=2, P=?(아무거나 맞는 걸루다가)

F=1, S=1, P=0

등이 자주 사용됩니다. 

 

 

그리고, 위의 예시들 중

F=1, S=1, P=0 이 있었는데, filter의 크기가 1*1이어도 되나? 생각하실 수도 있겠지만..

그래도 됩니다!  padding이 없어도 크기가 그대로 유지되며, 다음 activation map도 아무런 탈 없이 나옵니다!

이러한 F=1을 사용하는 Convnet들도 존재하고요.. 나중에 더 자세히 파보시면 되겠습니다!

 

 

그리고, 위의 것들은 각각 Torch, Caffe라는 프레임워크를 사용하여 구현한 CNN입니다.

뭐.. 지금 딱히 중요한 건 아니고, 그냥 이런 프레임워크들을 사용해서 CONVnet을 짤 수 있다고요.(그리고 나중 가면 다 쓰게 될 거고..)

 

그리고 이 아래부턴 또 두뇌랑 어떤 연관이 있는지에 대한 부분인데..

제가 몰라서.. ㅠㅠ 죄송합니다... 

그런데 그렇게 중요한 부분은 아니라.. 일단 거르도록 하겠습니다..

 

 

요기서 다시 시작!

저번에 Fully-connected Layer 했던 거.. 기억나시죠?

위에서도 한번 슬-쩍 말했었고요.

이거 일단 기억한 상태로, 다음으로 넘어가 봅시다.

 

 

자, 다시 이 사진으로 돌아와서 봅시다.

이제 우리가 해야 할 것들이 눈에 보이죠?

방금까지 CONV와 RELU를 끝냈으니.. 이번에는 POOL과 FC를 할 차례입니다!

 

 

Pooling 은 activation map의 크기를 downsampling 하는 과정을 이야기합니다.

즉, 이미지의 크기를 줄이는 것이죠.

 

'아니.. 아까 전에 이거 안 줄이려고 padding 한다며요? 근데 이번엔 또 왜 줄인대?'

왜 그러냐면.. 아까 전의 padding을 한 것은 크기 보존뿐만 아니라, 이미지의 부분적 특징을 살리기 위해서 한 것이었습니다.

계속해서 convolution을 한다면, 가장자리 부분의 특징이 잘 살아나지 못하니깐요.

 

하지만, 이번에 하는 pooling은, 이미지의 특정 부분을 잘라내는 것이 아니라, 사진 전체 부분을 유지한 상태로 픽셀만 줄이는 것입니다.

게임의 해상도를 줄이는 것을 생각하면 이해하기 편합니다. 1920*1080 해상도로 게임하다가, 딱 그 반 크기로 게임을 한다면, 전체적인 화면은 똑같겠지만, 뭔가 조금 화질이 안 좋다는 느낌이 팍 들겠지요?

뒤로 가면 갈수록 filter의 개수가 늘어나는 것이 일반적인데, 이런 식으로 늘여가다 보면 activation map의 깊이가 너무 깊어지게 되고, 위와 같이 224*224*64 같은 크기의 이미지가 되어 버립니다.

그런데.. 이렇게 된다면 계산하는 데에 너무 오랜 시간이 걸려서, 뭘 할 수도 없는 지경에 이르게 되겠지요?

하지만, pooling을 해서 activation map의 크기를 반으로 팍 줄여준다면.. 계산도 두배로 빨라지게 될 것입니다!

이것도 게임을 생각하면 편합니다. 해상도를 줄이면, 그래픽카드와 CPU가 해야 하는 연산이 조금 더 줄어드므로.. 렉 걸리던 게임도 잘 돌아가게 되겠죠?

 

 

 

 

Pooling의 가장 대표적인 방법으로는 MAX POOLING이란 것이 있습니다.

우선, 이 pooling layer의 filter를 준비합니다. (이 filter에는 parameter들이 있는 것은 아닙니다.)

위의 예에서는 4*4 크기의 사진에 2*2 크기의 filter로, stride는 2로 pooling을 합니다.

 

그러고 나서, filter를 convolution 하듯이 이미지에다가 MAX 연산을 해 줍니다.

위의 예에서는 빨간 부분이 처음 필터가 가는 곳인데, 저 위에 필터를 씌운다면, 1,1,5,6중 가장 큰 6만을 남겨놓고 나머지 숫자들은 모두 지워버립니다.

그 뒤, stride가 2이므로 오른쪽으로 두 칸 옮겨서 초록색 부분으로 갑니다. 그 뒤에, 다시 MAX 연산을 해서 2,4,7,8중 가장 큰 수인 8만을 남겨놓고 나머지는 다 지우고.. 뒤의 노란색, 파란색 부분도 마찬가지로 진행됩니다.

 

여기서 주의해야 할 점은, 일반적으로 stride는 filter끼리 서로 겹치는 것은 지양해야 합니다.

가령, 위의 stride가 1이라면, 빨간 부분과 초록 부분 사이에 걸치는 부분이 생기게 되는데, 이러면 조금 곤란해진다는 것이죠.

아예 안된다는 것은 아니지만.. 자주 그렇게 설정하지는 않습니다.

 

 

이와 관련된 공식 또한 존재합니다.

W1*H1*D1의 이미지가 있다고 했을 때,

pooling layer의 filter 크기 F, stride인 S가 있다면,

W2=(W1-F)/S + 1

H2=(H1-F)/S + 1

D2=D1

인 W2*H2*D2의 크기의 이미지가 됩니다.

 

참고로, Pooling layer에는 zero-padding을 하는 것은 일반적이지 않습니다. 

일반적으로는, 저 위의 예처럼 F=2, S=2의 filter를 사용합니다.

 

 

드디어 마지막, FC Layer, 즉 Fully Connected Layer만 남았습니다!

저번 인공 신경망 시간에 Fully Connected Layer 했었던 기억.. 나시죠? 위에서도 했고요!

 

convolution 하고, ReLU도 해주고, Pooling도 하고.. 이 과정들을 반복하면서 나온 가장 마지막 activation map들의 pooling까지 끝났을 때, 우리는 그 이미지들을 FC Layer에 집어넣습니다.

일반적인 인공 신경망과 마찬가지로, 각각의 클래스마다 점수를 도출해 내기 위함이죠. 이렇게 한다면, 인공 신경망처럼 '자동차' 점수, '트럭' 점수 등등이 나오며, Loss 값도 구할 수 있을 것입니다.

 

 

https://cs.stanford.edu/people/karpathy/convnetjs/demo/cifar10.html

위 링크를 따라 들어가시면, 직접 각 사진들의 activation map들도 보고, weight들도 보고, training 과정도 관여할 수 있습니다.

그냥 심심하면 들어가서 한번 봐보시고, 아니면 그냥 걸러도 됩니다.

 

 

이렇게, CNN에 대한 내용들을 모두 배웠습니다.

 

CNN은 CONV, POOL, FC layer들을 쌓아 올린 형태의 network입니다.

요즘 트렌드는 filter를 자그마하게 만들고, 더욱 깊은 아키텍처를 만드는 것이고, POOL과 FC layer를 빼서 그냥 CONV만 하게 만들기도 한다고 합니다.

일반적인 아키텍처(전체적인 구조)는 일반적으로,

CONV-RELU 여러 번 한 다음 POOL 하고.. 하는 것을 몇 번 반복해 주고, 마지막에 FC와 ReLU 몇 번 한 다음 SOFTMAX로 loss를 찾습니다.

최근의(2017년 기준으로) ResNet/GoogLeNet 같은 경우에는 이런 패러다임을 따르지 않았다고 하네요.

 

자, 이제 진짜 끝입니다!

CONV 하는 과정 및 이유, 장점과 Pooling, Padding, FC layer에 대한 내용까지 모두 이해하셨으면 좋겠군요.

아까 말했던, 저 위의 이미지를 다시 보면서, 하나하나 모두 기억이 나는지 확인해 보시는 것도 좋을 것 같습니다.

 

다음 강좌 내용은 인공신경망의 activation function(ReLU 그거 맞아요!), initialization, dropout, batch normalization 등에 대해 배우겠습니다.

그럼, 다음 강좌 때 봅시다!

 

 

+ Recent posts