Neural network에서 가장 중요한 것은 Optiamization
Stochastic Gradient Descent
minibatch를 활용한 경사하강법
Example)
probloems with SGD 1. 느리게 변하는 손실함수
SGD의 문제점 중 하나는 손실함수가 이같은 형태(taco shell)로 생겼을 때 ,w1과 w2가 있다고 가정
둘중 어떤 하나는 업데이트를 해도 손실함수가 아주 느리게 변한다.
손실 함수가 이런 식으로 생긴 환경에서 SGD를 수행하면, 이런 특이한 지그재그 형태
(gradient의 방향이 고르지 못하기 때문)
----> 고차원에서 더욱더 심각해집니다. (그림은 2차원 밖에 되지 않습니다만 실제로는
가중치가 수천 수억개 일 수 있을 것입니다.)
probloems with SGD 2. local minima 와 saddle points와 관련된 문제
▶ Local minima :우리는 최소의 loss를 찾으려고 SGD를 수행하는데 컴퓨터는 가까운 길에서 gradients가 0이되는 지점을 찾아버리므로 이게 최고의 loss다 생각하고 학습을 멈춰버리는 경우
▶ saddle points : 어느 방향에서 보면 극대값이지만 다른 방향에서 보면 극소값이 되는 점이다.
(한쪽방향으로는 증가, 다른 한쪽방향으로는 감소하는 지역)
probloems with SGD 3. 부정확한 loss 값
원래는 손실함수를 계산할 때는 엄청 엄청 많은 Traing set 각각의 loss를 전부 계산해야 합니다.
이 예시의 N이 전체 training set일 경우에 N개 만큼 계산을 해줘야합니다.
SGD 경우에는 미니배치의 데이터들만 가지고 실제 Loss를 “추정”하기만 합니다.
이는 매번 정확한 gradient를 얻을 수가 없다는 것을 의미합니다.
gradient의 부정확한 추정값(noisy estimate) 만을 구할 뿐입니다
기존 SGD 문제점들을 보완
왼쪽은 classic SGD (gradient 방향으로만 움직임)
오른쪽은 SGD + momentum (미니배치의 gradient 방향만 고려하는 것이 아니라 velocity를 같이 고려)
rho : momemtum의 비율 (velocity의 영향력을 rho의 비율 = 0.9 정도 사용)
-> gradient vector 그대로의 방향이 아닌 velocity vector의 방향
기존에는 방향성만 가지고 있었다면
momentum을 통해 velocity를 추가하여 계속해서 움직이므로 local minina와 saddle points 문제 둘다 해결이 가능해집니다.
이동을보면 지그재그로 움직이던게 noise가 평균화로 매끄럽게 움직임
빨간 점 : 현재 지점
Red Vector : 현재 지점에서의 gradient의 방향
Green vector : Velocity vector
actual step : 실제 움직이는 vector (위 둘의 가중평균)
-> gradient의 noise 해결
Momentum의 변형: Nesterov accelerated gradient (Nesterov momentum)
빨간 점에서 시작해서 우선은 Velocity방향으로 움직입니다.
그리고 그 지점에서의 gradient를 계산합니다. 다시 원점으로 돌아가서 둘을 합치는 것입니다.
-> 두 정보를 약간 더 섞어줌
nestroc 변형 : 에러보정 효과
기본 SGD : 많이 느림
momentum : minima를 그냥 지나쳐 버리는 경향이 있습니다.이전의 velocity의 영향을 받기 때문입니다.
Nesterov: 일반 momentum에 비해서 overshooting이 덜 한 것을 알 수 있습니다.
#AdaGrad
grad_squared =0
while True:
dx = compute_gradient(x)
grad_sqaured += dx * dx #학습 도중 계산되는 gradient 제곱
x -= learing_rate * dx / (np.sqrt(grad_sqaured) + 1e-7)
# Update를 할때 Update term을 앞서 계산한 gradient 제곱 항으로 나눔
Adagrad의 문제점
- dimension에 따른 속도
Small dimension에서는 gradient의 제곱 값 합이 작습니다. 이 작은 값이 나눠지므로 가속도가 붙게 됩니다.
Large dimension에서는 gradient가 큰 값 이므로 큰 값이 나눠지게 되겠죠. 그러므로 속도가 점점 줄어듭니다.
- Non-convex에서 AdaGrad의 멈춤
update 동안 gradient의 제곱이 계속해서 더해집니다. 때문에 이 값(estimate)은 서서히(monotonically) 증가하게 됩니다. 이는 Step size를 점점 더 작은 값이 되게 합니다.
손실함수가 convex한 경우에 점점 작아지는 것은 정말 좋은 특징이지만
non-convex case에서는 문제 saddle point에 걸려버렸을 때 AdaGrad는 멈춰버릴 수 있습니다.
#RMSProp
grad_squared =0
while True:
dx = compute_gradient(x)
grad_sqaured = decay_rate * grad_squared+(1-decay rate) * dx*dx
# 이전항 * decay_rate + 현재 gradient의 제곱 *(1 - decay rate)
x -= learing_rate * dx / (np.sqrt(grad_sqaured) + 1e-7)
-AdaGrad의 gradient 제곱 항을 그대로 사용
-이 값들을 그저 누적만 시키는 것이 아니라
기존의 누적 값에 decay_rate를 곱
RMSProp은 각 차원마다의 상황에 맞도록 적절하게 궤적(trajectory)을 수정
#Adam
first_moment =0
second_moment=0
while True:
dx = compute_gradient(x)
first_moment beta1 * first_moment + (1-beta1) *dx #momentum
second_moment = beta2*second_moment + (1-beta2) *dx*dx #AdaGrad/RMSprop #1번
first_unbias = first_moment / (1-beta1 **t)
second_unbias = second_moment/(1-beta2 **t) #bias correction #2번
x -= learing_rate * first_unbias / (np.sqrt( second_unbiast) + 1e-7) #AdaGrad/RMSprop #3번
ADAM의 동작
- Adam은 first moment와 second moment을 이용해서 이전의 정보(estimate)를 유지
- Adam은 초기 Step이 엄청 커져 버릴 수 있고 이로 인해 잘못 가능성
-> Adam을 이를 해결하기 위해 보정하는 항을 추가 (bias correction term )- first/second moments를 Update하고 난 후 현재 Step에 맞는 적절한 unbiased term 을 계산
지금까지 배운 Optimization 알고리즘들을 모두 1차미분을 활용한(first-order) 방법이었습니다.
그림처럼 1차원의 손실함수가 있다고 생각해 봅시다.
우리는 지금 빨간색 점에 있는 것입니다.이 점에서 gradient 를 계산하겠죠
이gradient 정보를 이용해서 우리는 손실함수를선형함수로 근사시킵니다.이는 일종의 1차 테일러 근사입니다.
(first-order Taylor apporximation)
이 1차 근사함수를 실제 손실함수라고 가정하고 Step을 내려갈 것입니다.
이 근사함수로는 멀리갈 수 없습니다 현재 사용하는 정보는 1차 미분값일 뿐입니다.
2차 근사 (second-order approximation)의 정보를 추가적으로 활용하는 방법이 있습니다.
이는 2차 테일러 근사 함수가 될 것이고 이 함수는 2차함수의 모양입니다.
2차 근사를 이용하면 minima에 더 잘 근접할 수 있습니다.
이것이 바로 2nd-order optimization의 기본 아이디어입니다.
Deep learning에서는 사용할 수 없습니다.
왜냐하면 Hessian matrix는 N x N 행렬입니다. N은 Network의 파라미터 수입니다.
N이 1억이면 1억의 제곱만큼 존재할 것입니다.
이를 메모리에 저장할 방법은 없으며 또한 역행렬계산도 불가능 할 것입니다.
지금까지 Optimization : training error를 줄이기 위한 방법
우리의 주된 관심사 한번도 보지 못한 데이터에 대한 성능!!!
'한번도 보지 못한 데이터' 에서의 성능을 올리기 위해서는 어떻게 해야 할까요?
가장 빠르고 쉬운 길은 바로 모델 앙상블입니다. Machine learning 분야에서 종종 사용하는 기법입니다.
모델을 하나만 학습시키지 말고 10개의 모델을 독립적으로 학습시키는 것입니다.
결과는 10개 모델 결과의 평균을 이용합니다. 모델의 수가 늘어날수록 overfitting 줄어들고 성능이 조금씩 향상됩니다. 보통 2%정도 증가하죠
앙상블이 아닌 단일 모델의 성능을 향상시키기 위해서는 어떻게 해야 할까요?
regularization
우리가 모델에 어떤 것을 추가할 텐데 모델이 training data에 fit하는 것을 막아줄 것입니다.
그리고 한번도 보지 못한 데이터에서의 성능을 향상시키는 방법입니다.
Neural network에서 가장 많이 사용하는 regularization은 dropout입니다.
왼쪽에는 dropout이 없고 오른쪽은 dropout이 적용된 경우입니다
dropout 진행 과정
- forward pass 과정에서 임의로 일부 뉴런을 0으로 만드는 것입니다.
- forward pass 할때마다 0이 되는 뉴런이 바뀝니다. Dropout은 한 레이어씩 진행하게 됩니다.
- 한 레이어의 출력을 전부 구합니다. 그리고 임의로 일부를 0으로 만듭니다.
- 다음 레이어로 넘어갑니다.
Dropout이 Overfitting을 어느정도 막아줍니다.
dropout code
#dropout
p=0.5 # dropout probability
def dropout(X):
# 1 layer
H1 = np.maximum(0,np.dot(W1,X)+b1) # 뉴런을 0으로 만듬
UI = np.random.rand(*H1.shape) < p # dropout probability만큼 0으로 만듬
H1 *= U1
# 2 layer
H2 = np.maximum(0,np.dot(W2,H1)+b2) # 뉴런을 0으로 만듬
U2 = np.random.rand(*H2.shape) <p # dropout probability만큼 0으로 만듬
H2 *= U2
# output layer
out = np.dot(W3,H2) +b3
단일 모델로 앙상블 효과가진 Dropout
Dropout을 적용한 네트워크를 살펴보면 뉴런의 일부만 사용하는 서브네트워크
Dropout으로 만들 수 있는 서브네트워크의 경우의 수가 정말 다양하다는 것을 알 수 있습니다.
서로 파라미터를 공유하는 서브네트워크 앙상블을 동시에 학습시키는 것이라고 생각할 수 있습니다.
아주 거대한 앙상블 모델을 동시에 학습 시키는 것이라고 볼 수 있겠습니다.
Dropout을 사용하면 Test time에 어떤 일이 일어날까요?
Dropout을 사용하면 Network에 z라는 입력이 추가됩니다.
z는 "random dropout mask"입니다. z는 random입니다. 하지만 test time에 임의의 값을
부여하는 것은 좋지 않습니다.
랜덤성을 없애면서 평균화하기 위한 dropout 적분법:
출력 : a
입력 : x, y
가중치 : w_1, w_2
dropout(p = 0.5)
dropout mask : 4가지 경우의 수
Test time에서 a는 w_1x + w_2y 입니다.
Train time에서 a의 기댓값은 1/2(w_1x + w_2y) 입니다.
dropout test time code
#dropout test time
p=0.5 # dropout probability
def predict(X):
H1 = np.maximum(0,np.dot(W1,X)+b1) *p #activation scale 설정
H2 = np.maximum(0,np.dot(W2,H1)+b2)*p #activation scale 설정
out = np.dot(W3,H2) +b3
#inverted dropout
p=0.5 # dropout probability
def train_inverted_dropout(X):
# 1 layer
H1 = np.maximum(0,np.dot(W1,X)+b1) # 뉴런을 0으로 만듬
UI = (np.random.rand(*H1.shape) < p) /p # inverted dropout
H1 *= U1
# 2 layer
H2 = np.maximum(0,np.dot(W2,H1)+b2) # 뉴런을 0으로 만듬
U2 = np.random.rand(*H2.shape) /p # inverted dropout
H2 *= U2
# output layer
out = np.dot(W3,H2) +b3
def predict(X):
H1 = np.maximum(0,np.dot(W1,X)+b1) # test time 효율성 증가
H2 = np.maximum(0,np.dot(W2,H1)+b2) # test time 효율성 증가
out = np.dot(W3,H2) +b3 ```
효율성을 증가시키기 위해 Dropout을 역으로 계산하는 것입니다 (inverted dropout)
fractional max pooling