본문 바로가기

수업 정리(개인용)/cs231n

CS231n 2017 Lecture 4 : Introduction to Neural Networks

지난 시간에 배운 gradient descent이다. Analytic gradient를 구할 수 있어야 이 알고리즘을 사용할 수 있다. 그렇다면 복잡한 함수를 어떻게 미분해야 할까?

 

ImageNet에서 활약한 AlexNet이다. 8개의 layer로 이루어진 이런 복잡한 신경망을 각 variable에 대해 미분하는 것은 쉽지 않다.

 

복잡한 함수를 미분하기 위해 함수를 computational graph로 나타낸 후 backpropagation을 한다. 그래프의 가장 오른쪽에서부터 chain rule을 recursive하게 이용해 한 단계씩 미분하는 것이다. 가령 df/dx를 바로 구하는 것은 힘들지만 df/dq를 알고 있다면 dq/dx만 구해서 곱하면 df/dx를 알 수 있다.

 

어떤 한 노드의 input과 output만을 고려해서 구한 gradient를 local gradient라고 하며, 이것을 오른쪽에서 흘러오는 gradient와 곱해주면 전체 함수에 대한 미분 값을 알 수 있다.

 

계속해서 backpropagation의 예시이다. 복잡해 보이는 수식도 각 노드 별로 local gradient를 구해서 곱하기만 하면 된다. + gate는 local gradient가 1이기 때문에 gradient가 그대로 나오기 때문에 gradient distributer라고 할 수 있다. * gate는 gradient switcher라고 할 수 있다. * 연산에서는 서로가 서로의 계수이기 때문에 w0의 local gradient는 x0이고 x0의 local gradient가 w0인 것을 알 수 있다.

 

우리가 선택하기에 따라서 저렇게 sigmoid gate라는 한 묶음으로 노드를 구성해도 된다. 우리가 local gradient를 구할 수만 있다면 차이가 없을 것이다.

 

Serena가 생각하기에 computational graph는 정말 유용한 도구이다. 아무리 어렵고 복잡해 보이는 함수가 나타나도 저렇게 간단한 조각들로 이루어진 computational graph를 그린 후 각각의 local gradient를 구하면 미분할 수 있기 때문이다.

 

Max gate의 역전파이다. z와 w의 경우를 보면 큰 값인 z만 max를 통과하고 w는 사라지기 때문에 전체 함수에 끼치는 영향이 없으므로 local gradient가 0이다. 즉, 가장 큰 값의 local gradient만 1이고 나머지는 0인 것이다. 나중에 relu gate를 구현할 때 반드시 필요한 개념이다.

 

하나의 값이 여러 변수에게 더해지는 경우. Local gradient를 영향을 끼친 각 변수에 대해 구해야 한다. 그 후 chain rule로 구한 gradient들을 sum 하면 된다.

 

본격적으로 어려워지는 부분이다. Input이 vector라면, gradient가 스칼라가 아닌 Jacobian matrix의 형태로 구해진다. Jacobian matrix는 예를 들면 벡터z의 각 원소들을 벡터 x의 각 원소들로 미분한 값을 포함하고 있다.

 

Input size가 4096이라면 스칼라를 미분해도 4096개의 gradient가 나오게 된다. 그런데 심지어 스칼라가 아닌 사이즈 4096인 벡터를 미분한다면 size가 4096*4096이 된다.

 

그러나, 위의 elementwise max 연산에서 input vector의 element는 output vector의 모든 element에 영향을 끼치는게 아니라 corresponding한 하나의 element에만 영향을 끼친다. 따라서 Jacobian matrix가 diagonal 하게 되고 그러므로 전체를 구하지 않아도 gradient를 계산하는 데에 문제가 없다.

 

간단한 미분을 통해 df/dq를 구할 수 있다. 어떤 vector의 gradient의 shape은 항상 vector의 shape과 같아야 함을 기억하자.

 

이번엔 W의 gradient를 구할 것이다. dq/dW를 구할 때 잘 보면 W의 1행 원소들은 q의 1행 원소에만 영향을 끼치고, W의 2행 원소들은 q의 2행 원소에만 영향을 끼치는 것을 알 수 있다. 즉, W의 i행은 q의 i행이 아닌 원소에 대한 local gradient가 0이다. 위 슬라이드의 수식에서 1k=i는 이런 의미로, k=i이면 1이고 아니면 0이라는 뜻이다. 그리고 다시 W가 x와 곱해지는 과정을 보면 W의 j번째 열 원소들은 x의 j번째 행 원소와 곱해진다. 이 사실을 이용하면 W의 local gradient를 구할 수 있다.

 

그리고 나서 df/dW를 구하는 수식을 보면, chain rule을 이용해서 구하는데 여기서 시그마가 붙은 이유는 위에서 말했듯이 하나의 변수가 여러 변수에 영향을 끼치고 있기 때문이다. W 원소 하나가 q의 각 원소에 영향을 끼치므로 각각에 대해 chain rule을 적용한 후 sum 하는 것이다(물론 여기서는 q의 i행을 제외한 원소에 W가 끼치는 영향은 0이다).

 

즉, W i,j의 gradient는 q의 gradient의 i번째 원소와 x의 j번째 원소의 곱인데, 이것을 전체 행렬에 대해 표현하자면 q의 gradient와 x의 outer product이다.

 

내 생각에는 슬라이드에서 W의 gradient에 오류가 있는 것 같다. (1,2)와 (2,1)이 바뀐 게 맞는 듯 하다.

 

이번엔 x의 gradient를 구할 것이다. 역시 W와 x의 행렬 곱 과정을 생각해 보면 x의 i번째 원소는 W의 i열 원소와 곱해진다. W의 k,i번째와 곱해져서 q의 k번째가 된다. 이후의 수식은 W의 경우와 비슷하다.

 

Gate 하나 하나를 class로 구현하여 ComputationalGraph 객체에 담고 iteration 돌리면서 forward와 backward 함수를 호출할 것이다. 

각 gate는 이런 식으로 구현할 것이다. Input을 저장해서 backward 시에 gradient 계산할 때 사용할 것이다.

 

Deep Learning Framework 들도 이런식으로 만들어졌다.

 

1. Neural net는 엄청 크고 복잡하기 때문에 모든 파라미터에 대해 gradient formula를 일일이 구하는 것은 실용적이지 않음.

2. 역전파 = chain rule을 recursive하게 적용하여 computational graph를 따라 모든 gradient를 구하는 방식.

3. 실제로 graph structure를 유지하며 구현할 것이고, node들은 forward()와 backward() 함수를 가질 것임.

4. forward 함수는 결과를 계산하고 gradient 계산에 필요한 변수들을 저장하는 기능을 할 것임.

5. backward 함수는 chain rule을 적용해서 loss function에 대한 input의 gradient를 구하는 기능을 할 것임.

 

Neural network를 brain stuff 없이 함수의 관점으로 바라보자면, 단순히 한 layer의 output을 다음 layer의 input으로 연결하는 방식이다. 지난 강의에서 linear classification을 다루면서 배운 것은 어떤 class에 대한 W가 그 class를 닮은 템플릿의 모양을 띈다는 것이다. 그러면서 제기된 문제점은 car에 해당하는 W가 car의 평균적인 모습을 띄고 있지만, 템플릿이 하나인 만큼 다양한 모양의 car에 대해 classification을 잘 못한다는 것이다. Horse에 해당하는 W를 보면 오른쪽을 보고 있는 말과 왼쪽을 보고 있는 말이 합쳐져 있는 모양이다.

 

위와 같이 중간에 hidden layer를 끼워 넣으면, 템플릿이 하나여서 생겼던 문제점을 해결할 수 있다. 가령 h에서 다양한 모양의 car, 이를테면 red car나 blue car 등에 대해 score를 저장하고, 그것들을 마지막 layer에서 모두 score_car에 연결하면 된다.

 

또 한가지 주목할 점은 이전 layer의 output을 다음 layer의 input에 넣기 전에 비선형 함수를 통과시킨다는 것이다. 위 슬라이드에서는 max(0,x)가 쓰였는데, 이것은 자주 쓰이는 activation function 중 하나인 relu이다. 비선형 함수를 통과시키지 않으면 선형 함수로 표현이 가능하기 때문에 layer를 쌓는 의미가 없다. 

 

이번엔 neural network와 뉴런의 구조적 공통점을 알아볼 것이다. 뉴런을 보면 dendrite들을 타고 온 신호가 cell body에서 합쳐져서 axon으로 나가는 것이, 우리가 봐온 computational graph와 유사하다. Sigmoid function은 뉴런의 fire rate를 닮은 면이 있다. 뇌과학자들 말로는 뉴런의 방식과 가장 닮은 activation function이 relu라고 한다. 

 

그러나 이런 관점에서 neural network를 바라보는 것은 신중해야 한다. Neural network와 실제 뉴런은 아주 high level에서 유사하다는 것이지 깊게 들어가면 뉴런은 더 복잡하다는 내용이다.

 

다양한 종류의 비선형 함수들.

 

뉴럴넷을 부르는 명칭들이다.

 

다음 강의에서는 CNN을 배울 것이다.