ai-creator

[머신러닝 기초] 지도학습 - 선형 회귀(Regression) 분석 본문

데이터 분석/왕초보를 위한 머신러닝

[머신러닝 기초] 지도학습 - 선형 회귀(Regression) 분석

ai-creator 2021. 10. 19. 14:57
반응형

ㅁ 선형 회귀란?

지도학습은 크게 2가지 유형으로 볼 수 있습니다.

1) 분류

2) 회귀

 

두가지 기법의 가장 큰 차이는 다음과 같다.

1) 분류 : 예측값이 이산형 클래스 값

2) 회귀 : 예측값이 연속형 숫자 값

 

데이터를 가장 잘 설명하는 최적의 회귀식를 찾는다.

회귀는 여러개의 독립변수(x)와 한개의 종속변수(y)의 상관관계를 모델링하는 기법을 통칭한다.

w0, w1, w2.... 를 회귀계수(regression coefficients)라고하며, 회귀 예측의 핵습은 주어진 피쳐(x, input, 독립변수)와 결정값(y, output, 종속변수)값 기반에서 학습을 통해 최적의 회귀계수를 찾아내는 것이다.

독립변수 1개 단일 회귀
독립변수 여러개 다중 회귀
회귀계수의 결합 선형 선형회귀
회귀계수의 결합 비선형 비선형 회귀

단일 회귀 vs 다중회귀

 

ㅁ 단순 선형 회귀를 통한 회귀 이해

단순 선형 회귀는 

- 독립변수 1개

- 종속변수 1개

인 선형 회귀 이다.

독립변수가 1개인 단순 선형 회귀에서는 기울기 w1과 절편 w0를 회귀계수로 칭하빈다. (절편은 영어로 intercept)

 

"데이터를 가장 잘 설명하는"이라는 의미는 "실제값과 예측차이가 작은" 으로 표현할 수 있습니다.

즉, 최적의 회귀 모델을 만든다는 것? "전체 데이터의 잔차(오류값) 합이 최소가 되는 모델을 만든다" 라는 의미입니다.

 

 

잔차(오류값) 합을 구하는 방법은 2가지를 사용하는데,

1) MAE (Mean Absolute Error) : 절대값을 취하는 방법 

2) RSS  (Residual Sum of Square) : 제곱을 취하는 방법

일반적으로 미분등의 계산이 편리하여 RSS를 사용한다.

다양한 표기법

RSS를 최소로하는 w0, w1, 즉 회귀계수를 학습을 통해서 찾는 것이 머신러닝 기반 회귀의 핵심사항이다. 

 

회귀에서는 이 RSS는 비용(Cost)이며, w변수(회귀계수)로 구성된 RSS를 비용함수(cost function, loss function, 손실함수)라고 한다. 머신러닝 회귀 알고리즘은 데이터를 계속 학습하면서 이 비용함수가 반환되는 값(=오류값)을 지속해서 감소시키고, 더이상 감소하지 않는 최소의 오류값을 구하는 것이다. 

 

ㅁ 비용 최소화 하기 (경사하강법, Gradient Descent)

어떻게 비용함수가 최소가 되는 W 파라미터를 구할 수 있을까?

 

예시로 x = [1,2,3] 이라고 하고, y=[3,5,7]의 관측값이 있다고 했을때 우리는 f(x) = 2x+1 이라고 생각할 수 있다. 하지만, 기계는 이렇게 계산을 할 수 없다. 초기화된 값을 기준으로 점점 오류가 작아지는 방향으로 f(x)식을 완성해 갑니다.

오류값과 관련된 RSS를 비용함수(cost function, loss)이라고 하며, 비용함수가 최소화할 수 있는 회귀계수(w)를 찾는다(=최적화합니다). 즉, '점진적으로' 반복적인 계산을 통해 W파라메터 값을 업데이트 하면서 오류값이 최소가 되는 W파라메터를 구하는 것이다.

 

경사하강법(Gradient Descent)을 통해 최적의 W를 찾아낸다.

 

이 동작은 마치 깜깜한 밤, 산 정상에서 내려가야 한다고 가정했을 때, 여러분들이 하는 일과 비슷하게 동작한다.

앞이 보이지 않기 때문에 스스로에게 질문을 던질 것이다.

Step1) 다 내려왔습니까? 

Step2) 어느 방향으로 가야 하나요? 아래로 내려가세요

Step3) 얼만큼 가야 하나요?  답을 말해줄 수 없음, 즉, 적당히 가세요 

Step4) 조금 움직인다.

를 반복적으로 수행할 것이다.

 

경사하강법도 최적의 W를 찾기 위해 아래 행위를 반복하게 된다.

Step1) 다 내려왔습니까?  = Gradient가 0입니까? => YES / NO

Step2) 어느 방향으로 가야 하나요? = 아래로 내려가세요

Step3) 얼만큼 가야 하나요? 답을 말해줄 수 없음, 즉, 적당히 가세요 = learning rate

Step4) 조금 움직인다. = W를 업데이트 한다.

 

 

 

<<경사하강법 상세>>

더보기

 

출처 :&nbsp;https://www.youtube.com/watch?v=ve6gtpZV83E
출처 :&nbsp;https://www.youtube.com/watch?v=ve6gtpZV83E

 

<미분 기초>

<편미분이란?>

다변수 함수의 특정 변수를 제외한 나머지 변수를 상수로 생각하여 미분하는 것이다. 

 

출처 :&nbsp;https://www.youtube.com/watch?v=ve6gtpZV83E

 

ㅁ 평가지표

평가지표 설명 수식
MAE Mean Absoulute Error(MAE)이며 실제값과 예측값의 치이를 절댓값으로 변환해 평균한 것
MSE Mean Squared Error(MSE)이며 실제값과 예측값의 차이를 제곱으로 평균한 것
MSLE Mean Squared Log Error(MSLE)이며, MSE에 로그를 적용한 것이다. 결정값이 클수록 오류값도 커지기 때문에 일부 큰 오류값들로 인해 전체 오류값이 커지는 것을 막아줌

ex) 주택가격
log(MSE)
RMSE MSE값은 오류의 제곱을 구하므로 실제 오류 평균보다 더 커지는 특성이 있음.
RMSE(Root Mean Squared Error)는 MSE에 루트를 씌운 것
RMSLE Root Mean Squared Log Error(RMSLE)이며, RMSE에 로그를 적용한 것이다. 결정값이 클수록 오류값도 커지기 때문에 일부 큰 오류값들로 인해 전체 오류값이 커지는 것을 막아줌

ex) 주택가격
log(RMSE)
R^2 분산 기반으로 예측 성능을 평가
실제 값의 분산 대비 예측값의 분산 비율(1에 가까울수록 예측 정확도 높음)

<<참고. R^2, 결정계수>>

ㅁ Sklearn API

이를 위해 sklearn에서는 API를 지원한다.

목적 import API
LinearRegression from sklearn.linear_model import LinearRegression LinearRegression()
- coef_
- intercept_
MAE from sklearn.metrics import mean_absolute_error mean_absolute_error()
MSE from sklearn.metrics import mean_squared_error mean_squared_error()
RMSE ** 제공하지 않음
MSE에 제곱근을 씌워서 계산하는 함수를 직접 만들어야 한다.
 
R^2 from sklearn.metrics import r2_score r2_score()

참고) R^2 결정계수

더보기

R^2 결정계수

출처 : https://www.youtube.com/watch?v=ClKeKeNz7RM

 

코드에서 보면, 

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error , r2_score

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from scipy import stats
from sklearn.datasets import load_boston
%matplotlib inline

# boston 데이타셋 로드
boston = load_boston()

# boston 데이타셋 DataFrame 변환 
bostonDF = pd.DataFrame(boston.data , columns = boston.feature_names)

# boston dataset의 target array는 주택 가격임. 이를 PRICE 컬럼으로 DataFrame에 추가함. 
bostonDF['PRICE'] = boston.target
print('Boston 데이타셋 크기 :',bostonDF.shape)
bostonDF.head()

y_target = bostonDF['PRICE']
X_data = bostonDF.drop(['PRICE'],axis=1,inplace=False)

X_train , X_test , y_train , y_test = train_test_split(X_data , y_target ,test_size=0.3, random_state=156)

# Linear Regression OLS로 학습/예측/평가 수행. 
lr = LinearRegression()
lr.fit(X_train ,y_train )
y_preds = lr.predict(X_test)
mse = mean_squared_error(y_test, y_preds)
rmse = np.sqrt(mse)

print('MSE : {0:.3f} , RMSE : {1:.3F}'.format(mse , rmse))
print('Variance score : {0:.3f}'.format(r2_score(y_test, y_preds)))

print('절편 값:',lr.intercept_)
print('회귀 계수값:', np.round(lr.coef_, 1))

결과값

MSE : 17.297 , RMSE : 4.159

Variance score : 0.757

절편 값: 40.995595172164336

회귀 계수값: [ -0.1 0.1 0. 3. -19.8 3.4 0. -1.7 0.4 -0. -0.9 0. -0.6]

ㅁ Regularization = 정규화 = 제약 (penalty)

** 자세한 내용은 하단의 "부록"을 참고 **

 

현실 데이터는 사람이 어찌할 수 없는 noise가 항상 존재하고 있다. 

 

그러나, 잔차의 경우는 train data에 대해서만 고려한 오류입니다. train data에 해당하는 잔차를 줄이기 위해서는 복잡한 회귀식이 도출되면 됩니다. 그러나, 복잡한 회귀식으로 train data에 과적합되면 될수록 실제데이터에서는 예측력이 떨어지는 현상이 발생하게 된다. 

 

Overfitting 은 학습데이터에 너무 최적화되어 W 값이 잡히고, 이후 학습 데이터가 아닌 새로운 데이터에는 올바른 값을 내보내지 못하는 현상을 의미한다. 즉, 현재 주어진 데이터(학습데이터)에 너무 과적합되어 모델(W) 이 피팅되었으니, 이를 좀 덜 적합하게하고, 이후 새로운 데이터에도 일반적으로 들어맞는 모델을 만들어야 한다. 이 때, 과적합이 아닌 일반성을 띄게 해주는 기법을 Regularization 이라고 한다.

 

즉, 오버피팅을 줄기위해 약간의 규제(penalty)를 주는 방법을 고민하게 되었다. 우리가 조정할 수 있는 것은 W값, 즉 각 x에 대한 계수 값이다.

  • Regularization의 목적은 반복적으로 발생하는 cost에 영향을 미치는 w(weight)가 보다 적은 영향을 미치도록 조정해주는 역할을 한다. Regularization에는 자주 비교되는 L1 Regularization과 L2 Regularization가 있으며, L1 Regularization보다는 L2 Regularization를 일반적으로 더 많이 사용함

 

1) L1 Regularization

- W의 절대값에 패널티를 부여하는 방식

- 영향력이 크지 않는 회귀계수값을 0으로 변환 = feature selection 기능

- Lasso 회귀

- alpha는 하이퍼파라메터

 

참고) L1 Norm

 

2) L2 Regularization

- W의 제곱에 패널티를 부여하는 방식

- 회귀계수의 크기를 감소시킴

- Ridge 회귀

참고) L2 Norm

3) L1+L2 Regularization

- L1과 L2의 패널티를 섞음

- ElasticNet 회귀

 

Lasso / Lidge / ElasticNet의 회귀계수 변화

4) Sklearn API

이를 위해 sklearn에서는 API를 지원한다.

목적 import API
Lasso from sklearn.linear_model import Lasso Lasso()
- coef_
- intercept_
Ridge from sklearn.linear_model import Ridge Ridge()
ElasticNet from sklearn.linear_model import ElasticNet ElasticNet()
-  l1_ratio == 0 이면, L2 규제와 동일
-  l1_ratio == 1 이면, L1 규제와 동일

코드에서 보면, 

# alpha값에 따른 회귀 모델의 폴드 평균 RMSE를 출력하고 회귀 계수값들을 DataFrame으로 반환 
def get_linear_reg_eval(model_name, params=None, X_data_n=None, y_target_n=None, 
                        verbose=True, return_coeff=True):
    coeff_df = pd.DataFrame()
    if verbose : print('####### ', model_name , '#######')
    for param in params:
        if model_name =='Ridge': model = Ridge(alpha=param)
        elif model_name =='Lasso': model = Lasso(alpha=param)
        elif model_name =='ElasticNet': model = ElasticNet(alpha=param, l1_ratio=0.7)
        neg_mse_scores = cross_val_score(model, X_data_n, 
                                             y_target_n, scoring="neg_mean_squared_error", cv = 5)
        avg_rmse = np.mean(np.sqrt(-1 * neg_mse_scores))
        print('alpha {0}일 때 5 폴드 세트의 평균 RMSE: {1:.3f} '.format(param, avg_rmse))
        # cross_val_score는 evaluation metric만 반환하므로 모델을 다시 학습하여 회귀 계수 추출
        
        model.fit(X_data_n , y_target_n)
        if return_coeff:
            # alpha에 따른 피처별 회귀 계수를 Series로 변환하고 이를 DataFrame의 컬럼으로 추가. 
            coeff = pd.Series(data=model.coef_ , index=X_data_n.columns )
            colname='alpha:'+str(param)
            coeff_df[colname] = coeff
    
    return coeff_df
# end of get_linear_regre_eval

# 라쏘에 사용될 alpha 파라미터의 값들을 정의하고 get_linear_reg_eval() 함수 호출
lasso_alphas = [ 0.07, 0.1, 0.5, 1, 3]
coeff_lasso_df =get_linear_reg_eval('Lasso', params=lasso_alphas, X_data_n=X_data, y_target_n=y_target)

ㅁ 그외 Sklearn API 에서 제공하는 선형 회귀 알고리즘

목적 import API
의사결정나무 from sklearn.tree import DecisionTreeRegressor
DecisionTreeRegressor()
랜덤포레스트 from sklearn.ensemble import RandomForestRegressor RandomForestRegressor()
XGBoost from xgboost import XGBRegressor XGBRegressor()
LightGBM from lightgbm import LGBMRegressor LGBMRegressor()

코드에서 보면, 

from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import GradientBoostingRegressor
from xgboost import XGBRegressor
from lightgbm import LGBMRegressor
from sklearn.model_selection import cross_val_score

def get_model_cv_prediction(model, X_data, y_target):
    neg_mse_scores = cross_val_score(model, X_data, y_target, scoring="neg_mean_squared_error", cv = 5)
    rmse_scores  = np.sqrt(-1 * neg_mse_scores)
    avg_rmse = np.mean(rmse_scores)
    print('##### ',model.__class__.__name__ , ' #####')
    print(' 5 교차 검증의 평균 RMSE : {0:.3f} '.format(avg_rmse))
    
dt_reg = DecisionTreeRegressor(random_state=0, max_depth=4)
rf_reg = RandomForestRegressor(random_state=0, n_estimators=1000)
xgb_reg = XGBRegressor(n_estimators=1000)
lgb_reg = LGBMRegressor(n_estimators=1000)

# 트리 기반의 회귀 모델을 반복하면서 평가 수행 
models = [dt_reg, rf_reg, xgb_reg, lgb_reg]
for model in models:  
    get_model_cv_prediction(model, X_data, y_target)

ㅁ 선형 회귀 모델을 위한 데이터 변환

- 선형모델은 일반적으로 피처와 타깃값 간에 선형의 관계가 있다고 가정한다

- 선형회귀 모델은 피처값과 타깃값의 분포가 정규분포 형태를 매우 선호 한다. 특히 타깃값(y)의 경우 정규분포가 아니라 특정값의 분포가 치우친 왜곡(skew)된 형태의 분포도일 경우 예측성능에 부정적인 영향을 미칠 가능성이 높다.

- 선형 회귀 모델을 적용하기 전에 먼저 데이터에 대한 스케일링/정규화 작업을 수행하는 것이 일반적

피쳐데이터(x) case1) StandardScaler 클래스를 이용해 평균 0, 분산 1인 표준 정규분포를 가진 데이터 세트로 변환
case2) MinMaxScaler 클래스를 이용해 최솟값이 0이고, 최댓값이 1인 값으로 정규화를 수행
case3) log 변환 
case4) categorical variable은 label encoding이 아닌 one-hot encoding 수행
타깃값(y) 일반적으로 log변환

-> log(0)는 무한대값이 되므로, log(관측값+1)에 해당하는 numpy.log1p()를 사용
-> 예측값은 log1p()를 적용하여 예측한 것으므로, 최종예측값은 numpy.expm1() 을 적용해야 한다.

  왜곡(skew)된 형태의 분포도 / log 변환 후 정규분포 형태

from sklearn.preprocessing import StandardScaler, MinMaxScaler

# method는 표준 정규 분포 변환(Standard), 최대값/최소값 정규화(MinMax), 로그변환(Log) 결정
def get_scaled_data(method='None', input_data=None):
    if method == 'Standard':
        scaled_data = StandardScaler().fit_transform(input_data)
    elif method == 'MinMax':
        scaled_data = MinMaxScaler().fit_transform(input_data)
    elif method == 'Log':
        scaled_data = np.log1p(input_data)
    else:
        scaled_data = input_data
    
    return scaled_data

ㅁ 추가학습

- 자전거 수요예측 (Link)

- 주택가격예측 (Link), 데이터 다운로드 (Link)

ㅁ 부록 - Regularization

더보기

좋은 모델이란? 무엇인가?

1) 현재 데이터(training data)를 잘 설명하는 모델 

2) 미래 데이터(testing data)에 대한 예측 성능이 좋은 모델

 

출처 :&nbsp;https://youtu.be/-sRfrCR07H8?t=115

현재 데이터(training data)를 잘 설명하는 모델은 무엇인가?

= traning error를 minimize하는 모델 

= 회귀의 경우 MSE를 최소화 하는 모델

 

Expected MSE는 increducible error , bias, variance 로 구성되어 있다. 

그러면 우리는 Expected MSE를 줄이기 위해 bias, variance, 혹은 둘다 낮춰야 한다.

 

제약의 유무 (출처 : https://youtu.be/pJCcGK5omhE)

ㅁ 참고

https://ratsgo.github.io/machine%20learning/2017/05/22/RLR/

- [책] 파이썬 머신러닝 완벽가이드

- [블로그] 파이썬 머신러닝 완벽가이드 정리본 (Link)

- [소스코드 출처] https://github.com/wikibook/pymldg-rev

 

 

 

반응형
Comments