Python으로 regression 학습 구현하기
Machine Learning에서 가장 중요한 것 중 cost function과 theta update이 있다.
일례로, Deep learning으로 RMSE를 낮추는 prediction 모델링을 한다고 할 때 더 고급 알고리즘을 사용하거나, Batch normalization 기법을 적용하는 것 보다 learning rate나 threshold, batch size를 수정해보는 것이 훨씬 좋은 결과를 얻는 경우가 많다.
사실 해보지 않으면 잘 모른다. 해봐도 모르는 경우가 많다. 그래서 이 내용들을 제대로 이해하려면 한 번쯤 직접 구현해보는 것이 좋다고 생각했고, 파이썬으로 basic한 머신러닝 이론들을 구현하는 Implementation 프로젝트를 시작했다(github 링크). 본 포스팅에서는 간단한 python 코드를 이용하여 regression 학습 상황에서, 명시적 해와 Gradient descent를 이용한 근사적 해를 구해보았다.
''' Referred to 'Data science from scratch' ''' import numpy as np from numpy.linalg import inv from statsmodels.regression.linear_model import OLS # constant, friends, work hour, Acquired doctoral degree input = [[1,49,4,0],[1,41,9,0],[1,40,8,0],[1,25,6,0],[1,21,1,0],[1,21,0,0],[1,19,3,0],[1,19,0,0],[1,18,9,0],[1,18,8,0],[1,16,4,0],[1,15,3,0],[1,15,0,0],[1,15,2,0],[1,15,7,0],[1,14,0,0],[1,14,1,0],[1,13,1,0],[1,13,7,0],[1,13,4,0],[1,13,2,0],[1,12,5,0],[1,12,0,0],[1,11,9,0],[1,10,9,0],[1,10,1,0],[1,10,1,0],[1,10,7,0],[1,10,9,0],[1,10,1,0],[1,10,6,0],[1,10,6,0],[1,10,8,0],[1,10,10,0],[1,10,6,0],[1,10,0,0],[1,10,5,0],[1,10,3,0],[1,10,4,0],[1,9,9,0],[1,9,9,0],[1,9,0,0],[1,9,0,0],[1,9,6,0],[1,9,10,0],[1,9,8,0],[1,9,5,0],[1,9,2,0],[1,9,9,0],[1,9,10,0],[1,9,7,0],[1,9,2,0],[1,9,0,0],[1,9,4,0],[1,9,6,0],[1,9,4,0],[1,9,7,0],[1,8,3,0],[1,8,2,0],[1,8,4,0],[1,8,9,0],[1,8,2,0],[1,8,3,0],[1,8,5,0],[1,8,8,0],[1,8,0,0],[1,8,9,0],[1,8,10,0],[1,8,5,0],[1,8,5,0],[1,7,5,0],[1,7,5,0],[1,7,0,0],[1,7,2,0],[1,7,8,0],[1,7,10,0],[1,7,5,0],[1,7,3,0],[1,7,3,0],[1,7,6,0],[1,7,7,0],[1,7,7,0],[1,7,9,0],[1,7,3,0],[1,7,8,0],[1,6,4,0],[1,6,6,0],[1,6,4,0],[1,6,9,0],[1,6,0,0],[1,6,1,0],[1,6,4,0],[1,6,1,0],[1,6,0,0],[1,6,7,0],[1,6,0,0],[1,6,8,0],[1,6,4,0],[1,6,2,1],[1,6,1,1],[1,6,3,1],[1,6,6,1],[1,6,4,1],[1,6,4,1],[1,6,1,1],[1,6,3,1],[1,6,4,1],[1,5,1,1],[1,5,9,1],[1,5,4,1],[1,5,6,1],[1,5,4,1],[1,5,4,1],[1,5,10,1],[1,5,5,1],[1,5,2,1],[1,5,4,1],[1,5,4,1],[1,5,9,1],[1,5,3,1],[1,5,10,1],[1,5,2,1],[1,5,2,1],[1,5,9,1],[1,4,8,1],[1,4,6,1],[1,4,0,1],[1,4,10,1],[1,4,5,1],[1,4,10,1],[1,4,9,1],[1,4,1,1],[1,4,4,1],[1,4,4,1],[1,4,0,1],[1,4,3,1],[1,4,1,1],[1,4,3,1],[1,4,2,1],[1,4,4,1],[1,4,4,1],[1,4,8,1],[1,4,2,1],[1,4,4,1],[1,3,2,1],[1,3,6,1],[1,3,4,1],[1,3,7,1],[1,3,4,1],[1,3,1,1],[1,3,10,1],[1,3,3,1],[1,3,4,1],[1,3,7,1],[1,3,5,1],[1,3,6,1],[1,3,1,1],[1,3,6,1],[1,3,10,1],[1,3,2,1],[1,3,4,1],[1,3,2,1],[1,3,1,1],[1,3,5,1],[1,2,4,1],[1,2,2,1],[1,2,8,1],[1,2,3,1],[1,2,1,1],[1,2,9,1],[1,2,10,1],[1,2,9,1],[1,2,4,1],[1,2,5,1],[1,2,0,1],[1,2,9,1],[1,2,9,1],[1,2,0,1],[1,2,1,1],[1,2,1,1],[1,2,4,1],[1,1,0,1],[1,1,2,1],[1,1,2,1],[1,1,5,1],[1,1,3,1],[1,1,10,1],[1,1,6,1],[1,1,0,1],[1,1,8,1],[1,1,6,1],[1,1,4,1],[1,1,9,1],[1,1,9,1],[1,1,4,1],[1,1,2,1],[1,1,9,1],[1,1,0,1],[1,1,8,1],[1,1,6,1],[1,1,1,1],[1,1,1,1],[1,1,5,1]] x_data = np.array(input) # output hour output = [68.77,51.25,52.08,38.36,44.54,57.13,51.4,41.42,31.22,34.76,54.01,38.79,47.59,49.1,27.66,41.03,36.73,48.65,28.12,46.62,35.57,32.98,35,26.07,23.77,39.73,40.57,31.65,31.21,36.32,20.45,21.93,26.02,27.34,23.49,46.94,30.5,33.8,24.23,21.4,27.94,32.24,40.57,25.07,19.42,22.39,18.42,46.96,23.72,26.41,26.97,36.76,40.32,35.02,29.47,30.2,31,38.11,38.18,36.31,21.03,30.86,36.07,28.66,29.08,37.28,15.28,24.17,22.31,30.17,25.53,19.85,35.37,44.6,17.23,13.47,26.33,35.02,32.09,24.81,19.33,28.77,24.26,31.98,25.73,24.86,16.28,34.51,15.23,39.72,40.8,26.06,35.76,34.76,16.13,44.04,18.03,19.65,32.62,35.59,39.43,14.18,35.24,40.13,41.82,35.45,36.07,43.67,24.61,20.9,21.9,18.79,27.61,27.21,26.61,29.77,20.59,27.53,13.82,33.2,25,33.1,36.65,18.63,14.87,22.2,36.81,25.53,24.62,26.25,18.21,28.08,19.42,29.79,32.8,35.99,28.32,27.79,35.88,29.06,36.28,14.1,36.63,37.49,26.9,18.58,38.48,24.48,18.95,33.55,14.24,29.04,32.51,25.63,22.22,19,32.73,15.16,13.9,27.2,32.01,29.27,33,13.74,20.42,27.32,18.23,35.35,28.48,9.08,24.62,20.12,35.26,19.92,31.02,16.49,12.16,30.7,31.22,34.65,13.13,27.51,33.2,31.57,14.1,33.42,17.44,10.12,24.42,9.82,23.39,30.93,15.03,21.67,31.09,33.29,22.61,26.89,23.48,8.38,27.81,32.35,23.84] y_data = np.array(output)
먼저, 코딩에 사용될 데이터는 다음과 같다. output 이라는 연속형 y값이 있고, 4개의 피처로 구성된 input이라는 x 데이터가 있다.
x array는 [절편값, 피처1, 피처2, binary 피처3] 로 구성되어 있다.
이를 우선 명시적 해를 구하는 방법으로 구해보자.
1. 명시적 방법으로 해를 구하기
다중선형 회귀모델은 다음과 같은 행렬로 정의될 수 있다.
그리고 행렬 항에서 다중 회귀 분석의 계수 벡터를 계산하는 공식은 다음과 같다.
β = (X'X)-1X'y
이를 파이썬에서 간단하게 구현해 본 코드는 다음과 같다.
''' 명시적으로 해를 구하는 방법 y = βX+ϵ 의 선형 회귀를 한다고 할 때, β = ( X^(T) * X )^(-1) * X^(T) * y의 수식으로 명시적인 해를 구할 수 있음. ''' def explicit_linear_regression_solution(y_data=y_data, x_data=x_data): coeffs = inv(x_data.transpose().dot(x_data)).dot(x_data.transpose()).dot(y_data) print(coeffs)
이는 라이브러리로도 간단하게 구할 수 있다.
coeffs = OLS(y_data, x_data).fit().params
2. Gradient Descent 방법으로 해를 구하기
데이터의 volume이 커지는 경우, cost 함수를 정의해놓고 근사적으로 해를 찾아나가는 것이 더 효율적인 방법이 된다. 그 방법 중 가장 대표적인 GD를 구현해보았다. 자세한 GD에 대한 개념은 이 글에서는 생략하겠다. 코드를 보면 매우 이해하기 쉽다. 단지 한 가지 생각해보아야 하는 점은, 학습되는 계수의 미분값이 transpose X와 오차값의 dot / 데이터 개수 라는 점이다.
''' GD를 이용하여 해를 근사적으로 추정 ''' class GradientDescent(): def __init__(self, learning_rate=0.01, threshold=0.01, max_iterations=1000): self._learning_rate = learning_rate self._threshold = threshold self._max_iterations = max_iterations self._W = None def fit(self, x_data, y_data): num_examples, num_features = np.shape(x_data) self._W = np.ones(num_features) x_data_transposed = x_data.transpose() for i in range(self._max_iterations): # 실제값과 예측값의 차이 diff = np.dot(x_data, self._W) - y_data # diff를 이용하여 cost 생성 : 오차의 제곱합 / 2 * 데이터 개수 cost = np.sum(diff ** 2) / (2 * num_examples) # transposed X * cost / n gradient = np.dot(x_data_transposed, diff) / num_examples # W벡터 업데이트 self._W = self._W - self._learning_rate * gradient # 판정 임계값에 다다르면 학습 중단 if cost < self._threshold: return self._W return self._W
추가적으로, Data를 scaling 한 뒤에 GD를 하는 것이 얼마나 효율적인지 추가적으로 검증해 보는 것도 좋은 공부가 된다.
위의 두 가지 방법과 scaling된 피처를 GD로 학습되는 것 까지의 전체 코드는 아래 깃헙 링크를 참고.
https://github.com/yoonkt200/ml-theory-python/blob/master/01-regression/
참고 블로그 및 사이트
1) https://wikidocs.net/7639
2) https://support.minitab.com/ko-kr/minitab/18/help-and-how-to/modeling-statistics/regression/how-to/fit-regression-model/methods-and-formulas/coefficients/
3) https://datascienceplus.com/linear-regression-from-scratch-in-python/
4) https://ratsgo.github.io/machine%20learning/2017/07/03/regression/