반응형

<복습>

CNN은 spatial structure를 보존하기 위해 convolutional layers를 사용하는 NN의 한 종류이다. (FC레이어는 이미지를 다루기 위해 이미지 행렬을 한 줄로 쭉 펴는 작업(Flatten)를 하는데 이미지에서 붙어 있던 픽셀들이 Flatten한 행렬에서는 서로 떨어진다 => 이미지의 공간적 구조(spatial structure) 무시. 반면에 CNN은 필터를 슬라이드함으로써 주위 픽셀들을 계산하면서 이미지의 공간적인 구조를 보존한다.) Conv 필터(weights)가 입력 이미지를 슬라이딩해서 계산한 값들이 모여 각 출력 Activation map을 만든다. Conv layer는 각 레이어 마다 다수의 필터를 사용할 수 있고, 각 필터는 서로 다른 Activation map을 생성한다. 우리는 모든 weights(가중치) 또는 파라미터들의 값을 알고 싶은 것이고, 지난 시간에 배웠듯이 Optimization을 통해서 네트워크의 파라미터를 학습할 수 있다. 파라미터를 업데이트 하면서, Loss 라는 산에서 Loss가 줄어드는 방향으로 이동하고 싶어한다. 그렇게 하기 위해서는 gradient의 반대 방향으로 이동하면 된다. Mini-batch Stochastic Gradient Descent는  우선 데이터의 일부만 가지고(sample a batch of data) Forword pass를 수행한 뒤에 Loss를 계산한다. 그리고 gradient를 계산하기 위해서 backprop를 수행한다.

 

앞으로의 강의는 NN의 학습에 대해 다룰 것이다.

1. NN학습을 시작할 때 필요한 기본 설정 (활성함수 선택, 데이터 전처리, 가중치 초기화 ,Regularization, gradient checking 등)

2. Training dynamics (학습 과정 다루기, 파라미터 업데이트, 하이퍼파라미터 최적화 등)

3. Evaluation 모델 평가, Model Ensemble

 

 

<Activation Fuction 활성화 함수>

Layer로 데이터 입력이 들어오면 (FC나 CNN에)가중치와 곱하고, 활성함수, 즉 비선형 연산을 거친다.

다양한 종류의 활성함수

 

1.Sigmoid

Sigmoid (1/(1+e^-x))는 각 입력을 받아서 그 입력을 [0, 1] 사이의 값이 되도록 해준다. 입력의 값이 크면 Sigmoid의 출력은 1에 가깝고, 값이 작으면 0에 가깝다. 0 근처 구간(rigime)을 보면 선형함수 같아 보이는 linear regime이 있다. Sigmoid는 역사적으로 유명했는데,  Sigmoid가 일종의, 뉴런의 firing rate(뉴런의 활성화률)를 saturation(포화)시키는 것으로 해석할 수 있기 때문이다. 어떤 값이 0에서 1사이의 값을 가지면 이를 fireing rate라고 생각할 수 있다.

 

문제점은 1) Saturation되는게 gradient를 없앤다. 출력 그래프를 보면 입력 신호의 총합이 크거나 작을 때 기울기가 0에 가까워 지는데 이 0에 가까워지는 현상을 Saturated라고 하고 이것은 Vanising Gradient 문제를 일으킨다. 밑에 그림에서 X가 -10이면 gradient는 0이 된다. Sigmoid에서 음의 큰 값이 X로 들어오면 sigmoid가 float하게 되고 gradient가 0이 된다. 그러므로 0에 가까운 값이 backprob된다. 그리고 gradient가 죽어버리고 밑으로 0이 계속 전달된다. X가 0이면 잘 동작할 것이다 (그럴싸한 gradient와 backprop이 잘됨). X가 10이면 (큰 양수), 이 때도 sigmoid가 flat하기 때문에 gradient를 다 죽이게 되고 gradient가 잘 흐르지 않음. graident가 0이 되고 0에 가까운 값이 backprob되면 결국 출력 노드에서는 가중치의 업데이트 의미가 있는 것이 되겠지만 밑으로 계속 Backpropagation 과정을 하다보면 0이 계속 전달되게 되므로 의미가 없어진다.

 

2) Sigmoid 의 출력이 zero centered하지 않다. Sigmoid의 입력(X)이 항상 양수일 때 어떤일이 벌어질까? X는 어떤 가중치랑 곱해지고 활성 함수를 통과할 것이다. W에 대한 gradient를 한번 생각해보면, local gradient가 그냥 X가 된다. 모든 X가 양수라는 건 local gradient는 전부 양수가 되거나 전부 음수가 된다는 뜻이다. 결론적으로는 gradient의 부호가 계속 동일하게 되기 때문에 가중치가 모두 같은 방향으로만 움직일 것임을 의미한다. 파라미터를 업데이트 할 때 다 같이 증가하거나 다같이 감소하거나 할 수 밖에 없다. 문제는 이런 gradient 업데이트가 아주 비효율적이라는 것이다 (4분면 중 2개의 영역만 사용함. 파란색 화살표가 최적의 업데이트인데 빨간색으로만 움직일 수 있다. => 더 많이 이동해야함). 우리는 일반적으로 zero-mean data을 원하는데, 입력 X가 양수/음수를 모두 가지고 있으면 전부 같은 방향으로 움직이는 일은 발생하지 않을 것이기 때문이다.

 

2. Tanh

Sigmoid랑 유사하지만, 범위가 [-1 , 1]이다. 가장 큰 차이점은 zero-centered라는 것이다. 그래서 Sigmoid의 두번째 문제가 해결된다. 하지만 saturation때문에 여전히 Gradient가 죽는다. 밑에 그래프를 보면 여전히 gradient가 평평해 지는 구간이있다. Tanh는 Sigmoid보다는 조금 낫지만 그래도 여전히 문제점이 존재한다.

 

3. ReLU

CNN 강의에서 본적이 있다 (Conv layer들 사이 사이에 ReLU). ReLU의 함수는 f(x) = max(0,x)이다. ReLU는 element-wise 연산을 수행하며 입력이 음수면 값이 0이 된다. 그리고 양수면 입력 값 그대로 출력한다. ReLU는 상당히 자주 쓰이는데, 기존에 Sigmoid와 Tanh한테 있었던 문제점을 가지고 비교해보면, ReLU는 양의 값에서는 saturation되지 않는다. 즉, 입력의 절반의 saturation 되지 않는다는 점이 큰 장점이다. 그리고 Sigmoid 함수 안에는 지수 항이 있지만 ReLU는 단순히 max 연산이라 계산이 매우 빠르다 (효율이 뛰어남). 실제로 Sigmoid나 Tanh보다 수렴속도가 거의 6배 정도 빠르다. 그리고 생물학적 타당성도 ReLU가 sigmoid보다 크다 (실제 신경과학적 실험을 통해 뉴런을 관찰해보면, 뉴런의 입/출력 값을 확인해봤을 때, sigmoid보다 ReLU 스럽다는걸 알 수 있다. ImageNet 2012에서 우승한 AlexNet이 처음 ReLU를 사용하기 시작했다.

 

하지만, 문제가 하나 있다면,  ReLU는 더이상 zero-centered가 아니다. Tanh가 이 문제는 해결했었는데,  ReLU는 다시 이 문제를 가지고 있다. 그리고 또 다른 ReLU의 이슈는 양의 수에서는 saturation 되지 않지만 음의 경우에서는 그렇지 않다는 것이다. X가 -10이면 gradient가 0이 되고, X가 10일 때는 선형 영역(linear regime)에 속한다. X가 0일 때는 0이다. ReLU는 gradient의 절반을 죽여버린다 (dead ReLU 현상). 밑에 data cloud(=training data)를 보면 ReLU에서는 평면의 절반만 activate 된다. 그리고 ReLU가 data cloud에서 떨어져 있는 경우에 "dead ReLU" 가 발생할 수 있다. dead ReLU에서는 activate 가 일어나지 않고 update되지 않는다. 반면 active ReLU는 일부는 active되고 일부는 active하지 않는다. 몇 가지 이유로 이런 일이 발생 할 수 있는데,

1) 초기화를 잘 못한 경우 (가중치 평면이 data cloud에서 멀리 떨어져 있는 경우, 어떤 데이터 입력에서도 activate 되는 경우가 존재하지 않을 것이고 backporp이 일어나지 않음)

2) Leraning rate가 지나치게 높은 경우 (처음에 "적절한 ReLU" 로 시작할 수 있다고 해도 만약 update를 지나치게 크게 해 버려 가중치가 날뛴다면 ReLU 가 데이터의 manifold를 벗어남 => 처음에는 학습이 잘 되다가 갑자기 죽어버리는 경우)

 

4. leaky ReLU

ReLU와 유사하지만 negarive regime에서 0이 아니다. 이 함수는 negative에도 기울기를 살짝 주게 되면 앞서 설명했던 문제를 상당 부분 해결한다. Leaky ReLU의 경우에는 negative space(음수)에서도 saturation되지 않는다. 그리고 여전히 계산이 효율적이라 Sigmoid나 Tanh보다 수렴을 빨리 할 수 있다. Dead ReLU 현상도 없다.

 

5. PReLU (parametric rectifier)

PReLU는 negative space에 기울기가 있다는 점에서 Leaky ReLU와 유사하다. 기울기가 alpha 라는 파라미터로 결정되는데, alpha를 딱 정해놓는 것이 아니라 backpro으로 학습시키는 파라미터로 만든다. 활성함수가 조금 더 유연해 질 수 있다. 

 

6. ELU

ELU는 ReLu의 이점을 그대로 가져오지만 zero-mean에 가까운 출력값을 보인다. zero-mean에 가까운 출력은 앞서 leaky ReLU, PReLU가 가진 장점이다. 하지만 Leaky ReLU와 비교해보면 ELU는 negative에서 기울기를 가지는 것 대신에 또 다시 saturation된다. ELU가 주장하는 바는 이런 saturation이 좀더 잡음(noise)에 강인할 수 있다는 것이다 (이런 deactivation이 좀더 강인함을 줄 수 있다). ELU는 ReLU와 Leaky ReLU의 중간 정도로 eaky ReLU처럼 zero-mean의 출력을 내지만 Saturation의 관점에서 ReLU의 특성도 가지고 있다.

 

7. Maxout Neuron

지금까지 본 활성함수들과 다르게 입력을 받아드리는 특정한 기본형식을 미리 정의하지 않는다. 대신에 w1에 x를 내적한 값 + b1과 w2에 x를 내적한 값 + b2 의 최댓값을 사용한다. Maxout은 이 두 함수 중 최댓값을 취한다. Maxout는 ReLU와 leaky ReLU의 좀 더 일반화된 형태인데, Maxout은 이 두 개의 선형함수를 취하기 때문이다. Maxout또한 선형이기때문에 saturation 되지 않으며 gradient가 죽지 않는다. 문제점은 뉴런당 파라미터의 수가 두배가 되어 W1과 W2를 지니고 있어야된다는 점이다.

 

다양한 활성화 함수를 살펴봤는데, 실제로는 ReLU가 가장 많이 쓰인다. ReLU가 표준으로 많이 사용되고, 대게 잘 동작한다. 하지만 주의할 점은 ReLU를 사용할 때 learning rate를 아주 조심스럽게 결정해야된다는 점이다. 문제에 맞춰 Leaky ReLU, Maxout, ELU와 같은 것도 써보면서 어떤 활성화 함수가 잘 동작하는지 알아볼 수 있다. Tanh도 써볼 수 있겠지만, 보통 ReLU와 ReLU의 변종들이 좀 더 잘 동작한다. Sigmoid는 추천하지 않는데, 가장 구식이고 요즘은 LU 패밀리 계열이 더 잘 동작하기 때문이다.

 

 

<Data Preprocessing 데이터 전처리>

일반적으로 입력 데이터는 전처리를 다 해준다. 가장 대표적인 전처리 과정은  zero-mean으로 만들고 normalize 하는 것이다. normalization(정규화)은 보통 표준편차로 한다. 모든 입력 값이 positive라면 최적의 weight update를 할 수 없는 문제가 발생하는데, 이를 해결하기 위한 방법으로 입력값에 zero-mean 값을 빼서 zero-centered되게 만들어준다. normalization을 해주는 이유는 모든 차원이 동일한 범위안에 있게 해줘서 전부 동등한 기여(contribute)를 하게 하기 때문이다. 실제로 이미지의 경우에는 전처리로 zero-centering 정도만 하고 normalization는 하지 않는다. 이미지는 이미 각 차원 간에 스케일이 어느정도 맞춰져 있기 때문이다. 따라서 스케일이 다양한 여러 ML 문제와는 달리 이미지에서는 normalization을 엄청 잘 해줄 필요가 없다.

 

*Data Normalization은 데이터의 범위를 사용자가 원하는 범위로 제한하는 것 -> (정규화하고자 하는 값 - 데이터 값들 중 최소값) / (데이터 값들 중 최대값 - 데이터 값들 중 최소값) 또는 (정규화하고자 하는 값 - 데이터의 평균) / 데이터의 표준편차 (Standardizaiton이라고도 함)

*앞서 sigmoid 에게는 zero-mean이 필요하다고 했는데, 데이터 전처리는 sigmoid의 zero-mean 문제를 단지 첫 번째 레이어에서만 해결할 수 있다. 처음에는 입력 이미지가 zero-mean 이므로 해결 할 수 있겠지만, 그 다음 레이어부터는 다시 문제가 반복된다. 이미지 전처리가 Sigmoid에서의 문제를 해결하기에는 충분하지 않음

 

 

<Weight initialization 가중치 초기화>

예시로 기본적이 2-layer neural network를 생각해봤을 때 우리는 가중치 업데이트를 하면서 학습을 한다. 하지만 맨 처음에 어떤 초기 가중치들이 존재할 것이다. 그리고 gradient를 계산해서 가중치를 업데이트할 것이다. 모든 가중치 = 0이면 어떻게 될까? 그럼 모든 뉴런이 같은일을 할 것이다. 가중치가 0 이라서 모든 뉴런은 모두 다 같은 연산을 수행한다. 출력도 모두 같을 것이고. 결국 gradient도 서로 같을 것이다. 결국 모든 가중치가 똑같은 값으로 업데이트 되고, 모든 뉴런이 모두 똑같이 생기게 된다. 하지만 우리는 뉴런들이 서로 다르게 생기길 원한다. 그래서 가중치를 동일하게 초기화시킬 수 없다. 가중치를 동일하게 초기화 시키면 Symmetry breaking이 일어날 수 없다 (가중치를 랜덤하게 초기화를 시켜 Symmetry breaking을 한다 => 대칭이 되는 것을 피한다.

 

초기화 문제를 해결하는 첫번째 방법은 임의의 작은 값으로 초기화하는 것이다. 우리는 초기 W를  표준정규분포(standard gaussian)에서 샘플링하는 방법을 써볼 것이다. 샘플링 후, 좀 더 작은 값을 위해 스케일링을 해줄 것이다. 0.01을 나눠 표준편차를 1e-2 즉 0.01로 만든다. 이런 식으로 모든 가중치를 임의의 값으로 초기화할 것이다. 하지만 이 방법은 더 깊은 네트워크에서 문제가 생길 수 있다.

 

왜그런지 조금 더 깊은 네트워크를 가지고 실험을 한번 해보자. 밑에 그림을 보면 10개의 레이어로 이루어진 네트워크가 있다. 레이어당 500개의 뉴런이 있고, 활성화 함수로는 tanh을 사용할 것이다. 그리고 가중치는 임의의 작은 값으로 초기화시킬 것이다. 데이터를 forward pass시키고 각 레이어별 activations 수치를 통계화 시켜 보면 다음과 같다. 밑에 결과는 각 레이어 출력의 평균과 표준편차를 계산한 것이다. 코드를 생각해보면 밑에 결과는 X와 W를 내적한 값에 tanh를 취하고 저장한 값이다. 첫번째 레이어를 보면 평균은 항상 0 근처에 있다 (가운데 파랑색 선 그래프). 이는 tanh이 zero-centered 이기 때문에 평균이 0에 가까운건 당연한 결과다. 하지만 표준편차를 보면 가파르게 줄어서 0에 수렴하는 형태다. 맨 밑에 막대 그래프를 보면 이해가 더 쉬운데 막대 그래프는 위에 선 그래프와 똑같은 결과를 분포로 표현한 것이다. 막대 그래프를 보면 첫번째 레이어에서는 가우시안(표준정규분포) 형태로 좋은 분포를 형성하고 있는 것을 볼 수 있다. 하지만 문제는 W를 곱하면 곱할 수록 가중치 W를 너무 작은값으로 초기화 했기 때문에 출력 값이 급격히 줄어든다. 그리고 결국 0이 된다. 그리고 모든 활성함수 결과도 0이 되버린다. Backwards pass로 생각해서 gradient를 구해보면 X(각 레이어의 입력값)이 엄청 작은 것이기 때문에 gradient도 작고 업데이트가 잘 일어나지 않는 결과가 나온다 (입력값이 점점 0에 수렴하고 가중치를 업데이트 하려면 upstream graident에 local gradient를 곱하면 되는데 WX를 W에 대해 미분해보면 local gradient가 입력 X가 된다 = X는 엄청 작은 값).

가중치가 엄청 작을 때 발생하는 문제

 

가중치를 큰 값으로 초기화 하면 어떨까? 가중치의 편차를 0.01이 아니라 1로 바꾸면 가중치가 커질 것이다. 이렇게 큰 가중치를 통과한 출력 WX를 구하고 tanh를 거치면 값들이 saturation 될 것이다. 가중치가 큰 값을 가지므로 tanh의 출력은 항상 saturation되는 것이다. 이렇게 되면 밑에 결과를 보면 출력이 항상 1이나 -1이 된다. 값들은 saturation되고 gradient는 0이되서 가중치 업데이트가 일어나지 않는 결과가 나온다. 이처럼 적절한 가중치를 얻기는 어려운 문제다. 너무 작으면 사라져버리고, 너무 크면 saturation되 버린다. 그래서 어떻게 하면 가중치 초기화를 잘 할 수 있을지 고민하기 시작했다. 

가중치가 엄청 클 때 발생하는 문제

 

<Xavier initialization>

가중치 초기화에 대해 널리 알려진 좋은 방법 중 하나는 바로 Xavier initialization이다 (Glorot가 2010에 발표한 논문).  밑에 W 가중치의 공식을 보면 Standard gaussian으로 뽑은 값을 입력의 수로 스케일링 해준다. Xavier initialization이 하는 일은 입출력의 분산을 맞춰주는 것이다. 입력의 수가 작으면 더 작은값으로 나누고 좀 더 큰 값을 얻는다. 우리는 입력 수가 작으면 더 큰 가중치가 필요한데, 작은 입력의 수가 가중치와 곱해지기 때문에 가중치가 더 커야지만 출력의 분산만큼 큰 값을 얻을 수 있기 때문이다. 반대로 입력의 수가 많은 경우에는 더 작은 가중치가 필요하다. 하지만 이는 Linear activation있다는 가정하에 잘 작동한다. Tanh경우에도 우리는 지금 tanh의 active region안에 있다고 가정하는 것이다. 그러므로 ReLU(비선형)를 쓰면 잘 동작하지 않는다.

Xavier initialization

 

ReLU는 출력의 절반을 죽이고 그 절반은 매번 0이 되기 때문에 출력의 분산을 반토막 내버린다. 그래서 값이 너무 작아지고 밑에 그림을 보면 분포가 줄어든다. 점점 많은 값들이 0이되고 결국 비활성 된다. 이 문제를 해결하기 위해서는 추가적으로 2를 더 나눠주는 방법이 있다. 2를 나눠주는 이유는 뉴런들 중 절반이 없어진다는 사실을 고려해서 2로 나눠준다. 실제 이 방법은 잘 동작한다. 

ReLU를 쓰면 잘 작동이 안됨

 

2로 나눠주는 방법을 쓰면 밑에 결과를 보면 알 수 있듯이 좋은 분포를 형성하고 있다. 이 작은 변화가 훈련에 있어 엄청난 차이를 보이는 것이다. 적절한 초기화는 여전히 활발히 연구되고 있는 분야다.

하지만 추가적으로 가중치 계산을 할 때 2를 나눠주면 ReLU를 써도 작동이 잘됨

 

<Batch Normalization 배치 정규화>

 

 

 

 

 

 

Slides: http://cs231n.stanford.edu/slides/2017/cs231n_2017_lecture6.pdf

 

반응형

'AI > CS231n' 카테고리의 다른 글

[9강] CNN Architectures  (0) 2020.12.05
[8강] Deep Learning Software  (0) 2020.11.28
[5강] Convolutional Neural Networks  (0) 2020.11.07
[4강] Introduction to Neural Networks  (0) 2020.10.31
[3강] Loss Functions and Optimization  (0) 2020.10.30