들어가며

이전 글에서는 샘플과 결괏값 사이의 관계를 1차식으로 구하는 선형 회귀에 대해서 알아보았습니다. 선형회귀만으로는 2차 이상의 관계를 구할 수 없으므로 이번에는 다항관계를 구하는 다항회귀에 대해서 알아보도록 하겠습니다.

목차

  1. 정의
  2. 코드 구현
  3. 다항회귀의 특징

1.1정의

앞서 말했듯 다항회귀는 2차 이상의 방정식을 통한 관계 구현입니다. 이를 구하기 위해서는 기존 특성의 거듭제곱을 구하고 이를 새로운 특성으로서 샘플에 추가한 후 선형회귀(LinearRegression)를 구하는 것입니다. 

이렇게 한 경우 x^2과 x^1을 서로 다른 특성으로 인식하여 선형회귀는 두 개의 특성에 맞는 결과를 구하기 위해 정규방정시을 사용합니다. 코드구현을 통해서 쉽게 알아보도록 하겠습니다.

 

1.2 코드구현 

1. 2차방정식 형태의 데이터 생성

import numpy as np
import numpy.random as rand

np.random.seed(42)

m = 100
X = 6 * np.random.rand(m, 1) - 3
y = 0.5 * X**2 + X + 2 + np.random.randn(m, 1)

plt.plot(X, y, "b.")
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.axis([-3, 3, 0, 10])
save_fig("quadratic_data_plot")
plt.show()

2. PolynomialFeatures모듈을 사용하여 거듭제곱 형태의 특성 추가

from sklearn.preprocessing import PolynomialFeatures
poly_features = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly_features.fit_transform(X)
X[0] 
#result = array([-0.75275929])

X_poly[0]
#result = array([-0.75275929,  0.56664654])

3. LinearRegression모듈을 통해서 선형관계 분석

lin_reg = LinearRegression()
lin_reg.fit(X_poly, y)
lin_reg.intercept_, lin_reg.coef_

#resutl = (array([1.78134581]), array([[0.93366893, 0.56456263]]))

4. 최종 결과 

1.3 다항회귀의 특징

PolynomialFeatures 모듈에 데이터를 입력할 때 두 개 이상의 데이터를 입력할 경우(x, y, z ... ) 이 모듈은 각 데이터만의 거듭제곱이 아니라 서로 다른 데이터들의 교차항(xy, x^2 y, xy^2 ... )을 추가하므로 특성 사이의 관계를 파악하는데 용이합니다.

하지만 입력 데이터의 수와 거듭제곱(degree)의 수가 증가할 수록 교차항이 기하급수적으로 증가하여 학습 시간 또한 기하급수적으로 증가하므로 이를 유의해야합니다.

목차

  • 들어가며
  • 선형회귀의 정의
  • 파라미터 계산
    • 정규방정식
      • 정규방정식의 장단점
    • 경사하강법
      • 정의
      • 학습률로인한 장단점
      • 배치 경사 하강법 
      • 확률적 경사 하강법 
      • 미니배치 경사 하강법 

 

들어가며

이번 글에서는 머신러닝의 모델 중 하나인 선형회귀의 정의와 모델의 파라미터를 최적화하는 방법들에 대해서 설명하도록 하겠습니다.

 

선형회귀의 정의

선형회귀란 주어진 데이터( x는 샘플, y는 예측값)들을 사용해서 샘플과 예측값과의 관계를 직선으로 표현하는것을 의미합니다. 직선의 방정식을 구하기 위해서는 하나의 샘플에서 각 특성에 곱할 기울기들과 편향이 필요합니다. 우리는 주어진 데이터들을 통해서 해석적으로 또는 경사하강법같은 계산을 통해서 파라미터(기울기, 편향)을 파악할 수 있습니다.

 

파라미터 계산

1. 정규방정식

선형회귀는 최적화된 파라미터를 공식을 통해서 해석적으로 구할 수 있습니다. 이를 정규방정식이라고 부릅니다.

파이썬으로 이를 구현해보겠습니다.

 

1.1. 선형으로 보이는 데이터를 생성합니다.

https://colab.research.google.com/drive/1cf_cY1zLCwaKzp73BuoYKZqfhHEtu7ga#scrollTo=txyqCXxO5-tp&line=6&uniqifier=1

 

Google Colaboratory

 

colab.research.google.com

1.2. 정규방정식으로 파라미터를 계산합니다. 

[4,3]의 결괏값을 원했지만 가우시안 노이즈 때문에 살짝 오차가 존재합니다.

1.3. 모델의 예측을 그래프에 나타내보겠습니다.

1.4. 동일한 작업을 사이킷런 라이브러리를 통해 작업합니다.

정규방정식의 장단점

우선 정규방정식의 장점은 선형회귀에 사용되는 여러방법중 가장 정확하게 파라미터를 계산할 수 있다는 것입니다. 이는 해석적으로 계산하기에 당연한 결과입니다.

단점은 이를 계산하기 위한 컴퓨팅자원의 한계입니다.

1. 정규방정식으로 계산을 하기 위해서는 램에 모든 샘플들을 올린 다음 계산할 필요가 있습니다. 만약 샘플의 크기가 매우 크기에 램이 부족하다면 이런 방법은 어려움이 있습니다.

2. 시간복잡도의 기하학적 증가

정규방정식의 시간복잡도는 O(n^2.4) ~ O(n^3) [ n : 행렬의 크기 ] 정도로 샘플의 행렬이 증가할 경우 기하학적으로 계산시간이 증가하게 됩니다. 

 

따라서 소규모의 샘플 계산인 경우 정규방정식을 사용하는 것이 좋지만 자원의 한계에 부딪힐 정도로 샘플의 수가 많은 경우 다른 방법을 사용하는 것이 좋습니다.

2. 경사하강법( Gradinet Descent)
2.1.정의

경사하강법은 비용함수를 최소화하기 위해 비용함수의 편도함수를 이용해서 파라미터를 조정하는것입니다.

위 정의를 하나씩 풀어 봅시다. 우선 비용함수는 쉽게 말해서 예측값과 실제 정답의 차이를 의미하는 함수를 의미합니다. 이 함수는 여러종류가 있지만 여기서는 MSE(평균 제곱 오차,Mean Squared Error)로 다루어보도록 하겠습니다.

위 식은 각 샘플의 예측값과 정답의 차이를 제곱하여 평균을 나타내는 것을 의미합니다. 

즉 비용함수(손실함수)가 낮을 수록 예측이 정확하다는 것을 의미합니다. 비용함수를 낮추기 위해서는 함수의 편도함수를 구하여 기울기의 방향을 파악하고 비용함수가 낮아지는 방향으로 파라미터를 수정하면됩니다.

좋습니다. 비용함수의 편도함수로 파라미터를 조정할 방향을 알아보았습니다. 이번에는 방향을 알았으니 얼마나 움직여야할지 크기(학습률)에 대해서 알아보겠습니다.

2.2학습률로 인한 장단점

우선 학습률이 작을 때 입니다.

학습률이 작을 경우 매번 스텝마다 매우 작게 움직이기 때문에 파라미터가 최적화되기에 오랜시간이 소요됩니다.

 

이번에는 학습률이 평균보다 클 때입니다.

학습률이 매우 큰 경우 비용함수의 최소점에 수렴하지 못하고 지그재그로 이동하는 모습을 볼 수 있습니다. 

경사하강법의 문제점

학습률에 상관없이 경사하강법에는 비용함수가 볼록,오목함수가 아닌 경우에 전역 극소값이 아니라 지역 극솟값에만 머무르게 되어 학습이 정체되는 현상이 발생할 수 있습니다. 이러한 경우는 모멘텀 기반 방식으로 해결할 수 있습니다.
2.3 배치 경사 하강법

배치 경사 하강법은 경사 하강법의 가장 일반적인 구조로 한번 비용함수의 기울기를 계산할 때마다 모든 샘플을 사용하는것을 의미합니다.  이를 구현하기 위해서 모든 샘플에 대한 MSE의 편도함수를 우선 구해봅시다.

편도함수는 해석적으로 풀이할 경우 두 번째 공식을 사용하여 구할 수 있습니다. ( 배치 경사하강법 이므로 모든 샘플 데이터를 사용합니다.)

 

이를 코드로 구현해봅시다.

eta = 0.1  # 학습률
n_iterations = 1000 #반복횟수
m = 100 # 샘플의 갯수

weight = np.random.randn(2, 1)  # 초기 랜덤 파라미터 설정

for step in range(n_iterations): 
    gradients = 2/m * X_b.T.dot(X_b.dot(weight) - y) #기울기 계산
    weight = weight - eta * gradients # 학습률의 크기만큼 기울기 방향으로 이동
     
     #파라미터의 변화 파악
    if (step+1) % 200 == 0:
        print('Step :{:04d}, weight = \n {}'.format(step+1, weight))
    
print('최종 결과값 : \n{}'.format(weight))

약 1000번의 반복을 통해서 정규방정식으로 구한 파라미터의 최적화에 근사한 값을 도출했습니다. 반복횟수를 조절해서 시간의 소요를 조절할 수 있지만 한번의 계산에 모든 샘플을 사용하기에 이 또한 정규방정식처럼 램 용량을 초과할 수 있습니다. 이번에는 이를 해결하기 위해 확률적 경사 하강법에 대해서 알아보도록 하겠습니다.

2.4 확률적 경사하강법

확률적 경사 하강법(Stochastic Gradient Descent)은 매 스텝에서 랜덤하게 하나의 데이터(샘플)을 선택해 Gradient Descent(GD)를 계산하는 방식입니다. 이 방식은 배치 경사하강법에 비해 불안정하게 최적값으로 수렴하지만 시간과 사용되는 램용량을 단축할 수 있습니다. 

 

불안정하게 수렴할 경우 local minimum에서 탈출할 가능성이 있지만 반대로 global minimum에는 도착하지 못할 가능성이 있습니다. 이를 해결하기 위해서 SGD에서는 학습률을 점진적으로 감소시키는 Learning Rate Decay(learning rate schedule)기법을 사용합니다.

Learning Rate Decay기법

학습을 시작할 떄는 학습률을 크게 설정하고 점진적으로 이를 줄여 전역 최솟값에 도달하는 방식입니다.

 

이를 코드로 구현해보도록 하겠습니다.

 

n_epochs = 50
t0, t1 = 5, 50

def learning_schedule(t):
    return t0 / (t + t1)

weight = np.random.randn(2, 1)  # random init

for epoch in range(n_epochs):
    for i in range(m):
        if epoch == 0 and i < 20:
            y_predict = X_new_b.dot(weight)
            style = 'b-' if i > 0 else 'r--'
            plt.plot(X_new, y_predict, style)
            
        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(weight) - yi)
        eta - learning_schedule(epoch * m + i)
        weight = weight - eta * gradients
        weight_path_sgd.append(weight)
        
    if (epoch+1) % 10 == 0:
        print('Epoch :{:03d}, weight = \n {}'.format(epoch+1, weight))
        
        
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()

에폭마다 학습률을 점진적으로 낮추면서 진행한 결과 파라미터가 최적값으로 수렴하는 것을 확인 할 수 있습니다.

SGD를 이용한 Regressor는 사이킷런에서 모듈을 지원하므로 이를 사용해 간단하게 계산할 수 있습니다.

from sklearn.linear_model import SGDRegressor

sgd_reg = SGDRegressor(max_iter=50, penalty=None, eta0=0.1, random_state=42)
sgd_reg.fit(X, y.ravel())
print(sgd_reg.intercept_, sgd_reg.coef_)
2.5 미니배치 경사하강법

미니배치 경사 하강법(Mini-batch Gradient Descent)은 각 스텝에서 전체 Train Set을 미니배치(mini-batch), 즉 작은 데이터셋을 추출한 뒤 Gradient를 계산하는 방법입니다. 

미니배치 경사 하강법은 SGD에 비해 덜 불규칙하게 감소하지만, local minimum에 빠질 확률은 높은 경우가 있습니다.

 

최종비교

위에서 설명햇듯이 3가지 방식을 비교해보면 배치경사하강법은 매번 파라미터 갱신 때마다 전체 샘플을 계산하므로 안정적으로 수렴하는 반면 SGD는 하나씩만 사용하기에 불안정한 모습을 볼 수 있습니다. 그리고 갱신때마다 일부 샘플만을 사용하는 미니배치의 경우에는 SGD보다는 좀 더 안정적으로 수렴하는 모습을 볼 수 있습니다.

들어가며

지난 글에서는 모델의 정확도를 올리기 위해서 최종적으로 하이퍼파라미터 조합을 탐색하는 그리드 탐색에 대해서 진행하고 최상의 하이퍼파라미터 조합을 통해 최적화된 모델을 구현해봤습니다. 이번 글에서는 최상의 모델을 분석하여 모델의 문제를 분석하고 이를 해결하기 위한 통찰을 얻고 처음에 분할 한 테스트 세트로 모델을 평가해보도록 하겠습니다.

최상의 모델과 오차 분석

각 특성의 중요도 파악

최상의 모델을 분석하면 문제에 대한 통찰을 얻을 수 있습니다. 예를들어서 주택가격을 구하기 위한 특성을 중요도를 파악할 수 있습니다.

분석을 통한 통찰

중요도를 통해서 우리는 다시 특성을 정제할 수 있습니다. 위 결과를 보니 ocena_proximity의 경우 하나의 범주를 제외하고는 별로 중요하지 않으므로 이를 제외 할 수 있습니다.

시스템이 오차를 만들었다면 이를 해결하기 위한 방법을 찾아야합니다.(특성 추가, 제거, 이상치 제거 등)

테스트 세트로 시스템 평가하기

이제 어느정도 모델 세부 튜닝이 끝났으니 초반에 나누었던 테스트 세트로 평가를 진행해봅시다. 테스트 세트를 full_pipeline을 통해서 데이터를 정제하고 만들어둔 최상의 모델에서 predict 메소드를 실행하여 예측값을 알아낼 수 있습니다. 그리고 MSE를 통해서 평가해보도록 합시다.

평가 값을 보니 훈련 데이터로 평가했을 때보다 오차가 크다는 것을 알고 있습니다. 아직 모델이 일반화가 덜 진행되었다는 것을 알 수 있습니다. 사실 이러한 결과는 이번 실습에서 사용한 데이터가 충분하지 못하다는 이유가 가장 큽니다. 그러나 데이터를 수집하는 것도 중요하지만 기존의 데이터를 정제하여 최상의 결과를 도출하는 방법을 탐구하는 개발자의 역량도 중요합니다. 

마치며

지금까지 머신러닝 모델을 구축하는 과정에 대해서 알아보았습니다. 앞으로 해야 할 것은 최종 모델을 분석하여 오차를 파악하고 다시 데이터를 정제하는 단계를 반복하면서 데이터 파악과 모델의 정확성을 향상시키는 일입니다.

이제 시스템 론칭 직전 단계까지 왔습니다. 학습한 것, 학습하지 않은것, 수립한 가정, 제한사항등을 강조하면서 솔루션과 문서를 출시하고 깔끔한 도표와 기억하기 쉬운 제목으로 멋진 발표자료를 만들어야합니다. 

 

들어가며

지금까지 모델의 정확도를 높이기 위해서 데이터를 정제하고 여러 가지 모델을 테스트를 해보았습니다. 이번에는 데이터 정제가 끝나고 모델 또한 확정되었을 때 마지막으로 정확도를 올리기 위한 시도인 모델 세부 튜닝에 대해서 설명해보도록 하겠습니다.

모델 세부 튜닝 (그리드 탐색)

세부 튜닝을 한다는 의미는 각 추정기와 변환기의 하이퍼 파라미터를 변경시켜 테스트한다는 의미입니다. 이것을 그리드 탐색이라고 지칭합니다. 그리드 탐색을 수동으로 조정하는 것은 매우 지루한 작업이기 때문에 이를 자동화시켜줄 GridSearchCV를 사용해봅시다.

원하는 하이퍼파라미터의 조합을 인자로 넣어서 실행할 경우 자동으로 가장 최적화된 하이퍼 파라미터의 조합을 출력해줍니다.

사용법

 GridSearchCV를 사용하면 위 처럼 하이퍼 파라미터가 최적화된 추정 기를 출력하고 각 파라미터에 따른 점수 또한 확인할 수 있습니다. 위 하이퍼 파라미터 조합 점수들을 확인하니 max_features와 n_estimators가 클수록 평균 오차가 작아 모델의 정확성이 좋을 것을 확인할 수 있습니다.

랜덤 탐색

그리드 탐색은 조합이 비교적 적어서 보유한 컴퓨팅 자원으로 충분히 실행가능한 경우에 사용됩니다. 만약에 조합이 너무 커서 그리드 탐색을 하기에는 현실적으로 불가능할 경우 어떻게 해야 할까요? 이를 위해서 RandomizedSearchCV를 이용한 랜덤 탐색을 사용할 수 있습니다. 랜덤 탐색은 각 반복마다 하이퍼 파라미터에 다른 임의의 수를 대입하여 지정한 횟수만큼 평가합니다. 랜덤 탐색의 장점은 두 가지입니다.

  1. 랜덤 탐색은 1000회를 입력할 경우 각 하이퍼파라미터에 1000개의 서로 다른 값을 입력합니다. 그리드 탐색으로 1000개의 조합을 탐색하려면 임의로 1000개의 값을 설정해야하지만 랜덤탐색은 자동으로 진행합니다.
  2. 반복 횟수를 조 잘하는 것만으로 컴퓨팅 자원의 소비를 제어할 수 있습니다.

+ Recent posts