어느 갱신 방법을 이용할 것인가?
앞선 포스팅에 갱신 방법을 4가지를 알아봤다.
위 그림만 본다면 AdaGrad가 가장 나은 듯 보인다. 그러나 결과는 어떤 issue를 해결해야 하는가에 따라 달라지므로 주의해야 한다.
또한, 학습률 등의 하이퍼파라미터를 어떻게 설정하느냐에 따라서도 결과가 바뀐다.
지금도 많은 연구에서 SGD를 사용한다. 또한, Momentum 과 AdaGrad도 시도해 볼만한 가치가 충분하다.
위 그림은 MNIST예제에서 사용해봤을때 얼마나 다른지를 비교해 본 것이다. 이 실험은 각 층이 100개의 뉴런으로 구성된 5층 신경망에서 ReLU를 활성화 함수로는 사용해 측정한 것이다.
가중치의 초기값
신경망 학습에서 특히 중요한 것이 가중치의 초깃값이다. 가중치의 초깃값을 무엇으로 설정하느냐가 신경망 학습의 성패가 갈린다고 해도 무방하다.
초깃값을 0으로 한다면?
오버피팅을 억제해 범용 성능을 높이는 테크닉인 weight decay(가중치 갑소) 기법을 소개해본다. 가중치 감소는 간단히 말하자면 가중치 매개변수의 값이 작아지도록 학습하는 방법이다. 즉, 가중치 값을 작게 하여 오버피팅이 일어나지 않게하는 것이다.
가중치를 작게 만들고 싶으면 초깃값도 최대한 작은 값에서 시작하는 것이 정공법이다. 그렇다면 가중치의 초깃값을 모두 0으로 설정하면 어떻게 될까? 실제로 가중치 초깃값을 0으로 하면 학습이 올바르게 이뤄지지 않는다.
이유는 오차역전파법에서 모든 가중치의 값이 똑같이 갱신되기 때문이다.
예를 들어 2층 신경망에서 1,2번째 층의 가중치가 0이라고 가정해보자. 그럼 순전파 때는 입력층의 가중치가 0이기 때문에 두 번째 층의 모든 뉴런에 값이 입력된다는 것은 역전파 때 두 번째 층의 가중치가 모두 똑같이 갱신된다는 말이 된다. 그래서 가중치들은 같은 초깃값에서 시작하고 갱신을 거쳐도 여전히 같은 값을 유지하는 것이다. 이는 곧 가중치를 여러 개 갖는 의미를 사라지게 한다.
이 '가중치가 고르게 되어버리는 상황'을 막으려면 초깃값을 random 하게 설정해야 한다.
은닉층의 활성화 값 분포
은닉층의 활성화 값(활성화 함수의 출력 데이터)의 분포를 관찰하면 중요한 정보를 얻을 수 있다.
다음 코드를 살펴보자.
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
return 1/(1+np.exp(-x))
x=np.random.randn(1000,100) #1000개의 데이터
node_num=100 #각 은닉층의 노드(뉴런) 수
hidden_layer_size=5 #은닉층이 5개
activations={} #이곳에 활성화 결과(활성화 값)를 저장
for i in range(hidden_layer_size):
if i!=0:
x=activations[i-1]
w=np.random.randn(node_num,node_num)*1
a=np.dot(x,w)
z=sigmoid(a)
activations[i]=z
for i, a in activations.items():
plt.subplot(1,len(activations),i+1)
plt.title(str(i+1)+'-layer')
plt.hist(a.flatten(),30,range=(0,1))
plt.show()
위 히스토그램을 보면, 활성화 값들이 0,1에 치우쳐 분포되어있다. 여기에서 사용한 sigmoid function은 그 출력이 0에 가까워지자(또는 1에 가까워지자) 그 미분은 0에 다가간다. 그래서 데이터가 0과 1에 치우쳐 분포하게 되면 역전파의 기울기 값이 점점 작아지다가 사라진다. 이것이 바로 gradient vanishing(기울기 소실)이라 알려진 문제이다. 층을 깊게 하는 딥러닝에서는 기울기 소실은 더 심각한 문제가 될 수 있다.
이번에는 가중치의 표준편차를 0.01로 바꿔보자.
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
return 1/(1+np.exp(-x))
x=np.random.randn(1000,100) #1000개의 데이터
node_num=100 #각 은닉층의 노드(뉴런) 수
hidden_layer_size=5 #은닉층이 5개
activations={} #이곳에 활성화 결과(활성화 값)를 저장
for i in range(hidden_layer_size):
if i!=0:
x=activations[i-1]
w=np.random.randn(node_num,node_num)*0.01
a=np.dot(x,w)
z=sigmoid(a)
activations[i]=z
for i, a in activations.items():
plt.subplot(1,len(activations),i+1)
plt.title(str(i+1)+'-layer')
plt.hist(a.flatten(),30,range=(0,1))
plt.show()
이번에는 0.5 부근에 집중되었다. 앞선 히스토그램처럼 0과 1로 치우치친 않았으니 기울기 소실 문제는 일어나지 않았다. 여기서 중요한 점이 있다. 활성화 값들이 치우쳤다는 것은 이 상황에서 다수의 뉴런이 거의 같은 값을 출력하고 있으므로, 뉴런을 여러개 둔 의미가 없어진다는 뜻이다. 즉, 뉴런 100개가 같은 값을 출력한다면 1개짜리와 차이가 없다는 것이다.
Xavier 초깃값
현재 Xavier 초깃값은 일반적인 딥러닝 프레임워크들이 표준으로 사용하고 있다.
각 층의 활성화 값들을 광법위하게 분포시킬 목적으로 가중치의 적절한 분포를 찾고자했다. 그리고 앞 계층의 노드가 n개라면 표준편차가 1/Route(n)인 분포를 사용하면 된다.
처음에는 임의로 초깃값을 선택하였다.
위 그림처럼 uniform Xavier initialization 과 Normal Xavier initialization 두가지가 있다.
초기화 과정은 위 그림과 같다. 더 깊은 수학적 내용과 이해는 이 챕터에서 요하지 않으므로 생략하겠다.
Xavier 초깃값을 사용한 결과, 층이 깊어지면서 형태가 다소 일그러지지만, 앞선 방식보다 확실히 넓게 분포됨을 알 수있다. 각 층에 흐르는 데이터는 적당히 퍼져 있으므로, 시그모이드 함수의 표현력도 제한받지 않고 학습이 효율적으로 이뤄질 것이다.
He 초깃값
sigmoid함수와 tanh함수는 좌우 대칭이라 중앙 부근이 선현인 함수로 볼 수 있다. 그래서 Xavier 초깃값이 적당한 반면, ReLU함수를 이용할 때는 ReLU에 특화된 초깃값을 이용하라고 권장한다. 이 특화된 초깃값을 찾아낸 Kaiming He 의 이름을 따 He 초깃값이라고 한다.
He초깃값은 앞 계층의 노드가 n개일 때, 표준편차가 Route(2/n)인 NormalDistribution을 사용한다. Xavier은 Route(1/n)이었는데 ReLU는 음의 영역이 0이라 더 넓게 분포시키기 위해 2배의 계수가 필요하다고 볼 수 있다.
'Deep Learning > 밑바닥부터 시작하는 딥러닝(1)' 카테고리의 다른 글
Chapter 7. 합성곱 신경망(CNN)(1) (0) | 2021.03.09 |
---|---|
Chapter 6. 학습 관련 기술들(3) (0) | 2021.03.04 |
Chapter 6. 학습 관련 기술들(1) (0) | 2021.03.02 |
Chapter 5. 오차역전파법 (0) | 2021.01.25 |
Chapter 4. 신경망 학습 (0) | 2021.01.23 |