릿지 회귀(Ridge Regression)

릿지 회귀가 뭐지?

Ridge regression is a method of estimating the coefficients of multiple-regression models in scenarios where independent variables are highly correlated.
- Wikipedia(Ridge regression)

기존의 다중 선형 회귀선을 팽팽한 고무줄이라고 하면, 릿지 회귀는 이 고무줄을 느슨하게 만들어준 것이다.

다중 선형 회귀 모델은 특성이 많아질수록 훈련 데이터에 과적합되기 쉽다. 이는 마치 "나는 훈련에서 무조건 만점을 받겠어!"라고 하여 열심히 훈련을 해서 만점을 받았는데, 정작 실전에서는 뭐 하나 제대로 못하는 병사같다.
이에 반해 릿지 회귀는 훈련은 좀 덜 열심히 했지만, 실전에서는 꽤 쓸만한 병사라고 할 수 있겠다.


릿지 회귀는 어떻게 생겼지?

과적합된 다중 선형 회귀 모델은 단 하나의 특이값에도 회귀선의 기울기가 크게 변할 수 있다. 릿지 회귀는 어떤 값을 통해 이 기울기가 덜 민감하게 반응하게끔 만드는데, 이 값을 람다(lambda, $\lambda$)라고 한다.
릿지 회귀의 식은 아래와 같다.

$$\beta_{ridge} : argmin[\sum_{i=1}^n(y_i - \beta_0 - \beta_1x_{i1}-\dotsc-\beta_px_{ip})^2 + \lambda\sum_{j=1}^p\beta_j^2]$$

(n: 샘플 수, p: 특성 수, λ: 튜닝 파라미터(패널티))
(참고 - 람다 : lambda, alpha, regularization parameter, penalty term 모두 같은 뜻)

식의 앞 부분은 다중 선형 회귀에서의 최소제곱법(OLS, Ordinary Least Square)과 동일하다.
뒤쪽의 람다가 붙어 있는 부분이 기울기를 제어하는 패널티 부분이다.
뒷부분을 자세히 보면 회귀계수 제곱의 합으로 표현되어 있는데, 이는 L2 Loss$^*$$^1$와 같다. 이런 이유로 릿지 회귀를 L2 정규화(L2 Regularization)라고도 한다.

만약 람다가 0이면 위 식은 다중 선형 회귀와 동일하다.
반대로 람다가 커지면 커질수록 다중 회귀선의 기울기를 떨어뜨려 0으로 수렴하게 만든다. 이는 덜 중요한 특성의 개수를 줄이는 효과로도 볼 수 있다.

적합한 람다의 값을 구하는 방법은 아래에서 scikit-learn을 통해 예시를 들겠다.


scikit-learn을 이용하여 릿지 회귀 수행하기

scikit-learn을 통해 릿지 회귀를 사용하는 방법으로 Ridge와 RidgeCV가 있다.
둘 모두 같은 릿지 회귀이나, RidgeCV는 여러 alpha값(=람다)을 모델 학습 시에 한꺼번에 받아서 자기 스스로 각각의 alpha값에 대한 성능을 비교 후 가장 좋은 alpha를 선택한다. (CV : Cross Validation$^*$$^2$)

아래 예시에서는 먼저 다중 선형 회귀와 릿지 회귀의 차이를 간단히 살펴본 후, 보다 구체적인 Ridge 그리고 RidgeCV의 사용을 다루겠다.

  1. 다중 선형 회귀와 릿지 회귀 간단한 비교
    (사용 데이터 : Anscombe's quartet)
# 데이터 불러오기
import seaborn as sns

ans = sns.load_dataset('anscombe').query('dataset=="III"')
baseline = ans.y.mean() # 기준 모델
sns.lineplot(x='x', y=baseline, data=ans, color='red'); # 기준 모델 시각화
sns.scatterplot(x='x', y='y', data=ans);

 

# 다중 선형 회귀(OLS)
%matplotlib inline

ax = ans.plot.scatter('x', 'y')

# OLS 
ols = LinearRegression()
ols.fit(ans[['x']], ans['y'])

# 회귀계수와 intercept 확인
m = ols.coef_[0].round(2)
b = ols.intercept_.round(2)
title = f'Linear Regression \n y = {m}x + {b}'

# 훈련 데이터로 예측
ans['y_pred'] = ols.predict(ans[['x']])

ans.plot('x', 'y_pred', ax=ax, title=title);

 

# 릿지 회귀
import matplotlib.pyplot as plt
from sklearn.linear_model import Ridge

def ridge_anscombe(alpha):
    """
    alpha : lambda, penalty term
    """
    ans = sns.load_dataset('anscombe').query('dataset=="III"')

    ax = ans.plot.scatter('x', 'y')

    ridge = Ridge(alpha=alpha, normalize=True)
    ridge.fit(ans[['x']], ans['y'])

    # 회귀계수와 intercept 가져오기
    m = ridge.coef_[0].round(2)
    b = ridge.intercept_.round(2)
    title = f'Ridge Regression, alpha={alpha} \n y = {m}x + {b}'

    # 예측
    ans['y_pred'] = ridge.predict(ans[['x']])

    ans.plot('x', 'y_pred', ax=ax, title=title)
    plt.show()

# 여러 알파값을 넣어서 기울기의 변화 확인하기
alphas = np.arange(0, 2, 0.4)
for alpha in alphas:
    ridge_anscombe(alpha=alpha)

 

∴ 그래프를 보면 alpha = 0인 경우에는 OLS와 같은 그래프 형태로 같은 모델임을 확인할 수 있고. alpha 값이 커질수록 직선의 기울기가 0에 가까워 지면서 평균 기준모델(baseline)과 비슷해진다.


  1. Ridge & RidgeCV 구체적인 활용 방법

1) 데이터 불러오기 및 전처리
(사용할 데이터 : Melbourne Housing Market)

import pandas as pd
from sklearn.model_selection import train_test_split

# 데이터 불러오기
df = pd.read_csv('https://ds-lecture-data.s3.ap-northeast-2.amazonaws.com/melbourne_house_prices/MELBOURNE_HOUSE_PRICES_LESS.csv')

# 범주형 특성 중 값의 종류가 너무 많은 특성은 제외
df.drop(columns=['Suburb','Address','SellerG','Date'], inplace=True)

# 결측치인 타겟 값 제거
df.dropna(subset=['Price'], inplace=True)

# 중복된 행 제거
df.drop_duplicates(inplace=True)
from category_encoders import OneHotEncoder

# 사용할 특성들과 타겟을 별도로 분리
target = 'Price'

data = df.drop(target, axis=1)
target = df[target]

# 훈련 / 테스트 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(data, target, train_size=0.8, test_size=0.2, random_state=2)

# 범주형 특성을 수치형으로 변환하는 인코딩 수행
# 자세한 내용은 아래 참고 자료 링크(One-hot Encoder)에 있습니다
encoder = OneHotEncoder(use_cat_names = True)
X_train = encoder.fit_transform(X_train)
X_test = encoder.transform(X_test)

2) Ridge

from sklearn.linear_model import Ridge

alphas = [0, 0.001, 0.01, 0.1, 1]

# Ridge의 경우 alpha값을 이와 같이 따로 따로 넣어주어야 함
for alpha in alphas:
  ridge = Ridge(alpha=alpha, normalize=True)
  ridge.fit(X_train, y_train)
  y_pred = ridge.predict(X_test)

  mae = mean_absolute_error(y_test, y_pred)
  r2 = r2_score(y_test, y_pred)
  print(f'Test MAE: ${mae:,.0f}')
  print(f'R2 Score: {r2:,.4f}\n')

Test MAE: $255,214
R2 Score: 0.5877

Test MAE: $255,264
R2 Score: 0.5878

Test MAE: $254,701
R2 Score: 0.5874

Test MAE: $252,997
R2 Score: 0.5794

Test MAE: $279,498
R2 Score: 0.4742


3) RidgeCV

from sklearn.linear_model import RidgeCV
from sklearn.metrics import mean_absolute_error, r2_score

alphas = [0, 0.001, 0.01, 0.1, 1]

# RidgeCV는 alpha로 넣고자 하는 값들을 리스트로 전달하면 내부적으로 최적의 alpha값을 찾아냄
ridgecv = RidgeCV(alphas=alphas, normalize=True, cv=5)
# cv : cross-validation -> 데이터를 k등분한 후 각각에 대하여 검증 진행
# 검증 결과 가장 점수가 높은 모델을 채택
ridgecv.fit(X_train, y_train)
y_pred = ridgecv.predict(X_test)

mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f'Test MAE: ${mae:,.0f}')
print(f'R2 Score: {r2:,.4f}\n')

print(f'alpha: {ridgecv.alpha_}') # 최종 결정된 alpha값
print(f'cv best score: {ridgecv.best_score_}') # 최종 alpha에서의 점수(R^2 of self.predict(X) wrt. y.)

Test MAE: $255,264
R2 Score: 0.5878

alpha: 0.001
cv best score: 0.5705823371670962


*1 L2 Loss란?
L2 Loss는 L2 Norm을 기준으로 만들어진 손실 함수이다.
다만 L2 Norm이 각 원소 제곱의 합에 루트를 씌워준 것이라면 L2 Loss는 루트를 씌우지 않는다는 차이가 있다.
$$L2\ Norm = \sqrt{\sum_{i=1}^{n} x_i^2}$$
$$L2\ Loss = \sum_{i=1}^{n} (y - \hat{y})^2$$

*2 CV : Cross Validation(교차 검증)?
교차 검증은 주어진 데이터를 동일한 크기로 (고등어 자르듯이) 여러 등분한 후, 각각에 대하여 검증을 진행하는 것이다.
이는 모델이 한 가지 경우에만 잘 맞는, 즉 과적합을 줄이고자 행하는 작업이다.


<참고 자료>

회귀 모델 평가 지표(Evaluation Metrics for Regression Models)

회귀 모델 평가 지표란?

Evaluation metrics are a measure of how good a model performs and how well it approximates the relationship
- towards data science

회귀 모델 평가 지표는 이 모델의 성능이 얼마나 좋은지, 모델에 사용된 특성과 타겟의 관계를 얼마나 잘 나타내는지 보여주는 값이다.

회귀 모델의 평가 지표에는 다음과 같은 것들이 있다.

  • MSE(Mean Squared Error, 평균 제곱 오차)
  • RMSE(Root MSE, 평균 제곱근 오차)
  • MAE(Mean Average Error, 평균 절대 오차)
  • $R^2$(R-squared, Coefficient of determination, 결정 계수)

MSE(Mean Squared Error, 평균 제곱 오차)

회귀 분석을 통해 얻은 예측 값과 실제 값의 차이(잔차)의 평균을 나타낸 것이다. ($y$ : 실제 값, $\hat{y}$ : 예측 값)

$$MSE = \frac {1}{n}\sum_{i=1}^{n}(y_i - \hat{y_i})^2$$

예측 값과 실제 값의 차이를 나타낸 것이므로 작으면 작을수록 좋다.

위 식에서 $(y - \hat{y})^2$를 SSE(Sum of Squares Error, 오차 제곱의 합)라고 하는데, 이는 데이터의 개수가 늘어나면 계속해서 오류의 값이 증가하는 문제가 있다. 이 문제를 해결하기 위해 평균으로 만든 것이 MSE이다.

MSE는 값 하나를 통해 모델의 성능을 파악할 수 있어 직관적이다.
동시에 MSE의 단점으로 아래와 같이 몇 가지가 있는데,

  1. 제곱을 했기 때문에 기존 값의 단위와 다른 단위를 갖는다(쉽게 말하면 값이 뻥튀기되었다).
  2. 이상치(Outlier)가 있는 경우 결과가 왜곡될 수 있다(1번과 동일 이유).
  3. 제곱을 하면 결과는 양수가 되기 때문에 예측 결과가 실제 값보다 높은 것인지, 낮은 것인지 알 수 없다.
  4. 값의 스케일에 의존적이다.$^*$$^1$

RMSE(Root MSE, 평균 제곱근 오차)

위에서 설명한 MSE의 값의 루트를 씌운 것이다.
MSE의 단점 중 제곱을 했기 때문에 발생한 단점을 보완하기 위해서 만들어졌다.

$$RMSE = \sqrt{\frac {1}{n}\sum_{i=1}^{n}(y_i - \hat{y_i})^2}$$

각각의 잔차에 루트를 씌우고서 더한 것이 아니라 전부 다 더한 값(MSE)에 루트를 씌웠기 때문에 제곱에 의한 왜곡이 여전히 존재하나, MSE보다는 덜하다.


MAE(Mean Average Error, 평균 절대 오차)

잔차에 절대값을 씌운 후 평균으로 만든 것이다.

$$MSE = \frac {1}{n}\sum_{i=1}^{n}|y_i - \hat{y_i}|$$

MSE나 RMSE와 마찬가지로 값 하나만 보고 모델의 성능을 직관적으로 파악할 수 있다.
또한 각각의 잔차에 단순히 절대값만 씌운 것의 평균이기에 기존 값의 단위를 그대로 유지하며, MSE에 비해 이상치에 의한 왜곡이 덜 하다는 장점이 있다.

하지만 절대값을 씌웠기 때문에 결과 값이 항상 양수이고, 이는 MSE와 마찬가지로 예측 결과가 실제보다 높은지 낮은지 알 수 없다.


$R^2$(R-squared, Coefficient of determination, 결정 계수)

$R^2$(이하 결정 계수)는 모델이 얼마나 예측을 잘 했는지를 0에서 1 사이로 나타낸 값이다. 1에 가까울수록 성능이 좋은 것이다.

$$1 - \frac{\sum_{i=1}^{n}(y_{i} - \hat{y_{i}})^{2}}{\sum_{i=1}^{n}(y_{i} - \bar{y_{i}})^{2}} = 1 - \frac{SSE}{SST} = \frac {SSR}{SST}$$

조금 더 구체적으로 말하자면, 실제 값의 분산과 예측 값의 분산을 비교하여 예측이 얼마나 잘 맞았는지 보는 것이다. 만약 결정 계수의 값이 0.7이라면 이는 모델이 실제 값의 분산을 70% 설명한다는 의미이다.

위의 MSE, RMSE, MAE와 달리 결정 계수는 값의 단위가 원래 무엇이었든 0부터 1 사이의 값으로 표현하기에 스케일에 의존적이지 않다.
또한 값 하나로 모델의 성능을 알아볼 수 있어 직관적이다.

그러나 과적합(Overfitting) 문제에 약하다는 단점이 있다. 특성이 많아질수록, 즉 모델이 복잡해질수록 모델은 훈련 데이터는 잘 맞추지만 테스트는 잘 망치는, 즉 과적합되는 문제가 있다. 결정 계수는 그저 자신의 값을 보여줄 뿐, 모델이 과적합인지 아닌지는 말해주지 않는다.
이러한 과적합 문제를 보완하기 위해서 Adjusted R-squared(수정된 결정 계수)가 있다.

※ 참고
- SSE(Sum of Squares Error, 관측치와 예측치 차이): $\sum_{i=1}^{n}(y_{i} - \hat{y_{i}})^{2}$
- SSR(Sum of Squares due to Regression, 예측치와 평균 차이): $\sum_{i=1}^{n}(\hat{y_{i}} - \bar{y_{i}})^{2}$
- SST(Sum of Squares Total, 관측치와 평균 차이): $\sum_{i=1}^{n}(y_{i} - \bar{y_{i}})^{2}$ , SSE + SSR

*1 스케일에 의존적이라는게 무슨 말?
예를 들어서 삼성전자와 애플의 주식 가격을 예측한다고 해보자.
여기서 한국 주식의 가격은 원화, 미국 주식의 가격은 달러이다(작성 시각 기준 삼성전자 70,200원, 애플 149.28달러).
이 둘에 대한 회귀 모델을 만들었고, MSE 값을 구했더니 둘 다 30이 나왔다고 해보자.
가격의 단위가 판이하게 다름에도 에러 값이 똑같이 나왔다.
그렇다고 해서 이 둘이 동등한 수준의 성능을 보여주는 것은 아니다.

이렇게 예측하고자 하는 값의 단위에 영향을 받는 것을 두고 스케일에 의존적이라고 한다.


<참고 자료>

다중 선형 회귀 모델(Multiple Linear Regression)

※ 단순 선형 회귀를 모른다면? (아래 클릭 시 링크로 이동)
단순 선형 회귀 모델(Simple Linear Regression Model)

다중 선형 회귀가 뭐지?

단순 선형 회귀와 다중 선형 회귀
선형 회귀의 가장 단순한 예제는 한 개의 스칼라 독립 변수 x와 한 개의 스칼라 의존 변수 y의 관계일 것이다. 이를 단순 선형 회귀라 부른다. 여기에서 독립 변수를 여러 개로 확장한 것이 다중 선형 회귀이다.

실세계의 거의 대부분의 문제는 여러 개의 독립 변수를 포함하며, 선형 회귀라 함은 보통 다중 선형 회귀를 일컫는다. 하지만 이러한 경우에도 여전히 응답 변수 y는 한 개의 스칼라 변수이다. 다변량 선형 회귀는 응답 변수 y가 벡터인 경우를 의미한다. 이러한 경우를 일반 선형 회귀라 부른다. 다중 선형 회귀와 다변량 선형 회귀는 다른 의미이므로, 혼동하지 않도록 주의해야 한다.
- 위키백과 (선형 회귀 中)

영향을 받은 결과와 영향을 준 요인의 개수를 '결과 : 요인'이라고 표현하면 단순 선형 회귀는 1:1이고 다중 선형 회귀는 1:N이다.
여기서 이 결과가 종속 변수(또는 타겟)이고, 요인을 독립 변수(특성)라고 한다.

선형 회귀에서의 가정이나 최소제곱법을 이용하여 결과값을 구하는 등의 특징은 단순 선형 회귀와 공유한다.
다만 다중 선형 회귀에서는 특성의 개수가 2개 이상이므로 다중공선성(Multicollinearity)$^*$$^1$을 주의해야 한다.


다중 선형 회귀는 어떻게 하는거지?

기본적인 원리는 단순 선형 회귀와 비슷하다.
다만 차이점이라면 특성의 개수가 2개 이상이므로 회귀 방정식(단순 선형 회귀에서는 직선을 나타내는 식)에 차이가 있다.

단순 선형 회귀에서의 회귀 방정식은 $$y=β_0+β_1x$$ 이고, 다중 선형 회귀에서의 회귀 방정식은 $$y=β_0+β_1x_1+β_2x_2+ ... +β_nx_n$$ 이다.
(자세한 계산 과정은 하단 참고 자료의 링크로)


scikit-learn을 활용한 다중 선형 회귀

  1. 데이터 불러오기 & 훈련/테스트 데이터 나누기
# 예시 데이터 불러오기 (House Sales in King County, USA)
import pandas as pd

df = pd.read_csv('https://ds-lecture-data.s3.ap-northeast-2.amazonaws.com/kc_house_data/kc_house_data.csv')
df.date = pd.to_datetime(df.date) # 날짜 형식으로 변환

df.head()
  id date price bedrooms bathrooms sqft_living sqft_lot floors waterfront view condition grade sqft_above sqft_basement yr_built yr_renovated zipcode lat long sqft_living15 sqft_lot15
0 7129300520 2014-10-13 221900.0 3 1.00 1180 5650 1.0 0 0 3 7 1180 0 1955 0 98178 47.5112 -122.257 1340 5650
1 6414100192 2014-12-09 538000.0 3 2.25 2570 7242 2.0 0 0 3 7 2170 400 1951 1991 98125 47.7210 -122.319 1690 7639
2 5631500400 2015-02-25 180000.0 2 1.00 770 10000 1.0 0 0 3 6 770 0 1933 0 98028 47.7379 -122.233 2720 8062
3 2487200875 2014-12-09 604000.0 4 3.00 1960 5000 1.0 0 0 5 7 1050 910 1965 0 98136 47.5208 -122.393 1360 5000
4 1954400510 2015-02-18 510000.0 3 2.00 1680 8080 1.0 0 0 3 8 1680 0 1987 0 98074 47.6168 -122.045 1800 7503
# 2015-01-01을 기준으로 훈련/테스트 데이터 분리
train = df[df.date < '2015-01-01']
test = df[df.date >= '2015-01-01']

  1. 타겟 및 기준 모델 설정
# 타겟 설정
target  =  'price'  
y_train  =  train[target]  
y_test  =  test[target]
# price 평균값으로 예측(기준모델)
predict = y_train.mean()
predict

539181.4284152258

# 기준모델로 훈련 에러(MAE) 계산
from sklearn.metrics import mean_absolute_error

y_pred = [predict] * len(y_train)
mae = mean_absolute_error(y_train, y_pred)

print(f'훈련 에러: {mae:.2f}')

훈련 에러: 233570.83

# 테스트 에러(MAE)
y_pred = [predict] * len(y_test)
mae = mean_absolute_error(y_test, y_pred)

print(f'테스트 에러: {mae:.2f}')

테스트 에러: 233990.69


  1. 다중 선형 회귀 모델 만들기
# 다중모델 학습을 위한 특성
features = ['bathrooms', 'sqft_living']
X_train = train[features]
X_test = test[features]
# 모델 fit
from sklearn.linear_model import LinearRegression  

model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_train)
mae = mean_absolute_error(y_train, y_pred)

print(f'훈련 에러: {mae:.2f}')

훈련 에러: 170777.34

# 테스트 데이터에 적용
y_pred = model.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)

print(f'테스트 에러: {mae:.2f}')

테스트 에러: 179252.53


  1. 절편과 회귀계수 확인
# 절편(intercept)과 계수들(coefficients)
model.intercept_, model.coef_

(-50243.56279640319, array([-5158.92591411, 286.13753555]))
∵ 특성이 2개이기 때문에 회귀 계수도 2개

## 회귀식
b0 = model.intercept_
b1, b2 = model.coef_  

print(f'y = {b0:.0f} + {b1:.0f}x\u2081 + {b2:.0f}x\u2082')

y = -50244 + -5159$x_1$ + 286$x_2$


  1. 훈련/테스트 데이터에 대한 평가 지표 결과 확인
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score  

def  evaluate(features, model, train, test):
    y_train = train[target]
    y_test = test[target]
    X_train = train[features]
    X_test = test[features]
    y_pred_train = model.predict(X_train)
    y_pred_test = model.predict(X_test)  

    mse = mean_squared_error(y_train, y_pred_train)
    rmse = mse ** 0.5
    mae = mean_absolute_error(y_train, y_pred_train)
    r2 = r2_score(y_train, y_pred_train)  

    mse_t = mean_squared_error(y_test, y_pred_test)
    rmse_t = mse_t ** 0.5
    mae_t = mean_absolute_error(y_test, y_pred_test)
    r2_t = r2_score(y_test, y_pred_test)  

    display(pd.DataFrame([['MSE', mse, mse_t],['RMSE', rmse, rmse_t],['MAE', mae, mae_t],['R2', r2, r2_t]], columns=['Metric',  'Score(Training)',  'Score(Test)']))
evaluate(features, model, train, test)
  Metric Score(Training) Score(Test)
0 MSE 6.709905e+10 7.108399e+10
1 RMSE 2.590348e+05 2.666158e+05
2 MAE 1.707773e+05 1.792525e+05
3 R2 5.076086e-01 4.599930e-01

 


∗1 다중공선성(Multicollinearity)이란?
다중공선성(多重共線性)문제(Multicollinearity)는 통계학의 회귀분석에서 독립변수들 간에 강한 상관관계가 나타나는 문제이다. 독립변수들간에 정확한 선형관계가 존재하는 완전공선성의 경우와 독립변수들간에 높은 선형관계가 존재하는 다중공선성으로 구분하기도 한다. 이는 회귀분석의 전제 가정을 위배하는 것이므로 적절한 회귀분석을 위해 해결해야 하는 문제가 된다.
- 위키백과

특성(독립 변수)들끼리 강한 상관 관계가 있는 경우를 의미한다.
만약 이를 배제하지 않고 분석에 그대로 사용하게 되면 결과가 왜곡될 위험이 크다.

<참고 자료>
[통계 이론] 선형 회귀 : 다중 회귀 분석
머신러닝 - 다중선형회귀(Multiple Linear Regression)

선형 회귀(Linear Regression Model)

선형 회귀가 뭐지?

통계학에서, 선형 회귀(線型回歸, 영어: linear regression)는 종속 변수 y와 한 개 이상의 독립 변수 (또는 설명 변수) X와의 선형 상관 관계를 모델링하는 회귀분석 기법이다. 한 개의 설명 변수에 기반한 경우에는 단순 선형 회귀(simple linear regression), 둘 이상의 설명 변수에 기반한 경우에는 다중 선형 회귀라고 한다.

선형 회귀는 선형 예측 함수를 사용해 회귀식을 모델링하며, 알려지지 않은 파라미터는 데이터로부터 추정한다. 이렇게 만들어진 회귀식을 선형 모델이라고 한다.

일반적으로 최소제곱법(least square method)을 사용해 선형 회귀 모델을 세운다. 최소제곱법 외에 다른 기법으로도 선형 회귀 모델을 세울 수 있다. 손실 함수(loss fuction)를 최소화 하는 방식으로 선형 회귀 모델을 세울 수도 있다. 최소제곱법은 선형 회귀 모델 뿐 아니라, 비선형 회귀 모델에도 적용할 수 있다. 최소제곱법과 선형 회귀는 가깝게 연관되어 있지만, 그렇다고 해서 동의어는 아니다.
- 위키백과

선형 회귀란 쉽게 생각하면 어떠한 현상 A와 이와 관련되어 보이는 한 개 이상의 현상 B가 있을 때, B가 변화함에 따라 A가 어떻게 변하는지 도형(직선, 평면 등) 하나를 통해 나타낸 것이다. 여기서 B가 1개라면 단순 선형 회귀(Simple Linear Regression), 2개 이상이면 다중 선형 회귀(Multiple Linear Regression)라고 한다.


선형 회귀에서의 가정(Assumptions of Linear Regression)

선형 회귀가 성립되기 위한 가정에는 다음 4가지가 있다.

  1. 선형성(Linearity) : 독립 변수와 종속 변수의 관계는 선형적이어야 한다. 즉, 그래프로 표현했을 때 끊어지지 않고 쭈욱 이어지는 선으로 표현 가능해야 한다.
  2. 정규성(Normality) : 잔차(종속 변수에 대한 예측 값과 실제 값의 차이)$^*$$^1$가 정규 분포여야 한다.
  3. 등분산성(Homoscedasticity, Constant Variance) : 독립 변수의 모든 값에 대하여 잔차의 분산이 같아야 한다.
  4. 독립성(Independence) : 잔차는 서로 독립적이어야 한다.

단순 선형 회귀(Simple Linear Regression)

위에서 간단하게 언급한 바와 같이 단순 선형 회귀는 독립 변수 1개와 종속 변수 1개의 관계를 직선으로 나타낸 것이다.
이를 통해 독립 변수의 값에 따라 이에 해당하는 종속 변수의 값을 구할 수 있다. 즉, 기존의 데이터를 바탕으로 실제 데이터에는 존재하지 않는 값을 예측할 수 있다.

예를 들면 어느 중학교 학생들의 공부 시간과 시험 성적을 조사한 결과가 아래와 같다고 해보자.

학생 공부 시간 시험 성적
A 3 30
B 5 50
C 2 20
D 6 60
E 4 40

이를 그래프로 나타내면 아래처럼 보일 것이다.

그리고 이 그래프 위의 점들에 최대한 가깝게 지나가는 직선을 그리면 아래와 같다.

실제 데이터에는 공부 시간이 2, 3, 4, 5, 6 뿐이다. 여기서 만약 8시간 공부한 학생의 시험 성적이 궁금하다면 x값(공부 시간)이 8일 때 직선 상의 y값(시험 성적)이 무엇인지 알아보면 된다.

굳이 별도의 계산이나 그래프가 필요할 만한 예시는 아니었지만, 단순 선형 회귀를 통해 예측 값을 얻는 과정이 이와 같다.

그렇다면 실제로 저 직선은 어떻게 구하는 것일까?


최소제곱법(Least Squares Method = Ordinary Least Squares, OLS)

최소제곱법이란 예측 값과 실제 값의 차이인 잔차들의 제곱의 합을 최소화하는 직선을 찾아내는 방법이다. 이 직선을 최소 제곱 직선이라고 한다.

※ 왜 제곱을 하지?

제곱하는 이유는 실제 값이 예측 값보다 클 수도, 작을 수도 있기 때문이다.
제곱을 안 하고서 그냥 합하면 양수와 음수가 서로 상쇄되어 결과가 왜곡될 수 있다(분산 구할 때 제곱해주던 것과 마찬가지).

<예시>
아래와 같이 x와 y가 있다고 해보자.

x y
1 2
2 4
3 5
4 4
5 5
평균 3 평균 4

이를 그래프로 나타내면 아래와 같다.

여기서 직선 $\hat{y} = \beta_0 + \beta_1x$ 가 예측 값을 얻는데 쓰일 최소 제곱 직선(다른 말로는 선형 회귀선)이다.

직선 $\hat{y} = \beta_0 + \beta_1x$는 $x$와 $y$의 관계를 나타낸다($\hat{y}$는 $y$의 예측값(추정치)라는 의미).
$x$와 $y$의 관계를 최대한 잘 나타내려면 각각의 $x$값에 대하여 실제 $y$값에 최대한 가까운 예측 값을 가져야 한다.
즉, 직선 상의 $y$값(= $\hat{y}$, 예측 값)과 점으로 표시된 $y$값(실제 값)의 차이(잔차)를 최소화해야 한다.

그럼 잔차를 최소화하는 직선 $\hat{y} = \beta_0 + \beta_1x$의 기울기와 $y$ 절편을 구해보자.


1) 기울기($\beta_1$) 구하기

직선의 기울기를 구하는 식은 다음과 같다.
$$\beta_1 = \frac{\sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y})} {\sum_{i=1}^{n} (x_i - \bar{x})^2}$$
아래 테이블에 계산되어 있는 값을 식에 그대로 넣으면 $\beta_1 = \frac{6}{10} = 0.6$ 이다.

$x$ $y$ $x - \bar{x}$ $y - \bar{y}$ $(x - \bar{x})^2$ $(x - \bar{x})(y - \bar{y})$
1 2 -2 -2 4 4
2 4 -1 0 1 0
3 5 0 1 0 0
4 4 1 0 1 0
5 5 2 1 4 2
평균 3 평균 4     합계 10 합계 6

2) $y$ 절편($\beta_0$) 구하기

이제 기울기를 알고 있으니 $y$ 절편을 구하는건 간단하다.
직선 $\hat{y} = \beta_0 + 0.6x$에 ($\bar{x}, \bar{y}$)를 넣어주면 된다.
$∴ \beta_0 = \bar{y} - 0.6\bar{x} = 4 - 0.6*3 = 2.2$

※ $\beta_1$을 구하는 식은 왜 저렇게 되고, $\beta_0$는 왜 저렇게 구할까? (참고 자료)
최소제곱법 - 네이버캐스트 수학 산책
최소 제곱법 (Least Square Method = OLS)
#선형회귀 의 기초인 #최소제곱법 ? 공식 유도를 해보자~! (영상)


기준 모델(Baseline Model)

위처럼 예측 값을 잘 뽑아줄 직선을 구했으니 무언가 할 일은 다 한 것 같다.
그러나 사실 그 전에 해야 할 일이 있었으니, 바로 기준 모델을 세우는 것이다.

기준 모델이란 예측 모델(위에서 만든 직선 식)을 구체적으로 만들기 전에 가장 간단하면서도 직관적이면서 최소한의 성능을 나타내는 기준이 되는 모델이다.
쉽게 말하면 예측 모델을 뚝딱 만들고 봤을 때 최소한 이 기준 모델이라는 친구보다는 일을 잘 해야 한다는 것이다.

기준 모델은 문제의 유형에 따라 보통 다음과 같이 설정한다.

  • 분류 문제: 타겟의 최빈 클래스
  • 회귀 문제: 타겟의 평균값 -> 평균 절대 오차(MAE, Mean Absolute Error)로 평가
  • 시계열 회귀 문제: 이전 타임 스탬프의 값

 


scikit-learn 활용하여 단순 선형 회귀 모델 만들기

  1. 데이터 불러오기
# 예시 데이터 불러오기 (House Sales in King County, USA)
import pandas as pd
df = pd.read_csv('https://ds-lecture-data.s3.ap-northeast-2.amazonaws.com/kc_house_data/kc_house_data.csv')

  1. 타겟과 이와 관련된 특성 찾아서 정하기
# 여기서는 타겟(price)과 가장 상관 관계가 높은 특성을 찾겠습니다
df.corr().price.sort_values(ascending=False)

price 1.000000
sqft_living 0.702035
grade 0.667434
sqft_above 0.605567
sqft_living15 0.585379
bathrooms 0.525138
view 0.397293
sqft_basement 0.323816
bedrooms 0.308350
lat 0.307003
waterfront 0.266369
floors 0.256794
yr_renovated 0.126434
sqft_lot 0.089661
sqft_lot15 0.082447
yr_built 0.054012
condition 0.036362
long 0.021626
id -0.016762
zipcode -0.053203
Name: price, dtype: float64

∴ 타겟과 상관 관계가 가장 높은 특성의 이름 : sqft_living


  1. 기준 모델 만들기 및 평가
predict = df.price.mean() # 기준 모델 : 타겟(price)의 평균값
print(int(predict))

sns.scatterplot(x=df.sqft_living, y=df.price)
sns.lineplot(x=df.sqft_living, y=predict, color='red'); # 기준 모델 시각화

 

# 평균값으로 예측할 때 샘플 별 평균값과의 차이(error)를 저장
errors = predict - df.price
errors

0 318188.141767
1 2088.141767
2 360088.141767
3 -63911.858233
4 30088.141767
...
21608 180088.141767
21609 140088.141767
21610 137987.141767
21611 140088.141767
21612 215088.141767
Name: price, Length: 21613, dtype: float64

# mean_absolute_error(MAE), error에 절대값을 취한 후 평균을 계산
mean_absolute_error = errors.abs().mean()
print(f'예측한 주택 가격이 ${predict:,.0f}이며 절대 평균 에러가 ${mean_absolute_error:,.0f}임을 확인할 수 있습니다.')

예측한 주택 가격이 $540,088이며 절대 평균 에러가 $233,942임을 확인할 수 있습니다.


  1. 예측 모델(선형 회귀 모델) 만들기
from sklearn.linear_model import LinearRegression

model = LinearRegression()
feature = ['sqft_living']
target = ['price']
X_train = df[feature]
y_train = df[target]
model.fit(X_train, y_train)

LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)

# 예측을 잘 하는지 값 하나 던져서 테스트 해보기
X_test = [[15000]]
y_pred = model.predict(X_test)

print(f'{X_test[0][0]} sqft GrLivArea를 가지는 주택의 예상 가격은 ${round(float(y_pred))} 입니다.')

15000 sqft GrLivArea를 가지는 주택의 예상 가격은 $4165773 입니다.


  1. 예측 값과 실제 값을 그래프로 표현
X_test  =  [[x]  for  x  in  df['sqft_living']]  
y_pred  =  model.predict(X_test)

y_pred

array([[287555.06702451],
[677621.82640197],
[172499.40418656],
...,
[242655.29616092],
[405416.96554144],
[242655.29616092]])

plt.scatter(X_train, y_train, color='black', linewidth=1)
plt.scatter(X_test, y_pred, color='blue', linewidth=1);

 


  1. 예측 모델의 회귀 계수(직선의 기울기)와 y 절편 확인
print(model.coef_) # 회귀 계수
print(model.intercept_) # y 절편

[[280.6235679]]
[-43580.74309447]

$*1$ 오차(error)와 잔차(residual)의 차이가 뭐지?
오차 : 모집단이 기준
잔차 : 표본 집단이 기준
실제 데이터를 토대로 선형 회귀 등의 방식으로 회귀식을 구하면 예측 값을 얻을 수 있다. 여기서 실제 데이터가 모집단인지, 표본 집단인지에 따라 실제 값과 예측 값의 차이를 각각 오차와 잔차라고 한다.

 

<참고 자료>

● 선형 회귀

● 선형 회귀에서의 가정

● 최소제곱법

● 기준 모델

● 오차와 잔차

● 시각화

+ Recent posts