데이터 분석을 공부하다보면 흔하게 마주치는 이름들이 있습니다. Linear, Ridge, Lasso, elasticNet… 회귀모델 4인방입니다. 어디선가 한 번 쯤 들어본 듯한 이분들(?)에 대해 최대한 쉬운 언어로 + Python 핸즈온 코드로 정리해보도록 노력하였습니다.
Ridge, Lasso, elasticNet의 경우는 수식을 전부 이해하시지 않더라도 sklearn에서 세 모델을 돌리기위해서 조정하는 값은 alpha 하나이기 때문에 코드를 돌리시는 데는 무리가 없지만 그래도 alpha라는 값이 모델에서 어떤 의미인지 이해하기 위해서는 수식에 대한 설명을 따라오시면 좋을 것 같습니다.
+ 이 글에서 Ridge 회귀모델을 잘 이해할 수 있다면 나머지 Lasso, elasticNet은 응용되는 개념이기 때문에 좀 더 순조롭게 이해하실 수 있습니다. 수식이 이해가 되지 않으시더라도 괜찮습니다!! 지금은 이해 안 가셔도 나중에 갑자기 이해가 되실 수 있으니 편안하게 읽어주시면 좋습니다.
설명 중간 중간 나오는 파이썬 코드 전체는 아래 github을 참고해주시면 됩니다.
https://github.com/entheos37/data_science/blob/master/regression/Data_Science_Model_Summary.ipynb
- Linear Regression 선형 회귀 모델
위에 공식은 위키백과에서 복사 붙여넣기를 한 것인데요, 쉽게 얘기하면 y라는 종속변수와 한 개 이상의 설명 변수(=독립변수) x들과의 선형 상관관계를 모델링하는 회귀 분석 모델이 바로 Linear Regression, 선형 회귀 모델입니다.
좀 더 쉽게 예시를 들어보겠습니다. 파운드와 킬로그램은 선형적 상관 관계를 가지고 있습니다. 7개 데이터를 가지고 점을 찍어보면 파운드 x와 킬로그램 y는 대략적으로 선형적 관계를 가지고 있다는 것을 확인할 수 있습니다. 즉 파운드 x 값에 어떤 weight를 곱하고 bias를 더해주면 킬로그램 y 값이 나오는 것입니다.
이제 sklearn 사이킷런 라이브러리의 LinearRegression 모델을 임포트해서 pound 데이터를 x, kilogram 데이터를 y로 학습시킨 후 해당 모델로 30 pounds가 몇 kilograms인지 예측해보도록 하겠습니다.
# load library
import numpy as np
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
%matplotlib inline
import warnings warnings.filterwarnings("ignore", category=DeprecationWarning)
# define data
kilograms = np.array([49, 10, 0, 21, 44, 2, 100], dtype=float).reshape((-1,1))
pounds = np.array([108.027, 22.0462, 0, 46.2971, 97.0034, 4.40925, 220.462], dtype=float).reshape((-1,1))
for i,c in enumerate(kilograms):
print("%s Kilograms = %s Pounds" %(c, pounds[i]))
이렇게 하면 결과가 아래와 같이 출력됩니다. 49 킬로그램은 108.027 파운드이구요, 10 킬로그램은 22.0462 파운드입니다. 이 데이터를 그래프로 그린 게 바로 위에 보여지는 scatter plot 산점도입니다.
[49.] Kilograms = [108.027] Pounds [10.] Kilograms = [22.0462] Pounds [0.] Kilograms = [0.] Pounds [21.] Kilograms = [46.2971] Pounds [44.] Kilograms = [97.0034] Pounds [2.] Kilograms = [4.40925] Pounds [100.] Kilograms = [220.462] Pounds
이제 이 데이터를 가지고 선형회귀 모델로 학습을 해서 과연 30 파운드는 몇 킬로그램인지를 예측해보도록 하겠습니다~!
from sklearn.linear_model import LinearRegression
# create a linear regression model
model = LinearRegression()
# train data
model.fit(pounds, kilograms)
# predict data - 30.0 pounds to kilograms
result_kilograms = model.predict([[30.0]])
print('30.0 pounds >> %s kilograms' %(result_kilograms[0][0]))
30 파운드가 바로 아래처럼 약 13.6078 킬로그램인 것을 알 수 있습니다.
30.0 pounds >> 13.607745297753652 kilograms
구글에 pounds to kg 로 쳐보시면 놀랍도록!! 선형회귀로 예측한 결과가 정확하신 것을 보실 수 있습니다.

# print weights and bias
print(model.coef_[0][0])
print(model.intercept_[0])
weights와 bias를 출력해보면 값은 아래와 같습니다.
0.4535926229261759 -3.3390031624946914e-05
이를 다시 공식화 해보면, 아래 수식이 됩니다.
y (kilograms) = 0.4535926229261759 * x (pounds) + (-3.3390031624946914e-05)
이 수식으로 다시 계산해보면 모델이 예측한 값과 동일한 것을 더블 체크해볼 수 있습니다.
# re-calculate based on the weights and bias
# output y kilograms = input x pounds * weights + bias
# 13.607745297753652 = 30.0 * 0.4535926229261759 + -3.3390031624946914e-05
30.0*model.coef_[0][0]+model.intercept_[0]
13.607745297753652
위에서 점으로 그렸던 실제 데이터 산점도 위에 모델로 예측한 값이 얼마나 일치하는지 그래프로 시각화 해보겠습니다.
# draw data with linear regression model
plt.title('Kilograms and Pounds')
plt.xlabel('Pounds')
plt.ylabel('Kilograms')
plt.plot(pounds, kilograms,'k.')
plt.grid(True)
plt.plot(pounds, model.predict(pounds))

보시면 실제 데이터 (점) 과 예측한 모델 (선) 이 겹치는 것을 보실 수 있습니다.
이제 회귀분석 몸풀기로 Linear Regression을 살펴보았으니~~ 그 다음으로 Ridge, Lasso, elasticNet Regression 모델을 살펴보겠습니다.
2. Ridge 릿지 & Lasso 라쏘 & elasticNet 엘라스틱넷 Regression
Ridge, Lasso, elasticNet 회귀 분석에서는 multicollinearity 다중공선성 문제와 over-fitting 과적합 문제 방지를 위해 아래와 같은 정규화 방식이 적용되었습니다.
- Ridge 회귀 모델은 L2 정규화
- Lasso 회귀모델은 L1 정규화
- elasticNet은 Ridge의 L2와 Lasso의 L1 정규화 혼합
+ 여기서 multicollinearity 다중공선성이란 독립변수 x들간의 강한 상관관계가 나타나서, 독립변수들이 독립적이지 않는 문제가 발생하게 되는 현상을 말합니다. 이 경우 coefficient 추정치가 부정확해지고 standard error 값이 높아지게 됩니다.
참고로 Ridge, Lasso 수식에 대한 설명은 Saptashwa Bhattacharyya 님의 글을 참고하였습니다.

Cost Function 비용함수는 간단하게 말하면 y (실제 결과값)과 y^ (모델로 추정한 결과값)의 차이(에러)를 제곱해서 합한 것입니다. 차이를 그냥 다 더하지 않고 제곱한 이유는 그냥 전부 더하면 0이 되버리기 때문에 차이에 대한 제곱을 더한 것입니다. 실제로 손수 계산해서 해보시면 제곱 안 하고 다 더하는 경우 0이 되는 것을 확인하실 수 있습니다.
+ 참고로 MSE라고 흔히 줄여서 말하는 Mean Squared Error는 Cost Function을 데이터 수 n만큼 나눠준 것이라고 보시면 됩니다.
Ridge 릿지 회귀는 Linear Regression 선형 회귀모델의 Cost Function 비용함수에 페널티를 적용한 것입니다. 여기서 페널티는 Lambda * 계수 coefficient 제곱의 합입니다. 이때 Lambda 값이 0에 가까워지면 Ridge는 본래 Linear Regression의 Cost Function에 가까워지게 됩니다.
반면에 Lambda의 값이 어느 정도 크다면, coefficient의 크기가 줄어서(0에 가까워져서) 모델의 복잡도가 줄어들고 multicollinearity 문제의 영향을 줄어들게 할 수 있게 됩니다. 왜냐면 서로 영향을 미치는 독립변수들의 weight가 줄어드는 것이기 때문입니다.

이제 Lasso 라쏘 회귀를 살펴보겠습니다.

Ridge와 수식은 비슷하지만 한 가지 차이점은, 페널티의 계산이 Lambda * coefficient 제곱합이 아니라 Lambda * coefficient 절대값의 합이라는 것입니다. 이 계산은 L1 정규화 방식이고, zero coefficient 를 만드는 것이 가능해집니다. 즉, 어떤 독립변수의 경우 아예 사라지게 될 수도 있게 되면서 feature selection, 상대적으로 더 중요한 독립변수를 선택할 수 있게 됩니다.
Lasso는 특정 독립변수의 coefficient 값을 0으로 만들 수 있고 Ridge는 0에 가까워지게 하지만 Lasso처럼 0으로 만들 수는 없다는 차이점이 있습니다.

위 사이트에서 elasticNet의 공식을 참고하였습니다.
+ 참고로 위 사이트는 에러 제곱합을 2n으로 나누어주었는데요 위에 MSE 설명 참고하시면 MSE란 에러 제곱합의 평균인데 의미적으로는 크게봤을 때 에러 제곱합이다 >> 라고 보시면 됩니다.
원래 Linear Regression의 비용함수에다가 penalty 함수가 있는데 이 penalty 함수의 구성이 원래 Ridge는 Lambda*계수 제곱합 ==> L2 정규화이었고, Lasso는 Lambda *계수 절대값 합 ==> L1 정규화 였습니다.
elasticNet은 (1-alpha)/2 * Ridge의 L2 정규화+ alpha * Lasso의 L1 정규화
즉 Ridge의 L2, Lasso의 L1을 합쳐서 비용함수를 계산한 것이라고 보면 됩니다.
alpha가 0에 가까우면 elasticNet은 Ridge에 가까워지고, alpha가 1에 가까우면 elasticNet은 Lasso에 가까워지게 됩니다. elasticNet을 사용하게 되면 Ridge처럼 coefficient의 크기를 줄이면서 Lasso처럼 특정 coefficient를 0으로 만들어 버리면서 특정 독립변수를 사라지게 하는 feature selection 효과를 누릴 수 있게 됩니다.
이제 sklearn 사이킷런의 관련 라이브러리들을 임포트해서 간단한 데이터로 핸즈온을 해보도록 하겠습니다. 참고한 코드는 아래 주석에 출처 표시가 되어있습니다. Ridge에 대해서만 코드를 살펴보도록 하겠습니다. 나머지 Lasso, elasticNet 구현은 비슷한 로직으로 되어있기 때문에 제 Github을 참고해주시면 됩니다.
# Reference
# https://www.datatechnotes.com/2019/08/elasticnet-regression-example-in-python.html
# https://towardsdatascience.com/ridge-and-lasso-regression-a-complete-guide-with-python-scikit-learn-e20e34bcbf0b
#load library
from sklearn.datasets import load_boston
from sklearn.linear_model import Ridge, RidgeCV, Lasso, LassoCV, ElasticNet,ElasticNetCV
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
# load boston house price data
boston=load_boston()
x, y = boston.data, boston.target
xtrain, xtest, ytrain, ytest = train_test_split(x, y, test_size=0.2,random_state=3)
alphas = [0.0001, 0.001, 0.01, 0.1, 0.3, 0.5, 0.7, 1]
# Ridge Regression
# select alpha 0.0001 or 0.001 or 0.01 by checking R2, MSE, RMSE
for a in alphas:
model = Ridge(alpha=a).fit(x,y)
score = model.score(x, y)
pred_y = model.predict(x)
mse = mean_squared_error(y, pred_y)
print("Alpha:{0:.4f}, R2:{1:.2f}, MSE:{2:.2f}, RMSE:{3:.2f}"
.format(a, score, mse, np.sqrt(mse)))

위 코드를 보시면 alpha를 0.0001 또는 0.001 또는 0.01 로 선택해야겠다는 판단을 할 수 있는데요 R2는 제일 높고(제일 설명력이 좋고), MSE(Mean Squared Error)와 RMSE(Root Mean Squared Error)는 제일 낮은 값을 기준입니다.
이렇게 봤을 때 좀 애매할 수 있어서 Cross Validation 을 돌려서 최적의 alpha를 출력해보도록 하겠습니다.
+ Cross Validation이란 데이터 세트를 여러 등분을 해서 반복적으로 교차 검증 (트레이닝 및 테스트)를 돌리는 것입니다. 나중에 기회가 되면 Cross Validation Test에 대해서 정리해보도록 하겠습니다.
# run Ridge cross validation test to find the best alpha based on training data
ridge_cv=RidgeCV(alphas=alphas, cv=5)
model = ridge_cv.fit(xtrain, ytrain)
print(model.alpha_)
cv를 5로 했다는 것은 5등분을 해서 교차검증을 5번 했다고 보시면 됩니다. 위에 코드를 실행시키면 결과적으로 alpha는 0.0001이 출력되는데요, 이 말은 alpha를 0.0001로 했을 때 테스트 세트에 대한 예측이 확률적으로 좋았다는 것입니다.
# calculate Ridge R2, MSE, RMSE from test data
ridge=Ridge(alpha=0.0001).fit(xtrain, ytrain)
ypred_ridge = ridge.predict(xtest)
score_ridge = ridge.score(xtest, ytest)
mse_ridge = mean_squared_error(ytest, ypred)
print("Final Result: Ridge R2:{0:.3f}, MSE:{1:.2f}, RMSE:{2:.2f}"
.format(score_ridge, mse_ridge, np.sqrt(mse_ridge)))
Final Result: Ridge R2:0.795, MSE:16.96, RMSE:4.12
최종적으로 테스트 세트에 대해서 Ridge를 가지고 예측한 결과 R2 값은 0.795, MSE는 16.96, RMSE는 4.12로 아까 트레이닝 세트를 가지고 간이로 예측했을 때보다(alpha 0.0001 기준)도 더 좋은 결과를 낸 것을 알 수 있습니다.
나머지 Lasso와 elasticNet 결과는 아래 github에 올라온 코드를 참고해주시면 됩니다.

그림을 그려보면 original boston house price에 대해서 ridge, lasso, elasticNet의 그래프가 겹치는 것을 보아 비슷한 예측 결과를 보였다는 것을 시각적으로 확인해볼 수 있었습니다.
이상으로 Regression 회귀 4인방 Linear, Ridge, Lasso, elasticNet에 대한 정리를 마치겠습니다.
감사합니다 🙂