인공지능/누구나 이해할 수 있는 딥러닝(cs231n)

- 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 등에 대해 배우겠습니다.

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

 

 

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

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

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

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

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

 

 

 

이번 강좌에서는, Backpropagation(역전파법)과 Neural Network(인공 신경망)에 대해서 알아보겠습니다!

 

 

우선, 저번 시간 복습부터 시작하겠습니다.

위의 scores function으로 각 클래스마다의 점수를 구한 이후에,

그 점수를 사용해서 SVM loss (또는 softmax loss)를 구하고,

거기다가 regularization을 더해서, 총 loss를 구한 다음에,

그 loss를 가장 적게 만들어 봅시다.

 

 

그런 다음, 산을 천천히 내려간다는 그 비유처럼, 조금씩 조금씩 loss가 내려가는 방향으로 분류기를 수정합니다.

이를 optimization이라고 하죠.

 

 

그리고, numerical 과 analytic, 기억 나시죠?

기울기를 조금씩 줄여주면서 optimization을 할 것이다!

 

 

그리고 지금까지 말한걸 computational graph로 표현한 것이 위의 슬라이드입니다.

그런데, 이건 그나마 f=Wx같은 간단한 거였으니깐 이런 식으로 예쁘게 그래프가 나오지, 나중에 나올 악랄한 식들도 존재하고, 그러면 그래프도 엄청나게 복잡해지고.. 이걸 하나하나 다 계산하는 건 미친 짓이라는 거죠 ㅎ

 

 

이게 대표적인 예인데, 이건 AlexNet이라는 Convolutional network를 computational graph로 나타낸 것입니다.

그게 뭐냐구요?

곧 할 거니깐, 그냥 그렇다고 알아두세요~!

 

 

이따구로 생긴,  징그럽다는 생각까지 들 정도로 이상하게 생긴 그래프도 있는데,

 

 

그걸 무수히 많이 사용하는 경우도 있으니..

저 블록 거의 하나하나마다 미분을 다 하고 있으면, 아무리 analytic이라도 엄청나게 오래 걸리겠죠?

 

 

 

그래서 나온 게 backpropagation(역전파법)인데... 음.. 어..

이것도 신나는 수학 시간이네요!

본격적으로 들어가기에 앞서, 드릴 말씀이 있습니다!

지금부터는 그냥 죄다 미분 및 계산에 관련된 내용입니다. 그렇기 때문에, 저런 계산에 약하신 분들은, backpropagation 부분 말미에 마지막 정리 부분만 봐주셔도 괜찮습니다!

게다가, 저조차도 계산을 잘 못하기 때문에, 중간중간 틀리는 부분이 있을 수도 있습니다.

설명 중간에 이상한 부분을 포착했다면 댓글로 저 좀 혼내주시면 감사하겠습니다 ㅎㅎ

또, 슬라이드가 이 부분은 굉장히 많습니다. 그만큼 슬라이드가 자세히 설명을 해 주고 있으니, 설명이 빈약한 부분은 슬라이드를 통해 더 자세히 이해할 수 있으면 좋겠습니다.

 

 

 

아무튼! 시작해 보도록 하겠습니다!

 

 

우리의 모델이 f(x, y, z) = (x+y) * z라고 해봅시다.

여기서의 예에서는 x=-2, y=5, z=-4로 두겠습니다.

그리고, q=x+y, f=q*z라고 할 때, 우리가 구하고 싶은 것은 밑의 식 세 개입니다.

f를 각각 x, y, z로 편미분 한 값을 원하는 것이죠.

왜냐고요??

입력값이 x, y, z일 때, 이 x, y, z값이 마지막 output 값에 끼치는 영향을 알고 싶은데, 그것이 바로 저 편미분 값이기 때문이죠.

 

 

자, 한번 거꾸로 가 봅시다!

일단 f를 f로 편미분 한 값은?

 

 

당연히 1!(팩토리얼 아님 ㅎ)

 

 

 

그러면, f를 z로 미분하면?

 

 

f=qz이므로, f를 z로 편미분 하니깐 당연히 q, 즉 3이 나옵니다!

 

 

같은 방식으로, f를 q로 미분한 값은, z인 -4가 되겠지요?

 

 

그런데, f를 y로 미분한 값은 어떻게 구할까요?

위에서 나와있는 Chain rule에 따라, f를 q로 편미분 한 값인 -4 와 q를 y로 편미분한 값을 곱해야 합니다.

그러면, q를 y로 편미분한 값은?

q=x+y이므로, q를 y로 편미분 하면 당연히 정답은 1이기 때문에

-4 * 1 = -4, 즉 f를 y로 편미분 한 값은 -4가 됩니다.

 

Chain rule이 뭐냐고요?

.. 우리 착한 수학자 형아들이 만들어 준거예요! 그냥 그렇구나! 하고 넘어갑시다!

 

 

 

마찬가지로, f를 x로 편미분 한 값도,

Chain rule을 이용하여 -4 * 1 = -4라고 구할 수 있습니다.

 

 

 

 

 

 

 

자, 이것을 보기 쉽게 그림으로 확인해 볼까요?

우선, x값과 y값을 입력으로 받고, f라는 함수에다가 집어넣어서 z라는 값을 얻어냅니다.

이때, 우리는 z를 x로 미분한 값과, z를 y로 미분한 값들을 얻어낼 수 있습니다.

그러고, z를 끝으로 L, 즉 loss를 계산했다고 해봅시다.

자, 이때는 역전파법을 어떻게 사용할까요?

 

우선, 마지막에서 시작합니다.

L을 z로 미분하는 것이죠.

그리고 나서, L를 각각 x, y로 미분한 값을 구할 때면, 아까 구해놨던 z를 x,y로 미분한 값과 chain rule을 사용하여 구할 수 있습니다!

그리고, 이 값들을 구해내는 것이죠.

 

이렇게, 입력값을 받아서 loss값을 구하기까지 계산해 가는 과정을 forward pass라고 하고,

forward pass가 끝난 이후 역으로 미분해가며 기울기 값들을 구해가는 과정은 backward pass라고 부릅니다.

 

 

다른 예제로는, 다음과 같은 그래프가 있는데...

 

얍!!!

자, 계산이 끝났습니다! 정말 쉽죠?

만약 직접 계산해보고 싶다면, 아래의 식 4개만으로도 충분히 모두 계산할 수 있으니, 한번 제대로 계산이 되었나 검산 한번 해보세요!

 

 

 

아니, 갑자기 왜 이렇게 스킵하냐고요? 왜 슬라이드 13개나 갑자기 넘기냐고요??

이걸 하나하나 다 계산해 보는 것에 큰 의미도 없을뿐더러, 그냥 계산 연습 수준의 예제라서 계산만 가능하다면 크게 이해하는 것에 어려움을 느끼진 않을 것이라 생각했기 때문입니다. 계산 못하면, 그냥 넘기셔도 됩니다!

 

 

 

그런데, 조금 특이한 점이 있습니다.

어떤 것을 미분을 하려고 할 때, 위의 sigmoid gate라고 적혀있는 저 부분만 떼서 보았을 때, 저 부분을 한 번에 미분이 가능하다는 것입니다! 즉,
0.73 부분에서 1/x부분 미분해서 -0.53, +1 미분해서 -0.53... 할 필요 없이,

그냥 한 번에 (0.73)*(1-0.73) = 0.2 라고 계산이 가능하다는 것이죠.

 

띠용?? 그럼 지금까지 했던 모든 미분을 죄다 한번에 할 수도 있는 거 아님?? 하실 수도 있는데..

사실 맞음! 엄청나게 긴 computational graph조차도 한방에 미분이 되게 할 수도 있습니다!

그런데.. 위에서 봤다시피 엄청나게 복잡하고, 수없이 많은 노드들이 연결되어 있는 그래프 같은 경우에는, 이게 사실상 불가능하겠죠?

그러니깐, 위의 sigmoid function과 같이 자주 사용되는 임의의 부분만 떼와서, 그 부분만 쉽게 계산할 수 있도록 하면 됩니다.

 

그런데 sigmoid function이 뭐냐고요??

그냥 중간에 껴들어가는 함수 언저리다~! 하고 생각하시면 됩니다. 나중에 다시 나오니깐, 뭔지 모르겠다고 끙끙대지는 마세요!

 

 

 

 

 

 

그리고, backpropagation을 하면서 자주 보일 패턴들입니다.

add gate는 덧셈을,

mul gate는 곱셈을,

max gate는 최댓값을 구하는 gate입니다.

그런데, 이것들의 backward pass를 할 때는, 조금 더 간단하게 계산이 가능합니다.

add gate가 나온다면, 이미 가지고 있던 gradient를 각각의 노드에 분배해 주면 됩니다.

mul gate가 나온다면, 현재의 gradient를 각각 숫자에 (위의 예는 x, y)에 곱해서, 바꿔치기해주면 됩니다.

max gate가 나온다면, 그냥 더 큰 쪽에만 gradient를 그대로 꽂아주고, 반대쪽은 씹고 넘어가면 됩니다.

이렇게 몇몇 gate에서는 간단하게 계산이 가능하다는 것을 알 수 있습니다.

자, 그럼 이제 위의 예제를 이 방식을 사용해서 한번 해보고 싶지 않으신가요?

 

저는 별로 해보기 싫으니깐 그냥 넘어가도록 하겠습니다 ㅎㅎ

 

 

그리고, 위와 같이 노드 하나에서 다른 노드 두 개로 모두 이어졌을 때, backpropagation을 수행하면 저 뒤의 두 개의 노드에서 오는 미분 값을 더해야겠죠?

반대로, 앞의 노드 하나만 바뀌어도 뒤의 노드 두 개가 모두 바뀌고, 또 그 뒤가 모두 바뀌고... 한다는 사실도 알아두시면 좋겠습니다.

 

 

그런데, 우리의 x, y, z들은 사실 어떤 한 변수가 아니었죠?

되게 여러 개가 나열되어 있는, 벡터였습니다! 그냥 수의 나열이라고 생각하시면 편해요!

그러니깐, 이런 짓거리를 벡터로도 한번 해봐야 겠.... 나요..?

 

 

 

 

.. 아니요! 안 할 겁니다!

위의 예처럼 그냥 계산만 복잡해졌다 이거지 딱히 다른 점도 없을뿐더러, 머리 터져요!!

 

그냥 슬라이드만 살짝궁 올려놓을 테니, 진짜로 해보고 싶으신 분들은 직접 계산 대입해서 하셔도.. 되긴.. 하는.. 데.. 비추드립니다 ㅎㅎ

 

 

 

아무튼, 여기까지 (간단하게나마) 했으니, 직접 한번 구현해보도록 합시다!

물론 저 위에 계산을 하나하나 다 하고 있자는 건 아니고, 그냥 어떤 식으로 이루어지는지만 확인할 겁니다.

 

우선, forward부분에서는 input이 주어지고, 그에 따른 loss값을 구할 수 있겠죠?

그러고 나서, backward 부분에서는 거꾸로 하나하나 미분해 가면서 미분 값들을 구하겠고요.

 

간단하게 하면, 이게 다예요!

 

 

쪼--끔만 더 들어가 보겠습니다.

아까 전에 gate들 중에서 mul gate를 예로 들어서 봅시다. (x, y, z는 다 벡터가 아니라 숫자라고 가정해요!)

 

일단, forward pass를 봅시다.

forward 부분에서의 인자 값은 간단하게 x와 y가 될 겁니다.

그리고, mul gate였으므로, x와 y를 곱해서 z값을 도출해 내고, 그 z값을 리턴하겠죠?

 

그렇다면, backward pass는 어떨까요?

우선, backward 부분의 인자는 어떤 것이 와야 할까요?

위 설명을 잘 읽으셨다면 아시겠지만, loss 값 L을 z로 미분한 값을 인자로 가져야겠죠?

그리고, 리턴 값은 당연히 L을 x, y로 미분한 값들이어야 할 겁니다.

우리가 backward를 하면서 구하고 싶은 값은 L를 x, y로 미분한 값이니깐요!

 

 

 

그리고, 위에서 설명했듯이, mul gate는 backward pass를 할떄, 서로서로 바꿔서 곱해준다고 했으니깐,

각각을 계산한 값들은 위와 같이

dx = self.y * dz

dy = self.x * dz

가 되겠죠?

 

이런 방식으로, forward pass와 backward pass를 구현할 수 있습니다!

 

 

물론 그걸 우리가 다 하나하나하고 있을 건 아니고, caffe 같은 라이브러리들이 대신 짜둔 것들을 우리가 써먹기만 하면 됩니다!

위에서 계산은 실전에서 그렇게까지 중요하지 않다고 한 이유가 바로 이건대, 우리가 직접 계산해야 될 것들은 별로 없기 때문이죠.

 

 

 

킹 갓 스탠퍼드 형님들은 이걸 저번에 배웠던 SVM과 섞어서 코드를 짜는 게 숙제라는데,

직접 해본 결과 도저히 backward pass를 짤 수가 없어서 GG 쳤었네요 ㅎㅎ

도전심이 들면 도전해보셔도 좋은데, 저처럼 이거 하느라 5시간씩 날려먹진 않으셨으면 좋겠네요..

 

 

자, 이제 정리해봅시다!

**** 이건 위에 안 읽으신 분들도 보셔야 돼요!! ****

 

인공 신경망은 굉장히 거대할 겁니다. 기울기, 즉 미분 값들을 하나하나 손으로 써 내려가는 것은 미친 짓입니다! (한번 해보시면 앎 ㅎ)

그래서, backpropagation(역전파법)을 사용할 겁니다.

chain rule이라는 일종의 수학 공식?을 사용하여, 모든 입력값 및 변수들에 대한 기울기를 계산해 나갈 겁니다.

그리고 이것들을 하기 위해서, 우리는 forward와 backward를 구현할 겁니다.

forward는 그냥 gate들을 거쳐가며 정방향으로 계산해 나가면서, 나오는 값들을 메모리에 저장해 나가는 것입니다.

backward는 그 값들을 토대로 뒤에서부터 계산하며 chain rule을 적용시켜, input 값이 loss값에 어떤 영향을 미치는지를 (기울기 값을) 계산할 것입니다.

 

여기서 나올 수 있는 질문 하나!

Q. 그런데, 굳이 거꾸로 계산하는 이유가 뭐임??

A. 정방향으로 계산하면서 하나하나 미분 값을 계산하는 것은 굉장히 연산의 낭비가 심합니다. 직접 한번 위의 예시들을 정방향으로 계산해 보면, 이미 했던 계산 또 하고 또 하는 자신을 발견하게 될 겁니다.

 

다른 질문 있으시면, (설명 안 했던 부분이라 하더라도) 댓글로 질문해 주시면 답변해 드리겠습니다 ㅎㅎ

 

그럼 이제 인공신경망을 건드려 볼까요?

아, 이번 시간에 그렇게 자세하게 들어가진 않습니다!

윗부분 읽으시면서 힘들으셨던 분들도, 쉽게 쉽게 이해하고 넘길 수 있는 부분일.. 겁니다.. 아마?

 

 

 

 

일단 인공 신경망을 우리의 신경망과 연관 짓지 말고 생각해보겠습니다.

 

원래 우리의 선형 분류기에서의 점수 계산은 그냥 f=W*x로 했었던 거, 기억 나시죠?

근데, 이제 그 폭을 한번 넓혀보겠습니다.

그냥 W라는 레이어 하나만을 지나는 것이 아니고, 다른 W 두 개를 지나게 만들어버리는 것이죠!

그러면 더욱 정확해지지 않을까? 하는 것이죠.

마치 초벌 된 치킨 맛과 재벌 된 치킨 맛의 차이랄까요? 물론 따로따로 먹어본 적은 없어서 잘 모르겠지만 아무튼 ㅎㅎ

 

위의 예에서는 f=Wx를 발전시켜

f=W2*max(0, W1x)

로 만들었습니다.

 

이를 조금 더 풀어보겠습니다.

우선, W1과 x를 곱해서 점수를 먼저 냅니다.

이거까지는 linear score function과 같죠?

그리고, 이 점수가 0보다 크면, W2라는 다른 레이어를 곱해줍니다. (0보다 작으면 그냥 0을 곱해줍니다!)

이렇게 이미 한번 곱해준 친구를 다시 한번 곱하는 것이죠.

그리고 이번 예에서는, W1과 x를 곱해줄 때 100개의 점수를 냅니다.

그 뒤, W2와 max(0, W1x)를 곱할 때에, 그 100개의 점수에서 10개의 점수로 줄여나갑니다.

(무조건 100개에서 10개로 줄여야 하는 것은 아닙니다! 하나의 예시일 뿐입니다!)

 

자, 그런데 대체 왜 max(0, W1x)라는 식이 쓰였을까요?

바로 non-linearity, 즉 비선형 식이 쓰여야 하기 때문입니다.

이에 대해서는 이후 6강에서 더욱 자세하게 다루겠지만, non-linearity 식이 중간에 껴들어가지 않는다면, 아무리 많은 레이어들을 합쳐봤자 결국 하나의 레이어와 같은 결과를 내기 때문입니다.

 

'어라? 그런데 그러면 max가 아니라 다른 것도 쓸 수 있지 않아요? 그냥 선형만 아니면 되는 거잖아요!'

네, 바로 그렇습니다. 위의 max(0, W1x)는 non-linearity 식 중 하나인 ReLU function이라는 것인데, 이후에 한번 나오고, 6강에서 다시 한번 보게 될 것입니다.

이 같은 non-linearity function은 위의 max(0, W1x)인 ReLU 말고도 다른 것들도 많은데, 이처럼 Linear 사이에 껴들어가는 non-linear 한 함수들을 일컬어 activation function이라고 합니다. 나중에 한번 더 나오니깐, 그때 더 자세하게 합시다!

 

자, 이러면 다중 레이어의 인공신경망의 기초를 알았는데, 이것을 왜 했는지가 중요하겠죠?

Linear Classifier를 배웠을 때, 한계점에 대해서도 배웠었죠?

각각의 클래스마다 하나의 사진만 나와서, 그에 최대한 가까운 사진들을 분류해내는 작업을 하는 것이었는데, 위의 그림을 보면 아시겠지만

자동차 같은 경우에는 그냥 아예 빨간 자동차의 경우만 제대로 자동차라고 인식하고, 다른 색상, 예를 들면 노란색의 차는 제대로 자동차라고 인식을 안 할 것이라고 예산할 수 있습니다.

하지만, 이와 같이 여러 개의 레이어를 덮어 씌웠다면, 중간에 100개의 h 중 하나는 빨간 자동차가 자동차라고 가리키는 노드도 있을 것이고, 노란 자동차가 자동차라고 가리키는 노드도 있을 것입니다.

그리고 이것 모두를 W2에다가 다시 한번 집어넣으니, 이젠 더욱 다양한 물체들도 인식이 가능한 것이죠.

 

어휴.. 슬라이드 하나에만 말할 것이 이렇게나 많다니!! 싶으시죠?

이 중에서 이해가 잘 안 되시는 것이 있어도, 걱정 말고 넘기셔도 됩니다. 어차피 5강과 6강에 걸쳐 다시 한번씩 계속 계속 설명해 드릴 테니까요.

 

 

 

그러면, 레이어 두 개가 아니라 세 개를 만들고 싶으면 어떻게 하면 될까요?

미리 만들어뒀던 2-layer를 그냥 따와서, non-linear 한 함수에 집어넣고, 다시 다른 레이어로 덮어 씌우면 되겠지요?

그것이 위 사진 중 가장 아래 식인,

f=W3*max(0 , W2*max(0, W1x))입니다.

그러면, 4-layer은? 5-layer은?

마찬가지로, 위의 f값에서 레이어를 하나씩, 하나씩 더 씌우면 되겠지요?

 

 

 

 

그럼 이제 조금 더, 인간의 신경망(뉴런)과 비교해볼까요?

...라고 하고 싶지만... 저는 저 뉴런의 구조와 같은 쪽에는 그냥 아예 무지한지라.. 설명이 불가능하겠네요..

그냥 우리가 방금 했던 인공 신경망이 우리 두뇌의 신경망에서 따온 것이라는 것만 알면.. 되겠습니다.

일단 슬라이드는 이해용으로 올려놓겠습니다만.. 흨 ㅠㅠ

 

아! 여기서부턴 다시 설명 시작 가능하겠네요 ㅎㅎ

아까 전에 Activation function부분입니다!

ReLU, 왼쪽 아래에 보이시죠? 저게 아까 봤던 max(0, W1x) 부분을 그래프로 나타낸 것입니다.

저것처럼, sigmoid, tanh, Maxout 등 다양한 친구들이 있습니다!

아 근데, 이거 지금 자세하게 다룰 거 아니고, 6 강가면 지겹도록 다루니깐, 그때 가서 봐요!

 

 

그리고, 2 레이어 인공신경망/3 레이어 인공신경망의 구조입니다.

우선 당연하게도 input layer에서 입력값이 주어질 것이고,

그다음 hidden layer라는 곳에는 x*W1값이 들어가게 되고,

output layer에서는 (위의 예에서라면) W2*max(0, W1x)가 들어가서, 결국 output에서는 점수가 뙇! 하고 나오겠죠?

3 레이어도 마찬가지로, W1*x가 첫 번째 계산, W2*max(0, W1x)가 두 번째 계산, 마지막엔 W3*max(W2*max(0, W1x))가 계산되겠죠?

이처럼, 중간중간에 모든 노드가 다음의 모든 노드에 영향을 끼치는 레이어를 fully-connected layer라고 합니다!

위의 Neural Network의 예에서도, 모든 W값들이 다음 값들에 영향을 미쳤으니, 그것도 fully connected layer라고 할 수 있겠죠?

 

또, 2-layer개에는 히든 레이어가 1개, 3-layer에는 히든 레이어가 2개... 이므로,

각각 1-hidden-layer 인공신경망, 2-hidden-layer 인공신경망 이라고도 불린다네요.

 

 

 

 

 

자, 그래서 우리는 인공신경망의 대략적인 구조를 알아보았습니다.

더욱 자세한 내용은 5강, 6강에 걸쳐 나오므로, 일단 이 정도만 알고 계시면 되겠습니다(그래도 어느 정도는 이해하셔야 합니다!)

 

이제 다음 강좌 때는, cs231n 5강의 내용인, CNN(Convolutional Neural Network)에 대해 알아보도록 하겠습니다!

 

 

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

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

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

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

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

 

 

이번 강좌에서는, Loss Function과 Optimization이라는 것들에 대해 배우겠습니다.

 

 

그전에, 일단 저번 강좌 복습부터 해보도록 하겠습니다.

우선, 이미지를 인식하는 것이 어째서 어려운 일인가? 에 대해서 이야기하였습니다.

 

 

데이터에 기반한 접근법과, K-Nearest Neighbor에 대해서도 배웠습니다.

왼쪽 위에 있는 것은 CIFAR-10데이터셋이고, 그 오른쪽은 K=1일 때와 K=5일 때의 차이를 그림으로 표현한 것입니다.

그 아래 train|test, train|validation|test는, 우리가 hyperparameter를 설정할 때 사용해야 하는 방식이라고 배웠던 것입니다.

둘 중에 어떤 것을 사용해야 했는지 기억이 안나신다면, 2강으로 다시 돌아가서 강좌를 봐주시기 바랍니다.

그리고, 그 오른쪽은 hyperparameter를 하나하나 설정해보며 어떤 값이 가장 최적의 값인지 알아내는 과정을 표로 나타낸 것입니다.

 

 

이제부터는 Linear Classifier에 대한 이야기입니다.

위의 K-Nearest Neighbor과는 다르게, W값만으로 predict가 가능하다는 장점과, 더 정확한 정보를 얻을 수 있다는 것이 장점이었습니다.

f(x, W)=W*x+b라는 식으로, (CIFAR-10 기준으로) 10가지의 카테고리당 점수를 알 수 있었습니다.

그 아래는, 이 Weight값들을 사진으로 나타내어, Linear Classifier가 알아낸 카테고리의 특징을 알아볼 수 있습니다.

 

 

이 사진은, W값이 랜덤이기에 일어나는 상황이라고 하였죠. 자동차를 제외한 두 가지 카테고리의 점수는 정답을 거의 맞히지 못하는 상황입니다.

그리고, W값이 랜덤이라는 점에서부터, 도대체 W값을 어떻게 바꾸어 나가야 제대로 된 점수를 얻을 수 있을까?라는 질문을 던졌었죠?

바로 그 질문에 대해서 오늘 알아볼 시간입니다.

 

아! 들어가기 전에, 이 위의 내용 중 잘 모르는 내용이 있다면, 다시 전 강좌로 돌아가서 다시 한번 강좌를 봐 주시기 바랍니다.

최소한 Linear Classifier부분은 완벽하게 이해가 되어 있어야 해요!

 

우선, 저번의 예제를 단순화시킨 곳으로 가봅시다.

고양이, 자동차, 개구리 세 가지의 카테고리밖에 없다고 가정하고, 그 각각의 점수만 나오는 것이죠.

예제를 보면, '고양이' 이미지는 자동차가 1등, 고양이가 2등, 개구리가 3등으로 나오고,

'자동차' 이미지는 자동차가 1등, 개구리가 2등, 고양이가 3등으로 나오고,

'개구리' 이미지는 자동타가 1등, 고양이가 2등, 개구리가 3등으로 나옵니다.

이렇게 된다면, '자동차' 이미지만이 정답을 맞혔고, 나머지 두 '고양이'와 '개구리'이미지는 정답을 맞히지 못한 것을 알 수 있습니다.

즉, 우리의 Linear Classifier가 썩 잘 작동하는 것은 아니라는 것이고, 우리는 이것을 고치고 싶습니다.

그래서 우리는 이러한 일에 대해 점수를 메기기로 했습니다. 과연 우리의 Linear Classifier가 얼마나 못하고 있는 걸까? 하는 것에 대한 점수죠.

이 점수를 우리는 Loss(오차)라고 합니다. 얼마나 정답에 오차가 있는가.. 하는 느낌이죠

그리고, 그 Loss를 도출하는 함수가 바로 Loss function(오차함수)입니다.

오른쪽의 식을 한번 볼까요? xi값은 이미지, yi값은 그 이미지의 카테고리의 정답인, N개의 데이터셋을 가지고 있다고 해봅시다.

그렇다면, 전체 오차값인 L은 어떠한 오차 함수 Li에 우리의 Linear Classifier인 f(xi, W)와 yi값을 집어넣은 값의 평균이라고 할 수 있습니다.

이러한 방식은, 다른 다양한 딥러닝 작업에 사용되는 오차값을 구하는 공식입니다. 그리고 이 안의 오차 함수가 무엇이 들어가느냐에 따라, 여러 가지 다른 오차값을 구할 수 있죠.

 

 

그러면, 여러가지 오차 함수중 하나인 SVM Loss를 알아볼까요?

SVM loss는 작동하는 방식이 굉장히 간단한 오차함수 중 하나입니다. svm loss가 작동하는 방식은 다음과 같습니다.

 

 

1) 카테고리를 본다. 만약 그 카테고리가 정답 카테고리라면, 씹고 넘어간다.

2) 정답 카테고리가 아닐 때, (현재 카테고리의 점수 + 1)이 (정답의 점수) 보다 작다면, 씹고 넘어간다.

3) 그렇지 않다면, (현재 카테고리의 점수 + 1) - (정답의 점수)를 오차값에 더해준다.

4) 이렇게 모든 카테고리를 돌았을 때, 나온 최종 오차값을 구한다.

 

참고로, 2)와 3)은

2)+3) 0과 현재 카테고리 점수 + 1 - 정답 점수 중 최대 점수를 구해서 더한다.

로 줄일 수도 있습니다.

 

어느 정도로 직관적인 식이라고, 개인적으로 생각합니다만.. 두 가지 정도의 의문이 들 수 있을 것 같습니다.

 

Q1. 음.. 잠시만요? 이러면 점수가 낮으면 좋은 건가요?

A1. 네, 그렇습니다. 아까 말했다시피, 우리가 구하는 것은 오차값입니다. 얼마나 '틀려있나'의 정도를 나타내는 값이 오차값이라고 하였으므로, 이 점수가 낮다는 것은, 틀려있는 정도가 낮은 것이므로, 점수가 낮으면 낮을수록 더욱 정확하다고 할 수 있겠죠.

Q2. 아니, 그런데 왜 그냥 현재 카테고리의 점수가 아니고, 현재 카테고리의 점수 + 1 값을 비교하고, 더해주는 거죠??

A2. 만약 현재 카테고리의 점수 그 자체를 비교한다면, 2등의 점수와 1등의 점수가 굉장히 근사하다 하더라도, 오차값은 무조건 0, 즉 최고로 좋은 상태가 되어 있을 것입니다. 이런 상황은 우리가 그다지 반기는 상황이 아닙니다. 비록 정답은 맞혔다 하더라도, 굉장히 간당간당하게, 다르게 말하면 운 좋게 맞춘 꼴이 된 것이니깐요.. 그렇기 때문에, 더욱 확실히 정답을 맞히는 것을 목표로, +1을 더해주는 것입니다.

 

이거 말고 다른 질문이 있으시다면, 댓글로 남겨주시면 감사하겠습니다..

 

그래서 이 SVM loss를 구한 것을 그래프로 그려본다면, 오른쪽의 그래프와 같은 꼴이 되는 것 까지는 그릴 수 있겠죠? 

x좌표가 이미지의 정답의 점수이고, y축이 오차값이 된 그래프입니다. Sj, 즉 어떤 카테고리에 대하여, Sy값이 Sj+1보다 크거나 같다면 오차는 0이고, 그것이 아니라면 Syi가 작으면 작을수록 오차값이 커지는 것을 알 수 있습니다.

이를 hinge loss 라 하는데, hinge(경첩)처럼 그래프가 생겨서 그런 것이라던데.. 전 잘 모르겠네요 ^ㅡㅡ^... 그냥 그렇다고요!

 

 

아직도 이해가 잘 안 되실 수도 있습니다. 아직 구체적으로 계산을 안 해봐서 그런 건데요, 한번 구체적으로 계산을 해보도록 합시다.

개구리를 예로 들어볼까요?

1) 고양이 점수 : 2.2, 정답(개구리) 점수 : -3.1이므로, max(0 , 2.2+1-(-3.1)) = max(0 , 6.3) = 6.3

2) 자동차 점수 : 2.5, 정답(개구리) 점수 : -3.1이므로, max(0 , 2.5+1-(-3.1)) = max(0 , 6.6) = 6.6

그리고, 이 둘을 더한 값은 6.3+6.6=12.9이므로,

이 개구리 이미지에 대한 오차값은 12.9라는 것을 알 수 있습니다.

이와 같은 방식으로 고양이 및 자동차의 오차값도 구할 수 있겠죠?

 

 

 

그리고, 최종 오차값은 전체 이미지의 오차값의 평균이라고 하였으므로, (2.9+0+12.9)/3 = 5.27이라는, 최종적인 오차값이 나옵니다.

 

 

그래서, 이를 파이썬 코드로 짠 것을 봅시다.

** 텐서 플로우, 케라스, 파이 토치 등의 라이브러리를 사용할 때는 이런 코드를 사용할 필요는 없지만, 위 코드를 정확히 이해한다면 SVM이 무엇인지를 더 잘 이해할 수 있습니다. 만약 파이썬 및 numpy에 대해 잘 모른다면, 그냥 넘어가도 좋습니다.**

def L_i_vectorized(x, y, W)    #x는 이미지 픽셀 값, y는 정답 카테고리, W는 가중치입니다.

scores = W.dot(x)    #점수를 계산해 줍니다.. dot은 저번에 말한 점수를 계산할 때 쓰이는, dot product, 즉 점곱을 의미합니다.

margins = np.maximum(0, scores - scores [y] + 1)    #0과 (현재 카테고리의 점수 + 1) - (정답의 점수)의 최대를 margins에 넣습니다.

margins [y]=0    #하지만, 정답 카테고리의 점수는 무조건 0이므로 정답 카테고리를 의미하는 y값에는 0을 넣어줍니다.

loss_i = np.sum(margins)    #그리고, 이 점수들의 합을 구해주고..

return loss_i    # 이 값을 리턴합니다.

 

다시 이론으로 돌아와 봅시다. 그래서 우리가 이 W값을 잘 조정해서, loss값을 0을 만들었다면, 우리는 완벽한 classifier를 만들어 냈다고 볼 수 있겠죠? 그렇죠??... 네 아닙니다 ㅎㅎ

눈치가 빠르신 분들이라면 위 이미지만 보고도 알아차렸을 수도 있겠습니다. 우리가 loss값이라고 만들어 놓은 것은 어떤 값인가요?

바로, training set에 대한 오차값입니다. training set에 대한 오차값이 굉장히 줄어든다고 하여도, 우리가 실제로 넣어보고 싶은 test set에 대한 오차값은 결코 장담할 수 없습니다.

위의 그래프를 보시면 이해가 빠를 것 같습니다. 파란 점은 training data, 초록 점은 test data입니다. 우리가 이 training set의 오차값을 0으로 만들었다면, 아마 우리의 분류기는 저 파란 선처럼 생긴 분류기처럼 작동할 것입니다.

하지만, 저 파란 선은 초록색 점인 test data가 들어왔을 때에는 영 엉망인 결과를 도출할 것입니다.  그리고, 초록 선은, 바로 우리가 지향하고자 하는 선이 되겠습니다. 초록색도 어느 정도 잘 맞으면서, 파란 점들도 대부분을 비슷하게 만족하는 선이니깐요.

참고로, 파란 선처럼 선이 그어지는 것을 바로 overfitting(과적합)이라고 합니다. training set에 너무 근접해 버려서, test set에서의 정답을 맞히지 못하게 되는 문제를 말하는 것이죠.

 

이렇듯, 우리는 classifier가 최대한 복잡하지 않고, 최대한 간결하고 심플하게 되는 것을 선호합니다. 그래야지, 직접 test set에 부딪혔을 때에도 좋은 결과를 도출할 수 있기 때문이죠.

 

 

그렇다면, 이런 문제는 어떻게 해결해야 할까요? 우리는 여기서 regularization(한글로 어떻게 번역하지..? 에잉 몰라 ㅎㅎ)라는 것을 사용할 것입니다. 복잡해진 classifier에 페널티를 가해서, 최대한 간결하고 심플하게 만들어 주기 위한 방법입니다.

위에서는 R(W)라는 놈이 바로 그 Regularization의 식입니다.

저 위의 식에서 보이는 ㅅ자 비슷한 놈은 '람다'라는 변수인데, 저것을 우리는 hyperparameter로 사용하여, R(W)의 값을 적당히 맞춰주는 역할로 설정해 줄 것입니다. 저게 너무 강해져 버리면, 너무 간결함만을 유지하고자 하게 되어서 loss값을 맞춰주는 큰 의미가 없어지고, 너무 작으면 regularization 하는 의미조차도 없어지게 돼버리기 때문이죠.

 

자주 사용되는 것은 L2 Regularization, L1 Regularization, Elastic Net 등등이 있다고 하는데, 일단 지금은 L2 Regularization이 뭔지만 먼저 보러 가봅시다.

 

 

 

자, 우선 예시를 들어봅시다. R(W)가 L2 Regularization이고, x값(이미지 픽셀 값들)은 [1,1,1,1]이라고 해봅시다.

그리고, w1 [1,0,0,0]과 w2 [0.25,0.25,0.25,0.25]가 각각 가중치라고 했을 때, 우리가 dot product(점곱)을 해서 답을 구했을 때는 모두 1이라는 똑같은 값이 나오겠죠?

그런데, L2 Regularization은 w1과 w2중 어떤 w값을 보았을 때 더 간결하다고, 또는 더 좋다고 생각할까요?

R(W)는 값들의 제곱을 더한 놈이므로, 당연히 w2가 더 좋다고 생각할 것입니다. w1의 모든 원소의 제곱보다는 w1의 모든 원소의 제곱이 더 작으니까요.

즉, L2는 w값이 최대한 평평하게, 너무 큰 값 없이 최대한 비슷한 값으로만 이루어지게 하고 싶은 것이죠. 이러면, 이미지에 가는 가중치들이 다들 비슷비슷해 지므로, 조금 더 전체적인 그림을 볼 수 있지 않을까? 하는 느낌인 것이죠.

 

자, 이제 다음 스테이지로 넘어가서, 지금까지 하던 SVM이 아닌 Softmax라고 불리는 다른 친구에 대해서 배워봅시다.

아까 전의 SVM에 대해서, 어떻게 SVM이 이루어지는지를 다시 복붙 해 오면..

 

1) 카테고리를 본다. 만약 그 카테고리가 정답 카테고리라면, 씹고 넘어간다.

2) 정답 카테고리가 아닐 때, (현재 카테고리의 점수 + 1)이 (정답의 점수) 보다 작다면, 씹고 넘어간다.

3) 그렇지 않다면, (현재 카테고리의 점수 + 1) - (정답의 점수)를 오차값에 더해준다.

4) 이렇게 모든 카테고리를 돌았을 때, 나온 최종 오차값을 구한다.

 

그리고 이것이 바로 SVM Loss가 구해지는 방식입니다.

그렇다면, 이제부터 말할 Softmax는 어떤 방식으로 loss를 구해낼까요?

 

이 슬라이드를 보면 대충 이해가 빨라질 겁니다.

우선, SVM때와 같이 각 클래스마다 점수를 구해줍니다.

그리고, 각 점수로 e를 제곱해 줍니다.

가령, cat 같은 경우엔 e^3.2가 되어서 대략 24.5가 나오고,

frog 같은 경우는 e^-1.7이 되어서 대략 0.18이 나옵니다.

그리고, 이 수들을 죄다 normalization(정규화) 해 주어서, 확률로 만들어 버립시다. 죄다 더하면 1이 되게 만들자는 것이죠!

그러면, cat의 확률은 0.13, car의 확률은 0.87, frog의 확률은 0.00... 이 됩니다.

 

이야 끝났다!.. 하고 있으면 안 되겠죠? 우리가 구하고 싶은 건 오차값인데, 0.13이 오차값이 되어버리면, 이건 굉장히 좋은 오차값이 되어버리니깐 말이죠.

그러니까, 우리 이 값에 -log 한번 씌워 봅시다!

그러면, 작은 값은 도리어 큰 값으로 나오고, 큰 값은 작은 값으로 나오니깐, 실제 오차값을 구할 수 있겠죠?

이렇게, 각 점수마다의 확률을 구하여 오차값을 구하는 방식을 Softmax라고 부릅니다.

 

 

 

위 그림은 우리가 했던 SVM과 Softmax의 과정을 나타내는 그림입니다!

이해가 안 되는 부분이 있다면, 위부터 다시 한번 찬찬히 읽어 보시기를 권장합니다.

 

그런데, SVM과 Softmax의 차이점은 무엇일까요?

 

일단, SVM은 Loss가 0이 되는 순간, 자기 할 일 다 한 겁니다. max값만을 구하기 때문에, 정답 카테고리가 y [0]이고, 점수가 [10,9,9,9]같이 나오더라도 Loss값은 0이죠? 이걸 완벽한 Loss로 보는 것이죠.

하지만, Softmax는 위 같은 상황을 봐도, 최대한 정답 카테고리의 확률을 높이기를 원하기 때문에, 계속해서 확률을 높이려고 애쓰겠죠? 쭉쭉 올라가서 점수가 [1000,5,3,1] 같아져도 계속해서 확률을 높이기 위한 방향으로 나아갈 겁니다. 웬만해선 loss가 0이 나오지 않는다는 거죠.(0에 굉장히 근접한 값은 나오겠지만 말이죠.)

 

 

자, 오늘 한 내용 정리 한번 해봅시다!

(x, y)의 데이터셋을 가지고 있을 때, 점수를 dot product를 통해서 구한 후, SVM 또는 Softmax와 같은 loss function으로 얼마나 오차가 있는지를 알아낼 것입니다.

또한, Regularization , 즉 R(W)를 함께 더해서 최종 loss값을 구할 수 있습니다.

 

여기까지 잘 따라오셨다면, 이런 의문이 들 차례입니다!

'아니 그래서, 분류기를 어떻게 훈련시키는데?? 이것만 가지고서는, 어떻게 W값을 찾는지는 모르잖아!'

 

그래서 그건 다음 시간이...

 

 

에 하고 싶은데, 이것도 3강에 있네요??

한번, 달려볼까요?!

좋은 W값을 찾아내는 과정인, Optimization(최적화) 시간입니다!!

 

 

이 optimization을 나타낼 때에 가장 자주 쓰이는 비유를 들어 설명해 보겠습니다.

 

제가 여러분들의 눈을 가려버리고, 험한 산골짜기 어딘가에 여러분을 던져놓고 왔다고 생각해봅시다.

그리고, 여러분들은 이 산을 내려오고 싶어 하고요.

자, 이때, 눈을 가린 상태로 산을 내려오려면 어떻게 해야 할까요?

아 물론, 여러분들이 진짜 엄청난 감각의 신이라서, 막 바람을 느끼고 산 향기가 제일 적은 곳으로 텔레포트를 뙇! 하면 딱 내려가질 수도 있겠지만, 사실상 말이 안 되죠..? 산이 험하면 험할수록, 이것도 굉장히 어려워지니깐요.

결국, 여러분들은 어떤 반복적인 행동을 통하여 산을 내려가야 할 겁니다.

이 과정을 두고 optimization이라고 합니다.

산의 높이가 loss이고 여러분의 위치를 W라고 한다면, loss가 적은 곳으로, W를 변화시키며 내려가는 것이니깐요.

 

자, 그럼 한번 생각해봅시다! 여러분들이라면, 눈을 가린 상태로 어떻게 산을 내려가실 건가요?

 

 

자, 우선 가장 멍청한 방법부터 생각해 봅시다.

'야, 그냥 아무렇게나 가면 되겠지 뭐! 어떻게든 되지 않을까?'라는 생각이죠.

W값을 막 바꿔가면서, 최적의 loss값을 찾는 것이죠.

 

 

한번 test set에 적용시켜 보면, 15.5% 정도의 정확도가 나온다고 합니다!

띠용~? 생각보다 좋잖아!라고 생각하시진 않겠죠?

State Of The Art(현대 최신 기술)로는 95% 정도가 나오는 건데.. 15.5%면.. 처참합니다 ㅎㅎ..

 

 

그러면, 다른 방법을 생각해 봅시다.

땅 주변에 발을 가져다 대 보면서, 좀 낮은 곳으로 조금 움직인 다음, 다시 한번 발을 갖다 대 보며 더욱더욱 낮은 곳으로 내려가 보는 것이죠.

 

 

자, 드디어 등장! 우리의 멋진 미분이라는 친구예요!!

근데, 이게 뭔지 설명해버리면, 강의 제목인 '누구나 이해할 수 있는'이 애초에 성립이 안되잖아요..?

간단하게, 미분은 어떤 함수의 기울기를 구한다!라고 생각합시다.

우리의 산이 지금 얼마나 기울어져 있는가? 를 알고 싶다면, 미분이라는 것을 하면 된다!라고 해 둡시다.

물론 미분이 뭔지 안다면 그냥 지나치면 되겠지만 말이죠 ㅎㅎ

 

대충 미분을 쓸 거고, 위의 식 f(x+h)-f(x) / h라는 공식을 써볼 거다!라는 것만 알아둡시다~!

 

자! 현재 W값으로는 loss가 1.25347이 나온다고 해 봅시다.

그러면 우리는 조금, 조금씩 발을 아래쪽으로 내리면서 갈 거죠?

 

 

0.0001이라는, 아주 적은 수를 W의 첫 번째 원소에 더해보니, Loss가 줄어들었습니다!

 

 

 

그리고 한번 '미분'이라는 것을 해보니, 기울기가 -2.5가 나온다네요?

 

 

또 더하니깐, 이제는 loss가 커져버렸습니다!

 

 

그러면, 또 미분을 해서 보았을 때, 기울기 값이 뙇! 하고 나오고, 이번에는 그 기울기 값이 양수라는 것을 알 수 있네요.

 

아 잠깐만요!! 이거 계속해서 뭐 어쩌자는 거임!

W값은 더럽게 크고, 이거 0.0001씩 조금조금씩 더해서 이거 언제 다함;;

안 그럼?? 안 그러냐고!!

 

네, 그렇습니다!

이걸 일일이 다 하고 있기에는 너무 오래 걸리겠죠?

 

 

 

그래서, 뉴턴 형아랑 라이프니치 형아가 와서 도움을 조금 주고 간다네요?

얍!! 하면, 수학적으로 저 위의 미분을 간단하게 할 수 있고,

 

 

쨔잔!! 미분 값이 구해졌네요? 이야 신난다! 끝!!

 

 

 

 

 

.. 갑자기 왜 이리 대충 하냐고요? 

어차피 이거 보시는 분들은 인공지능을 처음 배우시거나, 초보분이실 텐데, 이거 자세히 알 필요가 없어요! 

일단 다음 슬라이드로 넘어가 봅시다!

 

 

아까 식으로 f(x+h)-f(x) / h를 한 것을 numerical, 그냥 미분 공식으로 나온 것이 analytic입니다.

numerical로 하면, 느리고, 정확하진 않지만(컴퓨터가 소수점을 약간은 빼버리니깐..) 코드 작성은 쉽고,

analytic으로 하면 빠르고, 정확하지만 코드 작성이 어렵답니다.

 

아 근데요, 어차피 이거 미분식 코드로 짤 거 아니잖아요?? 텐서 플로우나 케라스나 파이 토치나.. 저거 미분식 하나하나 다 쓰고 있을 필요가 없어요!

그러니깐, 그냥 이것만 알아두고 갑시다.

'미분'을 통해서 조금조금씩 loss가 적은 방향으로 내려갈 것이다!

 

그리고, Gradient Descent, 즉 기울기 하강을 통하여 가장 적절한 W값을 찾아낼 것입니다.

코드가 되게 간단하죠? evaluate_gradient에서 미분 값을 계산하고, weights를 step_size * 기울기만큼 빼준다네요?

 

엥? step_size가 뭔데요??

뭐, 엄밀한 정답은 아니지만, 아까의 비유를 가져와서 다시 설명하겠습니다.

아까 산을 내려간다고 했죠? 근데 만약에, 여러분 다리가 미친 듯이 길어서, 가야 할 방향으로 조금만 가야 하는데 막 100km를 가버렸다고 해봅시다. 그럼 과연 그게 제대로 내려간 걸까요? 

아니겠죠?? 그냥 날아간 수준으로 간 거죠? 그러니깐, 별로 좋지 못하다는 겁니다. 그래서, step_size를 0.001, 0.0001 같은 수로 정해줘서, 다리를 조금 줄여주는 거죠. 최대한 적당한 만큼 가게 말이죠. step_size가 너무 작아서 다리가 너무 작아지는 것도 문제겠지만 말이죠..

즉, step_size란 친구는 우리가 잘 정해줘야 하는 hyperparameter라는 겁니다.

 

자, 보라-파랑-하늘-.. -빨강 순으로 낮은 곳이라고 생각해봅시다. 그러니깐, 중앙이 가장 낮은 부분이라고 생각해 봅시다. 3차원 그릇 같은 모양인 거죠.

그러면, 대충 우리가 가야 하는 방향 (미분해서 나온 기울기의 방향)은 위의 하얀 화살표 방향 비슷하게 나올 겁니다.

그리고, 우리는 중앙으로 가고 싶어 하는 것이죠.

 

여기서 step_size의 조금 더 엄밀한 내용이 나옵니다.

만약 우리가 저 방향으로 너무 멀리 가게 돼버린다면, 우리가 지향하는 빨간색이 아닌, 저ㅓㅓㅓㅓ 끝의 보라색으로 가버리겠죠?

즉, 현재 지점보다 높은 곳으로 갈 수 있다는 건데, 현 지점보다 높은 곳으로 가버리면, 기울기가 더욱 커지게 됩니다.

그러면 또다시 더ㅓㅓㅓㅓ욱 더ㅓ====욱 먼 곳으로 날아가고, 이런 악순환이 반복되게 됩니다.

그렇기 때문에, step_size를 잘 설정해줘서 최대한 제대로 가게 만들어야죠.

하지만, step_size가 너무 작으면, 가자는 방향으로 아주 쬐ㅣㅣ끔만 가는 꼴이 되는데, 그러면 진짜 엄청나게 오래 이 짓을 해야 할 겁니다.

마치, 아래 방향으로 산을 손톱 크기만큼 내려가고, 또 그렇게 내려가고.. 하면 되게 오래 걸리겠죠?

그러니깐, step_size를 최대한 적절하게 조정해 주어서 빨간 방향으로 잘 가게 만들어줍시다.

 

 

여기서 잠깐!

근데, 모든 사진들을 이런 식으로 계산하기에는 너무 오래 걸립니다.

가령, Imagenet Challenge에서는, 대략 1.3 Million, 즉 130만 개의 training set이 있는데, 이 사진 하나하나를 죄다 미분하고 계산하고 하기엔 너무 느리고, 오래 걸립니다.

 

그래서 생각해낸 방법이 바로 Stochastic Gradient Descent, 줄여서 SGD입니다.

아이디어는 간단합니다. 매번 loss를 계산할 때, 우리 training set에서 랜덤으로 조금만 가져와서, 그 사진들에 대한 loss를 계산하고, weight를 계산하는 겁니다.

이러면 당연히 더 빨리 계산이 가능해지겠죠?

대부분의 경우에는 이런 SGD기법을 사용하여 계산을 더욱 빠르고 효율적으로 진행한다고 하네요 ㅎㅎ

 

이야! 쉬는 시간이다!!

http://vision.stanford.edu/teaching/cs231n-demos/linear-classify/

직접 들어가서, x, y, Softmax/SVM, Regularization 등을 손대 보면서 최적의 값을 찾아보세요!

step_size잘못 건드리면 황천길 가고,

Regularization 잘못 건드리면 무지개다리 건넌다는 것만 확실하게 느끼면 좋겠네요 ㅎㅎ

 

 

 

 

 

 

*뒤에 슬라이드들 여러 개 있는데, 별로 안 중요해 보여서 그냥 거르겠습니다^^

저거 해봤자 나도 힘들고, 보는 사람도 힘들고 해서 ㅎㅎ

 

아무튼! 최종 정리 한번 해봅시다.

 

1.SVM/Softmax loss에 대해서 배웠다.

2. Regularization에 대해서 배웠다.

3. optimization에 대해서 배웠다.

3-1. step_size가 뭔지 배웠다.

4. SGD가 뭔지 배웠다.

 

아니.. 뭔가 되게 많이 한 것 같은데 별거 없잖아?? 내가 뭐 빼먹었나?

아몰랑 ㅎㅎ 아무튼 저 위에 4개 중에 기억이 잘 나지 않는 부분이 있다면, 다시 한번 읽어 보고, 위 내용만큼은 확실히 흡수할 수 있도록 합시다.

근데 위에 4개 말고 했는데 빼먹고 안 적은 거 있을 수도 있는데.. 아무튼 저것만큼은 기억합시다!

 

 

다음 시간에는, neural network(인공신경망)에 대해서 배울 것이고,

Backpropagation(역전파법)에 대해 배울... 건... 데...

다음 강좌는 굉장히 *수학 수학* 해질 것 같네요..

아무튼, 다음에 봐요! 안녕!@@

 

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

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

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

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

- 1강은 전체적으로 컴퓨터비전이 어떤 역사를 가지고 있는지 등에 설명하는 내용이므로, 일단 생략하도록 하겠습니다.

 

 

 

 

컴퓨터 비전에서 가장 중요한 것은 바로 이미지 분류 작업입니다. 이미지 분류란, 어떤 사진들을 보았을 때, 그 사진들이 어떤 물체 또는 동물 등인지 분류하게 하는 것입니다.. 위의 사진을 예로 들면, 귀여운 고양이 사진을 보았을 때, 컴퓨터가 '이건 고양이야!' 하고 말하게 만드는 것이죠.

 

사람들은 이런 작업들을 비교적 쉽게 수행해 냅니다. 하지만, 컴퓨터는 그렇지 못하죠. 왜 그럴까요?

 

 

일단, 컴퓨터는 우리가 사진을 보는것 처럼 사진을 보지 않습니다. 컴퓨터는 어떤 사진을 볼 때, 우리처럼 '흠, 이건 고양이 사진이군' 하며 보는 것이 아닙니다. RGB 값을 나타내는 수없이 많은 숫자들의 나열로 보는 것이죠. 그렇기 때문에, 컴퓨터는 이런 사진을 한눈에 알아볼 방법이 없습니다. 주변의 모든 물체가 죄다 숫자들로 보인다고 생각하면 이해가 쉬울 것 같습니다. 지금 여러분들이 보고 있는 화면도 그냥 숫자만 있다고 생각하면.. 이게 컴퓨터 화면인지 인식하기 힘들겠죠?

 

 

그 외에도 다른 문제점들이 있습니다.

일단, 사진을 찍는 각도에 따라 숫자들이 죄다 바뀐다는 것이죠. 아무리 똑같은 고양이를 찍어도, 아무리 똑같은 사물을 찍어도 카메라가 살짝 오른쪽으로 간다면, 화면 자체는 비슷할지라도 모든 숫자들 또한 살짝 오른쪽으로 가버리니.. 어떤 숫자들이 어떤 부분에 있다는 단순한 점만으로는 찾기에 힘들어 보입니다.

 

 

 

아무리 같은 고양이라도 조명이 다르면 숫자들은 모두 달라지고, 자세가 달라도 모두 달라지도, 어디에 숨어있으면 또 달라지고, 배경이랑 비슷하면 숫자들이 다들 비슷할것이고(색상값이므로), 고양이가 많으면 또 인식하기에 힘들 것입니다.

 

 

그렇기 때문에, 단순한 코딩으로는 이 문제를 해결할 수 없다는 것을 알게 됩니다.

가령, '1+1'을 하는 프로그램을 코딩해라! 하는 것은 단순한 코딩으로 바로 답을 알 수 있겠지만, 위처럼 숫자 나열들을 주며 '자, 이제 이 숫자들이 의미하는 바가 무엇일까..? 고양이?? 개??? 한번 맞춰봐!!'같은 사이코 같은 문제는 단순한 코딩으로 맞출 수 없습니다.

 

 

 

그래서 사람들은 '데이터에 기반한 접근법'을 생각하게 됩니다. 단순히 어떤 사진을 보고 인식하는 코드를 짜는 것이 아니라

무수히 많은 사진들과, 그 사진들이 무엇인지 알려주는 정답을 먼저 구하는 겁니다.

그 후, 머신러닝이라는 방식으로 

그렇게 만들어진 분류기로, 사진들을 분류하면.. 사진이 잘 분류되지 않을까?? 하는 생각인거죠.

아이에게 교육하는 방식과 비슷하다고 생각하시면 됩니다. 우리가 아이들에게 어떻게 생긴 것들이 고양이인지 가르칠 때, 아무 고양이 사진 하나만 가지고 '이게 고양이야!'라고 하진 않죠? 

다양한 고양이 사진들을 보여주면서 '이것도 고양이, 저것도 고양이, 그리고 요것도 고양이란다!' 하는 방식을 통해 아이들을 가르친다면, 아이들은 훨씬 더 빠르게 어떤 것이 고양이인지 알아낼 것입니다. 바로 이런 것과 동일한 접근법이라고 보시면 될 것 같습니다.

 

 

그래서, 처음 생각해낸 분류법이 바로 Nearest Neighbor, 즉 '가장 가까운 이웃 찾기' 방법입니다.

이름이 굉장히 직관적인데요, 말 그대로 사진들을 모두 외워서, 다른 사진들을 봤을 때 지금까지 알고 있던 사진들과 비교하며, 가장 비슷하게 생긴 것을 찾아내는 겁니다.

바로, 아이들에게 '이렇게 생긴 것은 대강 고양이일 거고, 저렇게 생긴 거는 대강 배일 거야. 알겠지??' 하며 가르치는 것입니다.

 

 

그리고, 이렇게 가르칠 때 데이터셋은 CIFAR-10이라는 데이터셋을 사용할 겁니다.

데이터셋이란, 앞면에는 사진이 그려져 있고, 뒷면에는 그것이 어떤 사진인지 적혀있는 카드 뭉치라고 생각하시면 될 것 같습니다.

그리고, CIFAR-10이라는 카드 뭉치는 총 10가지 종류의 물체/동물들을 모아놓은 사진 뭉치인데, 비행기, 차, 새, 고양이 등을 나눠놓은 카드 뭉치입니다.

그리고, 우리는 이 카드 뭉치를 '컴퓨터'라는 아이에게 가져다주어 위의 'Nearest Neighbor'라는 교육법으로 아이를 가르칠 겁니다.

 

 

그런데, 사진이 뭐가 비슷한지 어떻게 아냐고요? 바로 이런 방식으로 알아낼 수 있습니다.

아까 전에, 컴퓨터는 사진을 볼 때 숫자들의 나열로 본다고 했습니다. 그렇다면, 그 숫자들 사이의 크기가 비슷하다면, 사진도 비슷하겠죠?

이런 생각을 바탕으로, 사진의 숫자들을 모두 뺄 겁니다. 그리고 거기에다가 절댓값을 씌워 주면? 두 숫자 사이의 거리, 즉 두 사진 사이의 다른 정도가 나옵니다. 이것은 L1 distance(L1 거리)라고 부르는데, 이 짓을 사진의 모든 픽셀에 똑같이 해준 후 나온 숫자들을 모두 더해주면, 두 사진 사이의 다른 정도가 하나의 숫자로 나올 수 있습니다.

 

 

 

그러면, 이제 한번 아이에게 분류하는 법을 가르쳐 볼까요? 일반적으로, 아이를 가르칠 때에는 두 가지 방법으로 나누어 줍니다.

첫 번째는, training(훈련) 부분입니다. 그리고 아까도 말했듯이, Nearest Neighbor에게 train은 그저 사진들을 외우는 것일 뿐입니다.

그리고, predict(예측) 부분입니다. 아이에게 지금까지 봤던 사진이 아닌 다른 사진을 주면서, '이게 뭘까~?' 하는 것이죠.

Nearest Neighbor는, predict작업은 사진을 보고, 지금까지 외웠던 사진들의 숫자들로 빼 주는 것입니다.

'개 사진들이랑 뺐을 때는.. 평균 100만큼 차이나고, 고양이 사진들이랑 뺐을 때는, 평균 200만큼 차이가 나네! 그러면 아마 이 사진은 고양이보다는 개에 더 가까울 거야!'

라고 생각하는 과정이, 바로 Nearest Neighbor의 predict 과정입니다.

 

 

그런데 문제가 있습니다. 이것을 외우는 데는 시간이 엄청나게 빨리 걸립니다. 아, 사람이 외울 때는 당연히 오래 걸리겠지만, 우리의 아이는 엄청난 기억력을 가진 컴퓨터라는 아이니깐, 순식간에 외울 수 있습니다.

하지만, 이것을 예측하는 것은 꽤나 오랜 시간이 걸립니다. 아이가 지금까지 50000개의 사진을 외웠다면, 그 모든 사진들을 죄다 빼주면서 계산을 해야 하니까요. 단순히 연산이 50000번인 것도 아니고, 50000개의 사진의 모든 숫자들을 다 빼서 더하고, 평균을 내고... 을 해야 하니까요.

근데 이게 왜 문제냐고요? 만약 우리가 개나 고양이 사진을 찍어서, 이게 개인지 고양이인지 구분하는 프로그램을 만들었는데, 사진 하나 찍고 이게 개인지 고양이인지 구분하려고 보니깐 1시간이 걸린다고 하네요! 그러면, 여러분들이라면 과연 이 프로그램을 사용할까요? 그렇지 않겠죠..? 웬만한 변태가 아니라면 말입니다.

그렇기 때문에, 훈련에 걸리는 시간은 오래 걸려도 별로 상관이 없습니다. 이 훈련은 한 번만 하면 다음부터 사용할 때에는 그 정도의 시간은 걸리지 않으니까요. 하지만, 예측 단계에서 이렇게 느리면.. 그것은 문제가 생긴다는 것이죠.

 

 

아무튼, 다시 본론으로 돌아가 봅시다. 이것은 Nearest Neighbor 방식으로 교육받은 아이가 그린 그림입니다. 색깔은 개나 고양이 같은 사진의 종류를 말하고, 점들은 사진을 말합니다. 즉, '형아, 저기 점 주변에 찍히는 다른 점들은 내가 배경으로 색칠해놓은 색의 사진일 거야. 그렇지?'라고 말하는 것이죠. 근데, 과연 그럴까요?

가운데 노란색 부분을 봅시다. 직관적으로 봤을 때, 뭔가 저 노란색 주변에 있는 점들은 초록색이어야 할 것 같은 느낌, 안 드시나요? 아무래도 초록색으로 둘러싸여 있는데, 저 점 하나 때문에 그 부분만 노란색 부분이 된다는 것은, 약간 무리가 있어 보입니다.

 

 

그래서 고안된 방법이 K-Nearest Neighbor 방법입니다. 가장 가까운 사진을 찾는 건 그대로이지만, 주변 K개를 봤을 때 가장 비슷한 것이 바로 정답일 거라는 생각인 것이죠.

위의 사진에서 그것이 가장 잘 보입니다. K=1이었을 때는, Nearest Neighbor와 정말히 똑같은 그림이 그려집니다. 하지만, K=3, K=5 일때는 초록색 부분의 가운데 배경 색이 초록색으로 바뀌었고, 위에 삐죽 튀어나왔던 부분도 파란색과 보라색으로 바뀐 것을 볼 수 있습니다.

 

 

그래서.. CIFAR-10을 K-Nearest Neighbor로 훈련시킨 결과입니다. 왼쪽은 정답 종류를 의미하는 사진, 오른쪽은 훈련된 컴퓨터가 고른 예측입니다.

멀리서 보면 꽤나 잘 맞춘 것으로 볼 수도 있습니다. 실제로 사진이 다 비슷하게 생겼기 때문이죠. 특히, 개구리 (위에서 4번째) 사진들은 죄다 배경의 흰색 계열로써, 사진 전체적으로 굉장히 비슷하죠.

하지만, 그것만 비슷할 뿐, 자세히 보면 완전 틀린 답들을 골라내고 있습니다. 사진이 비슷하긴 하지만, 그렇다고 그것이 정답이라는 보장은 없으니깐 말이죠. 

 

 

잘 안 나온 것 같으니, 다른 방법을 한번 써봅시다. 아까 전에 L1 distance라고 이야기했던 것 기억나시나요? 그것 대신, L2 distance라는 친구를 사용해서 두 사진 사이의 차이를 알아내 볼 겁니다. 정확한 수식은 잘 모르셔도 됩니다. 그저, 거리를 재는 다른 방법이라는 생각과, 대충 거리를 그림으로 나타내면 위처럼 L1은 다이아몬드, L2는 원 형태가 나온다는 정도만 아시면 될 것 같습니다.

 

 

.. 그리고 이것이, K=1, 그리고 L1 또는 L2를 사용해서 훈련했을 때 컴퓨터가 그려낸 사진입니다. 약간 형태가 다른 게 느껴지실 겁니다.

 

 

""쉬는 시간?""

이 사진들을 한번 만들어 보며 가벼운 실습을 하고 싶다면,

http://vision.stanford.edu/teaching/cs231n-demos/knn/

이 곳에 들어가셔서, 직접 K값과 거리(L1, L2)를 바꾸면서, 종류 개수도 바꾸면서.. 어떤 그림이 그려지는지 확인해보시는 것도 좋을 것 같습니다.

 

 

 

위에서 잠깐 해보았으면, '그럼, K랑 distance는 어떤 게 가장 좋은 거야??'라는 생각이 드셨을지 모르겠습니다. K와 distance는 우리가 직접 정해야 하는 값, 즉 Hyperparameter(초모수) 니까요. 이에 대한 정답은 간단합니다. 

상황마다 다릅니다! 그리고, 직접 하나하나 넣어 보면서, 어떤 것이 가장 정답률이 높은 지를 알아내는 것이 중요하죠.

hyperparameter이라는 말은 앞으로도 굉장히 자주 나올 말이니까, 숙지해 두는 것이 좋을 것 같습니다.

 

 

 

그렇다면, 이 hyperparameter들을 제대로 맞추는 방법을 알아봅시다.

가장 먼저 드는 생각은, '아니, 그냥 내 데이터셋에다가 집어넣어서, 제일 잘 작동되는 거 고르면 되는거 아니야??' 일 것 같습니다.

하지만, 이렇게 해버리면 문제가 생겨버립니다. 아까 위의 사진을 다시 생각해 봅시다. 다른 사진이 들어오지 않을 때에, K=1이 가장 우리의 데이터셋에서 좋은 효과를 내고 있다고 볼 수 있었을 것입니다. 하지만, 다른 사진이 들어갈 때에는, 약간 문제가 생겼죠? 즉, 우리가 진정으로 원하는 것은, 우리가 가지고 있는 데이터셋에서 작동이 잘 되는 것이 아니라, 그냥 진짜로 찍은 사진에 작동이 잘 되는 것을 찾고 싶은 것이죠.

 

그러면, 다음 생각은 '아니.. 그럼 train과 test로 나눈 후에, test에 잘 되는 놈으로 하지!' 일 것입니다. 하지만, 그것마저도 별로 좋은 선택은 아닙니다. train에서 훈련된 것을 test에서 확인을 한 후에, test값에 적절한 k 또는 distance 값을 바꾸어 주더라도, 결국엔 test 셋에서만 잘 되는 것일 수도 있고, 아예 새로운 데이터가 들어왔을 때는 또 어떻게 반응할지 모르기 때문이죠.

 

그럼, 다른 좋은 생각 없을까요? 한번, 데이터셋을 세 부분으로 나누어 봅시다. train, validation, test로 말이죠.

train 데이터셋에서 열심히 훈련된 값을, validation 데이터셋으로 확인하며 hyperparameter를 바꾸어 주고, test는 마지막 딱 한 번만 만져 보는 것이죠. 이렇게 되면, test 셋은 마치 우리의 데이터셋 안에 없는, 세상에서 아예 새로운 데이터가 되는 셈이고, 그렇다면 우리가 원하던 '진짜 새로운 데이터에서의 테스트'를 해 볼 수 있게 되는 것이죠.

 

 

그리고, Cross-Validation이라는 방식도 존재합니다. 일단 큰 거 하나와, 작은 거 하나 총두 개로 먼저 나눕니다. 그 후, 작은 거는 test 셋으로 설정하고, 큰 거 하나를 놓고 다시 여러 개로 나누어서, 첫 번째 부분만 val로 놓고, 나머지를 train으로 놓은 뒤에 hyperparameter를 설정하고, 다시 두 번째 부분만 val로 놓고, 나머지를 모두 train으로 놓은 뒤에 hyperparameter를 설정하고.. 

이런 방식으로 해서, 그 결과의 평균값으로 hyperparameter를 설정하는 것입니다. 혹시 validation을 나눈 것이 이상하게 나누어져 제대로 되지 않을 수도 있으니, 이런 식으로 hyperparameter를 정해 보는 것이죠. 이렇게 한다면, 더욱더 확실하게 hyperparameter의 값을 구할 수 있을 것입니다.

하지만, 딥러닝에서 자주 사용되는 기술은 아닙니다. 이후에 나올 training 들은, 연산을 굉장히 많이 해야 하므로, 저렇게 하나하나 다 해보는 데에는 너무 오래 걸리고, 힘이 들기 때문이죠.

 

 

 

그렇다면, 이런 식으로 설정된 hyperparameter들을 한번 비교해 봅시다.

그래프의 가로축은 k의 값이고, 세로축은 test에서의 정확도 값입니다.

그래프를 보시면 아시겠지만, k가 대충 7 정도일 때 가장 높은 정확도를 보이는 것을 확인할 수 있습니다. 즉, k를 7 정도로 사용하는 것이 가장 효율적일 것이라는 거죠.

주의하셔야 할 점은, 이렇게 나온 hyperparameter들은 문제 및 데이터셋마다 모두 다르다는 것입니다. 같은 방식으로 훈련을 하더라도, 

'이 사진을 보고 개인지 고양이인지 구별하시오'라는 문제에 최적화된 hyperparameter의 값과

'이 사진을 보고 웃는 얼굴인지 우는 얼굴인지 구별하시오'라는 문제에 최적화된 hyperparameter의 값이 다르다는 것이죠.

즉, 이러한 과정은 모든 데이터셋과 문제들마다 한 번씩 거쳐가며 설정하는 것이 좋다는 것입니다.

 

 

하지만, 지금까지 열심히 공부했던, Nearest Neighbor이라는 방식은 절대로 사용되지 않습니다.

우선, 초반에도 언급한 predict 과정이 굉장히 오래 걸린다는 것과, 사진 사이의 distance는 사실 그렇게 의미 있는 값이 아니라는 것 때문이죠.

예를 들어, 위의 Boxed, Shifted, Tinted 사진은 왼쪽의 원본 사진과 같은 L2값을 가지고 있다고 합니다. 즉, 이것으로 학습을 한다면 얼굴에 박스가 쳐져있던, 색이 바래졌든 간에 같은 사진으로 생각해 버린다는 것이죠.

또한, 저---위에 Lecture 2 - 30에서 보았을 때에도 잠깐 언급했지만, 사진이 비슷하게 생겼다고 해서 비슷한 물체인 것은 아닙니다.

갈색 조명으로 갈색 느낌을 준 축구공과, 갈색 농구공은 비슷하게 생겼지만 다른 공인 것처럼 말이죠.

 

더 큰 문제로는, 차원이 넓어질수록 필요한 데이터의 개수가 기하급수적으로 많아집니다. 1차원일 때에는 4개만 필요하면 데이터의 개수가, 2차원이 되면 4*4=16개가 필요해지고, 3차원이 되면 4*4*4=64개가 필요해지니까요.

'오잉? 생각보다 적네?'라고 생각하실지 모르겠지만, 실제로는 이것보다 훨씬 큰, 100000 같은 수가 1차원에 필요하다 하면, 2차원은 10000*10000,, 3차원은 10000*10000*10000개가 필요하니... 정말 정말 많아지는 것을 볼 수 있습니다.

 

 

그럼.. 우리 지금까지 허튼짓 한 거냐고요?? 어차피 안 쓸 거 왜 알려줬냐고요?

그 질문을 하기 전에, 일단 이것을 배우는 과정을 통해 배운 것들을 확인해봅시다.

우선, 이미지 분류에는 트레이닝 이미지와 그에 맞는 카테고리들이 필요하고, test 셋에서 그것을 predict 해야 한다는 것을 알았습니다.

K-nearest는 그 카테고리, 라벨들을 가장 가까운, 유사한 training 셋의 사진으로 맞춘다는 것도 알았고,

 distance(L1, L2)와 K값은 hyperparameter(초모수)라는 것이라는 것도 알았고,

hyperparameter를 고를 때에는 validation 셋에서 골라야 하고, test 셋에서 맨 마지막 딱 한번 돌려봐야 한다는 것도 알았습니다!

 

nearest neighbor라는 가장 단순한 알고리즘으로, 우리는 앞으로 배워나갈 인공지능에 대한 기본적인 지식들을 깨달을 수 있었습니다.

그러면, 위 질문에 충분히 대답이 되었겠죠?

그럼 다음으로 넘어가서, 지금 배운 것을 써먹어봅시다!

 

.. 바로 Linear Classification(선형 분류)이라는 친구를 말이죠.

 

 

 

그럼, 일단 CIFAR-10 데이터셋을 다시 가져와 봅시다! 기억나시죠? 10개 카테고리로 이루어져 있고, 그에 맞는 사진들이 있던 바로 그거요!

 

 

자, 이미지가 32*32*3 (가로*세로*RGB) 개의 숫자들로 이루어져 있다고 해봅시다. (CIFAR10의 이미지 사이즈입니다.)

그러면, 그 사진의 숫자들과, weight(가중치)라는 변수를 어떤 함수에 같이 넣으면, 10개 카테고리에 대한 점수가 나오게 하면 어떨까요?

이것이 바로 linear classification의, Parametric (매개 변수적) 접근 방식입니다. 
이렇게 해두면, 앞의 Nearest Neighbor 과는 다르게, W값만 가지고 predict가 가능해지니, 훨씬 더 효율적이겠죠?

 

그렇다면 이것이 어떻게 작동할까요? 일단, 함수를 가장 기본적인 형태로, f(x, W) = W*x + b라고 하고 시작합시다. 저 함수를 지나가면, 10개의 카테고리 각각에 대한 점수가 나올 수 있게 말이죠.

이미지를 3072*1의 크기로 평평하게 펴봅시다. 그리고 그것을 x로 놓읍시다.

그 뒤, 어떤 수들을 3072*10(카테고리 개수)의 크기로 만들어봅시다. 참고로, 이 수가 무엇이 되어야 하는지는 한--참 뒤에 설명합니다. 일단은 그냥 랜덤 한 3072*10의 크기의 배열이 생성된다고 생각해 주세요. 그리고, 이것을 W라고 합시다.

그러면, W의 첫 번째 줄과 x를 곱하고, W의 두 번째 줄과 x를 곱하고,... , W의 10번째 줄과 x를 곱해서 10개의 수를 구할 수 있습니다.

그러면, 이제 f(x, W)=W*x+b라는 식중 W*x까지는 해낸 겁니다.

 

마지막으로, b를 더해주어야 합니다. 이때, b의 개수는 어때야 할까요?

네, 당연하게도 카테고리의 개수인, 10개 여야겠죠? 우리는 10가지 카테고리에 각각에 대한 점수를 구하고 싶으니깐, b를 1*10의 크기로 만들어줍시다. (참고 : 이 값은 bias, 편향 값이라고 불립니다.)

근데, 이거 왜 더하냐고요? 만약, 데이터셋에 고양이만 엄청 많고 개는 엄청 적다? 그러면 당연히 어떤 사진을 봤을 때 그게 고양이일 확률이 더 크겠죠? 그러므로 bias 값을 고양이에 더 크게 부과해 주어, '고양이' 점수에 보너스를 주는 것이죠.

 

 

 

 

 

위 사진은 지금까지 말했던 공식을 사진으로 나타낸 것입니다. 대충 어떻게 계산되는지 알아봅시다.

일단, 이미지는 2*2 크기의, 4개의 픽셀로 단순화된 이미지라고 가정하고, 세 가지 카테고리의 점수만 알아본다고 가정합니다.

그러면, 아까 전의 방법처럼 해볼까요?

일단, 사진의 크기는 2*2이므로, 1*4 크기로 평평하게 만들어 줍니다.

그리고, (사진의 크기) * (카테고리의 개수)인, 4*3 크기의 W값을 만들어 주겠습니다.

그 후, x와 W를 곱해주는데, 이렇게 곱하는 것을 스칼라 곱(Scalar product) 또는 점곱(Dot product)라고 합니다.

(참고 : https://ko.wikipedia.org/wiki/%EC%8A%A4%EC%B9%BC%EB%9D%BC%EA%B3%B1)

 

아, 물론 자세하게 수학적으로 갈 필요는 별로 없습니다. 곱하는 방식은 단순합니다.

일단, 고양이 점수를 먼저 계산해 봅시다.

첫 번째 줄, 즉 고양이를 나타내는 W값인 0.2,-0.5,0.1,2.0을 각각 x값인 56,231,24,2에 곱해줍니다.

계산기를 사용하면, 각각 11.2,-115.5,2.4,4.0이고, 이를 합해주면 -97.9가 되겠습니다.

그 후, 고양이의 bias값인 1을 더하면, 결국 -96.8이 나오고, 이것이 바로 위 함수, f(x, W)에서의 고양이의 점수라고 할 수 있습니다.

나머지 개, 배 점수는 직접 한번 구하여 맞는지 확인해 보는 것도 나쁘지 않을 것 같습니다.

 

 

 

자! 이제 그럼 f(x, W)=W*x+b의 모든 과정을 달려왔습니다.

아니 근데, 이거 왜 했냐고요?? 이게 그래서 뭐 하는 거냐고요??

 

자, 아래 사진들을 봅시다. 이 아래 사진들은, CIFAR-10 데이터셋으로 훈련된 Weight값을 사진으로 표현한 것입니다. 보시면 아시겠지만, 뭔가 카테고리에 가까운 방향으로 사진이 나온 것을 볼 수 있습니다. 특히, car 같은 경우는 정말 자동차처럼 생겼습니다.

그런데, horse 같은 경우를 보면 뭔가 많이 이상합니다. 머리가 양쪽에 두 개 달려있는 말처럼 보이시지 않나요? 이것이 바로 Linear Classifier의 문제점 중 하나인데, 왼쪽을 보고 있는 말과, 오른쪽을 보고 있는 말 같은 다양한 사진들이 존재하는데, 이것을 하나로 묶어서, 그냥 그러한 모든 자잘한 점들을 평균을 내버리니깐 말이죠.

물론, Linear Classifier는 카테고리마다 하나의 결과밖에 내지 못한다는 본연의 한계가 있으니, 그것치곤 상당히 잘 만들어졌다고 볼 수 있겠습니다.

 

 

 

그리고 이것을 조금 더 알기 쉽게 그래프에 놓고 보면, 일차함수 직선들이 가득한 좌표평면을 확인하실 수 있을 겁니다. 이 선 옆은 비행기, 이 선 아래는 사슴, 이 선 오른쪽은 자동차.. 이런 식으로 나누어진다고 할 수 있겠죠.

실제로, W는 기울기 값을, b는 y절편을 나타내므로, 무수히 많은 사진들이 있는 점들 중에서 최대한 많은 정답을 직선 한쪽으로 몰아넣을 수 있는 W, b값을 정하는 것으로 Linear Classifier의 훈련과정이 작동합니다.

 

그러나, 이런 문제점들도 발생할 수 있습니다.

일차함수 직선으로는 제대로 나눌 수 없는 위의 세 가지 경우들이 가장 대표적인 예시입니다.

만약 사진들이 저런 식으로 분포가 되어 있으면, 어떻게 해도 직선으로는 완벽하게 나눌 수 없겠죠?

 

 

그렇다면, 위의 예시를 한번 봅시다! 우리의 W와 b값이 잘 만들어져서, 사진들에 잘 작동하고 있는지 확인해 볼까요?

일단 고양이 같은 경우에는, 개에 점수가 가장 크고, 고양이는 중위권 정도를 하는 것을 보아, 정답을 맞히지는 못했겠지만 어느 정도는 했다고 볼 수 있겠습니다.

자동차는, 놀랍게도 정답을 맞혔습니다! 아니 잠깐만, 이게 왜 놀랍냐고요?

왜냐하면, 아직은 W값을 그냥 랜덤 값으로 둔 것이거든요!

그러니깐, 맞춘 게 용하다고 볼 수 있다는 것이죠.

한번 오른쪽 개구리를 보시면 더욱 와 닿을 텐데, 실제 정답이 뒤에서 2등인 점수가 나올 정도로.. 좋지 못합니다.

그렇다면, W값은 어떻게 설정해야 할까요? 어떻게 바꿔나가야 할까요?

 

 

.... 는 다음 시간에 하도록 하겠습니다!

다음 시간에는, 좋은 W값을 어떻게 찾아야 하는가에 대한 이야기로 시작해보도록 하죠.

 

+ Recent posts