데이터분석

Linear Regression Gradient Descent

블쭌 2020. 12. 30. 23:31
728x90

선형회귀 표현식

  • Y는 종속변수 값
  • θ₀ 는 bias
  • θ₁,…,θₙ 회귀계수
  • x₁, x₂,…,xₙ 는 독립변수

위의 식을 아래와 같이 벡터로 표현이 가능하다.

  • θ는 model의 파라미터 벡터
  • x는 Xo=1인 입력 벡터
# data 생성
np.random.seed(321)
x_old = np.random.rand(1000, 1)

# intercept항 1추가
y = 2 + 5 * x_old + np.random.rand(1000, 1)

# plot
plt.scatter(x_old, y, s=10)
plt.xlabel('x')
plt.ylabel('y')
plt.ylim(2, 8)
plt.show()

# intercept 추가
x = np.c_[np.ones(x_old.shape[0]), x_old]

비용 함수(Cost Function)

예측값에서 실제값을 뺀 잔차의 제곱의 평균을 사용한다. 제곱을 사용하는 이유는 잔차(예측값 - 실제값)의 차이가 클수록 더 많은 Penalty를 부여하기 위함이다. 추가적으로 1/2을 곱해주는 이유는 제곱을 미분할때 사라지기 위함이다.

residuals = y_pred - y

cost = np.sum(residuals ** 2) / (2 * m)

경사하강법 (Gradient Descent)

비용함수를 각 세타에 대해서 편미분을 진행한 결과는 다음과 같다.

미분에 대해서 잘 모르는 사람을 위해서 부족하지만 θ₁에 대한 편미분 예시입니다.

이해가 안되시는분들은 합성곱 미분에 대해서 구글에 검색후 찾아보세요!!!

결국 아래와 같이 Gradient 벡터를 한번에 표현하게 됩니다.

# h(x)
y_pred = np.dot(x, self.w_)

# h(x) - y
residuals = y_pred - y

# x_T * (h(x) - y)
gradient_vector = np.dot(x.T, residuals)

gradient vector를 가지고 모델 파라미터를 업데이트 해준다.

  • a는 learning rate

이 또한 아래와 같이 벡터로 한번에 표현하게 된다.

learning rate를 잘 설정해줘야 하는 이유를 아래 그림을 통해서 보시면 알 수 있습니다.

learning rate가 낮으면 천천히 내려가기 때문에 데이터가 많아지면 많은 시간이 걸릴수도 있고 원하는 global minimum에 도달하지 못한채 학습이 끝나버릴 수 있습니다.

반대로 learning rate가 너무 높으면 계속 왔다갔다 진행하기 때문에 제대로 global minimum에 수렴하지 못할 수 있습니다.

learning rate가 낮을 때
learning rate가 높을 때

self.w_ -= (self.lr / m) * gradient_vector

최종결과


Code

class LinearRegressionGD:
    def __init__(self, lr=0.001, n_iter=30000):
        self.lr = lr
        self.n_iter = n_iter
        
    def fit(self, x, y):
        self.cost_ = []
        self.w_ = np.zeros((x.shape[1], 1))
        
        m = x.shape[0]
        
        for i in range(self.n_iter):
            y_pred = np.dot(x, self.w_)
            
            residuals = y_pred - y
            
            gradient_vector = np.dot(x.T, residuals)

            self.w_ -= (self.lr / m) * gradient_vector
            
            cost = np.sum(residuals ** 2) / (2 * m)
            
            self.cost_.append(cost)
            
            if i % 2000 == 0:
                self.draw(x, y)
            
        return self
    
    
    def predict(self, x):
        return np.dot(x, self.w_)
    
    
    def draw(self, x, y):
        y_predicted = np.dot(x, self.w_)
        plt.scatter(x[:,1], y, s=8)
        plt.xlabel('x')
        plt.ylabel('y')
        plt.plot(x[:,1], y_predicted, color='b')

        plt.show()
728x90