오늘은 확률적 경사 하강법에 대해 알아보겠습니다!
어제 봤던 배치 경사 하강법의 가장 큰 문제는 매 스텝에서 전체 훈련세트를 사용해 그래디언트를 계산한다는 것입니다.
따라서 훈련 세트가 커지면 매우 느려지는데, 이와 반대로 확률적 경사 하강법은 매 스텝에서 딱 1개의 샘플을 무작위로 선택하고, 그 하나의 샘플에 대한 그래디언트를 계산합니다. 매 반복에서 매우 작은 데이터만 처리하기 때문에 알고리즘이 훨씬 빠릅니다.
또한, 매 반복에서 하나의 샘플만 메모리에 있으면 되므로 매우 큰 훈련 세트도 훈련시킬 수 있습니다. (SGD는 외부 메모리 학습 알고리즘으로 구현할 수 있습니다.)
반면, 확률적(무작위) 이기 때문에, 이 알고리즘은 배치 경사 하강법보다 훨씬 불안정합니다. 비용 함수가 최솟값에 다다를 때까지 부드럽게 감소하지 않고, 위아래로 요동치면서 평균적으로 감소합니다. 시간이 지나면 최솟값에 매우 근접하겠지만 요동이 지속되면서 최솟값에 안착하지 못할 것입니다. 따라서 알고리즘이 멈출 때 좋은 파라미터가 구해지지만 최적은 아닙니다.
비용 함수가 매우 불규칙할 경우, 알고리즘이 지역 최솟값을 건너뛸 수 있도록 도와주므로 확률적 경사 하강법이 배치 경사 하강법보다 전역 최솟값을 찾을 가능성이 높습니다. 이러한 무작위성은 지역 최솟값에서 탈출시켜줘서 좋지만 전역 최솟값에 다다르지 못하게 한다는 점에서는 좋지 않습니다. 이 딜레마를 해결하는 한 가지 방법은 학습률을 점진적으로 감소시키는 것입니다.
시작할 때는 학습률을 크게 하고, (수렴을 빠르게 만들고, 지역 최솟값에 빠지지 않게 합니다.) 점차 작게 줄여서 알고리즘이 전역 최솟값에 도달하게 합니다.
매 반복에서 학습률을 결정하는 함수를 학습 스케줄(learning schedule)이라고 부릅니다. 학습률이 너무 빨리 줄어들면 지역 최솟값에 갇히거나 최솟값까지 가는 중간에 멈춰버릴 수도 있습니다. 하지만 학습률이 너무 천천히 줄어들면 최솟값 주변을 오랫동안 맴돌거나 훈련을 너무 일찍 중지해서 지역 최솟값에 머무를 수 있습니다.
아래는 간단한 학습 스케줄을 사용한 확률적 경사 하강법의 구현입니다.
theta_path_sgd = []
m = len(X_b)
np.random.seed(42)
# 일반적으로 한 반복에서 m번 되풀이되고, m은 훈련 세트에 있는 샘플 수 입니다.
# 이때 각 반복을 에포크(epoch)라고 합니다.
n_epochs = 50
#학습 스케줄 하이퍼파라미터
t0, t1 = 5, 50
def learning_schedule(t):
return t0 / (t + t1)
# n_epoch 반복 수 (1당 len(m)만큼 돌게 됨.)
for epoch in range(n_epochs):
for i in range(m) :
if epoch == 0 and i < 20:
y_predict = X_new_b.dot(theta)
style = 'b-' if i > 0 else 'r--'
plt.plot(X_new, y_predict, style)
#확률적(무작위)으로 샘플을 골라 1개 추출
random_index = np.random.randint(m)
xi = X_b[random_index : random_index+1]
yi = y[random_index : random_index+1]
gradients = 2 * xi.T.dot(xi.dot(theta) - yi )
eta = learning_schedule(epoch * m + i)
theta = theta - eta * gradients
theta_path_sgd.append(theta)
plt.plot(X, y, 'b.')
plt.xlabel('$x_1$', fontsize=18)
plt.ylabel('$y$', rotation =0, fontsize = 18)
plt.axis([0,2,0,15])
plt.show()
아래 그림은 첫 20스텝을 보여줍니다.
#배치 경사 하강법 코드가 전체 훈련 세트에 대해 1,000번 반복하는 동안, 이 코드는 훈련 세트에서 50번만 반복하고도 좋은 값에 도달한 것을 알 수 있습니다.
theta
(비슷한 값인 줄은 알겠는데 왜 저는 똑같이 책을 따라하는데 값이 다를까요..ㅋㅋㅋ 이해불가능..ㅠㅠ)
샘플을 무작위로 선택하기 떄문에, 어떤 샘플은 한 epoch에서 여러 번 선택될 수도 있고 어떤 샘플은 전혀 선택되지 못할 수도 있습니다. 알고리즘이 에포크마다 모든 샘플을 사용하게 하려면, 훈련 세트를 섞은 후 차례대로 하나씩 선택하고 다음 에포크에서 다시 섞는 방법을 사용할 수 있습니다. (그러나 이렇게 하면 더 늦게 수렴됩니다.)
사이킷 런에서 SGD 방식으로 선형 회귀를 사용하려면 기본값으로 제곱 오차 비용 함수를 최적화하는 SGDRegressor 클래스를 사용합니다. 다음 코드는 학습률 0.1(eta0=0.1)로 기본 학습 스케줄을 사용하여 epoch를 50번 수행합니다.
from sklearn.linear_model import SGDRegressor
sgd_reg = SGDRegressor(max_iter=5, penalty=None, eta0=0.1, random_state=42)
sgd_reg.fit(X, y.ravel())
sgd_reg.intercept_, sgd_reg.coef_
여기서도 정규방정식으로 구한 것과 매우 유사한 값을 얻었습니다.
블로그
출처
이 글의 상당 부분은 [핸즈온 머신러닝, 한빛미디어/오렐리앙 제롱/박해선] 서적을 참고하였습니다.
나머지는 부수적인 함수나 메서드에 대해 부족한 설명을 적어두었습니다.
학습용으로 포스팅 하는 것이기 때문에 복제보다는 머신러닝에 관심이 있다면 구매해보시길 추천합니다.
도움이 되셨다면 로그인 없이 가능한
아래 하트♥공감 버튼을 꾹 눌러주세요!
'## 오래된 게시글 (미관리) ## > Python (Linux)' 카테고리의 다른 글
30. Python - 다항 회귀 (0) | 2019.01.25 |
---|---|
29. Python - 미니배치 경사 하강법 (0) | 2019.01.25 |
27. Python - 배치 경사 하강법 (0) | 2019.01.24 |
26. Python - Matplotlib에 한글 사용법 (0) | 2019.01.23 |
25. Python - 분류 연습문제 4 [ 스팸 필터 / 스팸 분류기 ] (0) | 2018.12.05 |