[ML] 회귀 - 경사 하강법 (Gradient Descent)

강주형·2022년 7월 14일
0

단순 선형 회귀 (Simple Linear Regression)에 대한 경사 하강법 실습
NumPy로 구현해보기


Y=W0+W1X1Y = W_0 + W_1X_1
W0=W0ηdLoss(W)dW0=W0+η(2Ni=1N(Yi(W0+W1Xi1))W_0 = W_0 - \eta \frac{dLoss(W)}{dW_0} = W_0+\eta(\frac{2}{N}\sum_{i=1}^N(Y_i-(W_0+W_1X_{i1}))
W1=W1ηdLoss(W)dW1=W1+η(2Ni=1Nxi(Yi(W0+W1Xi1))W_1 = W_1 - \eta \frac{dLoss(W)}{dW_1} = W_1+\eta(\frac{2}{N}\sum_{i=1}^Nx_i(Y_i-(W_0+W_1X_{i1}))

식 도출 과정은 생략

시뮬레이션 데이터 값 생성

실습을 위해 Y=4X+6Y = 4X + 6 시뮬레이션 데이터를 생성함

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

np.random.seed(0)

X = 2 * np.random.rand(100,1)
y = 6 +4 * X+ np.random.randn(100,1)

plt.scatter(X, y)


(Batch) Gradient Descent

W0W_0W1W_1 업데이트시 모든 데이터를 사용하는 Gradient Descent

W0W_0W1W_1의 값을 최소화 할 수 있도록 업데이트를 수행하는 함수 생성

  • zeros_like(W): W와 같은 Shape으로 모든 원소가 0인 ndarray 만듦
  • 최종적으로 업데이트된 W0W_0W1W_1을 반환함
  • 나중에 반복문에 들어가서 반복적으로 업데이트 수행
# w1 과 w0 를 업데이트 할 w1_update, w0_update를 반환
def get_weight_updates(w1, w0, X, y, learning_rate=0.01):
    N = len(y)

    w1_update = np.zeros_like(w1)
    w0_update = np.zeros_like(w0)
    
    y_pred = np.dot(X, w1.T) + w0
    diff = y-y_pred
         
    w0_factors = np.ones((N,1))

    # w1과 w0을 업데이트할 w1_update와 w0_update 계산
    w1_update = -(2/N)*learning_rate*(np.dot(X.T, diff))
    w0_update = -(2/N)*learning_rate*(np.dot(w0_factors.T, diff))    
    
    return w1_update, w0_update

위에서 만든 get_weight_updates()함수를 호출해서 W0과 W1을 반복해서 업데이트하는 함수 생성

  • 인자 iters 만큼 반복 수행
# 입력 인자 iters로 주어진 횟수만큼 반복적으로 w1과 w0를 업데이트 적용
def gradient_descent_steps(X, y, iters=10000):
    # w0와 w1을 모두 0으로 초기화. 
    w0 = np.zeros((1,1))
    w1 = np.zeros((1,1))
    
    # 인자로 주어진 iters 만큼 반복적으로 get_weight_updates() 호출하여 w1, w0 업데이트 수행. 
    for ind in range(iters):
        w1_update, w0_update = get_weight_updates(w1, w0, X, y, learning_rate=0.01)
        w1 = w1 - w1_update
        w0 = w0 - w0_update
              
    return w1, w0

예측 오차 비용 계산을 수행하는 함수 생성

cost=Σ(YY^)2Ncost = \frac{\Sigma(Y-\hat{Y})^2}{N}
def get_cost(y, y_pred):
    N = len(y) 
    cost = np.sum(np.square(y - y_pred))/N
    return cost

경사 하강법 수행 후 최종 W0,W1W_0, W_1 값 출력 후 costcost 계산

w1, w0 = gradient_descent_steps(X, y, iters=1000)
print("w1:{0:.3f} w0:{1:.3f}".format(w1[0,0], w0[0,0]))
y_pred = w1[0,0] * X + w0
print('Gradient Descent Total Cost:{0:.4f}'.format(get_cost(y, y_pred)))
w1:4.022 w0:6.162
Gradient Descent Total Cost:0.9935

최종 예측한 선형 회귀 선을 그려보기

plt.scatter(X, y)
plt.plot(X,y_pred)


Mini-Batch Gradient Descent

W0W_0W1W_1 업데이트시 Batch 사이즈만큼 데이터를 사용하는 Gradient
여기에 설명 있음, 아래 코드는 방법 (1)

get_weight_updates() 함수는 Batch것과 같은 것을 사용

def stochastic_gradient_descent_steps(X, y, batch_size=10, iters=1000):
    w0 = np.zeros((1,1))
    w1 = np.zeros((1,1))
    prev_cost = 100000
    iter_index =0
    
    for ind in range(iters):
        np.random.seed(ind)
        # 전체 X, y 데이터에서 랜덤하게 batch_size만큼 데이터 추출하여 sample_X, sample_y로 저장
        stochastic_random_index = np.random.permutation(X.shape[0])
        sample_X = X[stochastic_random_index[0:batch_size]]
        sample_y = y[stochastic_random_index[0:batch_size]]
        # 랜덤하게 batch_size만큼 추출된 데이터 기반으로 w1_update, w0_update 계산 후 업데이트
        w1_update, w0_update = get_weight_updates(w1, w0, sample_X, sample_y, learning_rate=0.01)
        w1 = w1 - w1_update
        w0 = w0 - w0_update
    
    return w1, w0
w1, w0 = stochastic_gradient_descent_steps(X, y, iters=1000)
print("w1:",round(w1[0,0],3),"w0:",round(w0[0,0],3))
y_pred = w1[0,0] * X + w0
print('Stochastic Gradient Descent Total Cost:{0:.4f}'.format(get_cost(y, y_pred)))
w1: 4.028 w0: 6.156
Stochastic Gradient Descent Total Cost:0.9937

Mini-Batch를 사용해도 Batch와 costcost에 큰 차이가 없음
실제로는 거의 Mini-Batch 방식만 사용함

profile
Statistics & Data Science

0개의 댓글