30. Sigmoid 보다 ReLU가 더 좋아 (lec 10-1)



신앙처럼 여겨왔던 sigmoid를 능가하는 존재가 나타났다. 여기서는 Deep Learning의 성능을 향상시키는 다양한 방법들에 대해 알려준다.


sigmoid는 logistic classification에서 어디에 속하는지 분류를 하기 위해 사용했다. 일정 값을 넘어야 성공내지는 참(True)이 될 수 있기 때문에 Activation function이라고도 불렀다.


업계 최고라고 부르는 9단이 나타났다. 9개의 hidden layer에 맞게 W와 b 또한 그 만큼의 갯수로 늘어났다. 그러나, 코드가 어렵지는 않다. 지루한 반복같은 느낌이다.


TensorBoard로 결과를 보기 위한 코드도 추가했다. (그림 오른쪽 코드)


각각의 layer가 그래프의 노드가 되어 표시되었다. 보기 좋다.


그런데, 결과는 좋지 않다. hidden layer를 무려 9개나 두었음에도 불구하고 정확도는 겨우 50%!


결과가 좋지 않은 상황을 그래프로 보니, 어떻게 된건지 한 눈에 들어온다. cost는 처음에 뚝 떨어진 이후로 변화가 없지만, accuracy는 뒤쪽에 가서 이상하게 변하고 있다.

layer가 많아지면 overfitting 문제가 발생한다고 했던 것을 기억하는가? 너무 잘 하려다 보니 지나쳐 버린 것이다. 그러나, 여기서는 더 중요한 문제가 있다.


backpropagation을 그린 그림이다. 지금처럼 layer가 많을 때는 미분 결과를 최초 layer까지 전달하는 것이 어렵다. 아니 불가능하다! 계산이 쉬운 것이지 올바른 결과 전달까지 쉬운 것은 아니다. 보통 2~3개의 layer에 대해서는 잘 동작하지만, 그 이상이 되면 문제가 발생한다.


backpropagation에서 결과를 전달할 때 sigmoid를 사용한다. 그런데, sigmoid는 전달된 값을 0과 1 사이로 심하게 변형을 한다. 일정 비율로 줄어들기 때문에 왜곡은 아니지만, 값이 현저하게 작아지는 현상이 벌어진다. 3개의 layer를 거치면서 계속해서 1/10로 줄어들었다면, 현재 사용 중인 값은 처음 값의 1/1000이 된다. 이렇게 작아진 값을 갖고 원래의 영향력을 그대로 복원한다는 것은 불가능하다.


이 문제를 vanishing gradient라고 부르고 1986년부터 2006년까지 아무도 해결하지 못했다. Neural Network에 있어 두 번째로 찾아온 위기였다. backpropagation에서 전달하는 값은 똑같아 보이지만, layer를 지날 때마다 최초 값보다 현저하게 작아지기 때문에 값을 전달해도 의미를 가질 수가 없었다.


CIFAR에서 지원했던 hinton 교수가 2006년에 방법을 찾아냈다. Neural Network가 발전하지 못했던 것들에 대해 4가지로 요약을 했는데, 여기서는 마지막이 중요하다.

non-linearity에 대해 잘못된 방법을 사용했다는 것이었다. non-linearity는 sigmoid 함수를 말한다. vanishing gradient 문제의 발생 원인은 sigmoid 함수에 있었다. sigmoid 함수가 값을 변형하면서 이런 문제가 생긴 것이었다. 이걸 알아내는데, 10년이 넘게 걸렸다.


hinton 교수님은 sigmoid 함수 대신 ReLU 함수를 제안했다. ReLU 함수는 그림에 있는 것처럼 0보다 작을 때는 0을 사용하고, 0보다 큰 값에 대해서는 해당 값을 그대로 사용하는 방법이다. 음수에 대해서는 값이 바뀌지만, 양수에 대해서는 값을 바꾸지 않는다.

ReLU를 함수로 구현하면 다음과 같다. 너무 간단해서 설명하기도 그렇다. 0과 현재 값(x) 중에서 큰 값을 선택하면 끝이다. 코드로 구현하면 max(0, x)이 된다.


TensorFlow에서 ReLU를 적용하는 방법은 매우 간단하다. sigmoid 함수를 relu 함수로 대신한다. 전달된 값을 처리하는 방식이 조금 달라졌기 때문에 나머지는 전혀 수정하지 않아도 된다. ReLU 함수는 Rectified Linear Unit의 약자로 rectified는 "수정된"이란 뜻을 담고 있다. 즉, 기존의 linear 함수인 sigmoid를 개선했다는 뜻이다.


sigmoid를 ReLU로 대체했다. layer의 갯수가 바뀌거나 하지 않는다. 나머지 코드는 전혀 수정하지 않는다.


잘 동작한다. 감동적이다. Accuracy는 100%를 찍었다. 다만 왼쪽에 있는 step이 198,000이어서 눈에 거슬린다.


그래프를 통해서 봐도 제대로 동작한다. accuracy는 처음에 100%가 된 이후에 계속 100%를 유지했다. cost는 처음에 급격히 떨어진 이후로는 아주 미세하게 값이 줄어들었다. 198,000번까지 반복할 필요가 없었다는 것도 알 수 있다.


sigmoid와 ReLU 함수를 비교하고 있다. 비교 자체가 무의미할 정도다. 정확히 말하면, sigmoid를 사용해선 안될 곳에 사용했기 때문에 올바른 비교라고 할 수도 없다.

ReLU 함수를 두 번 표시한 것은 나중에 설명할 초기값 선택 때문이다. Weight의 초기값을 어떤 걸로 하느냐에 따라 결과가 많이 달라진다. 엄청나게 반복하면 결국엔 같아지겠지만, 좋은 초기값을 선택하면 덜 반복해도 좋은 결과가 나온다.


다양한 activation 함수들을 보여주고 있다. 뒤에 결과가 나오는데, 이 중에서 가장 좋은 성능을 내는 것은 maxout이다.

tanh - sigmoid 함수를 재활용하기 위한 함수. sigmoid의 범위를 -1에서 1로 넓혔다.
ReLU - max(0, x)처럼 음수에 대해서만 0으로 처리하는 함수
Leaky ReLU - ReLU 함수의 변형으로 음수에 대해 1/10로 값을 줄여서 사용하는 함수
ELU - ReLU를 0이 아닌 다른 값을 기준으로 사용하는 함수
maxout - 두 개의 W와 b 중에서 큰 값이 나온 것을 사용하는 함수


Activation 함수의 성능을 비교하고 있다. sigmoid 함수가 n/c라고 나온 것은 결과를 낼 수 없기 때문이다. 사용하지 않아야 할 함수를 사용했으니까.

1%를 약간의 차이라고 해야 할까? 나에게는 약간인데, 김성훈 교수님께서는 상당하다고 말씀하신다. 실제 상황에서 1% 끌어올리는 것이 그만큼 어렵기 때문이 아닐까,하는 생각이 든다.