Post

[혼공머신] 2주차 03-3 특성 공학과 규제

[혼공머신] 2주차 03-3 특성 공학과 규제

본 게시글은 한빛미디어의 혼자 공부하는 머신러닝+딥러닝을 바탕으로 작성되었습니다.

다중 회귀

지난번에 살펴본 linear regression model은 조금 더 세부적으로 분류해 본다면 simple linear regression model(SLR)이라고 말을 했을 것이다. 이것은 하나의 independent variable이 있다는 것이다. 하지만 우리가 살고 있는 세상은 그렇게 쉽게 다룰 수 있는 대상이 아니다. 예를 들어서 학교 성적을 predict 한다고 해보자. 그럼 우리는 independent variable로 공부 시간, 공부 장소, 성격, 학군, 심지어 날씨, 기분 이런 것도 포함해서 분석할 수 있을 것이다. 우리는 이렇게 하나의 variable만 사용하는 것이 아니라, 여러 개의 variable, feature를 사용하는 경우의 회귀를 다중 회귀(Multiple Regression)라고 한다.

그런데 꼭 multiple regression을 써야 하는 것일까? 그냥 단순회귀만 쓰면 안 될까? 라는 귀차니즘이 생길 수 있을 것이다. 그렇다면 아래의 그래프를 살펴보자. 우선 그래프는 x, y, z 세 개의 variable이 있다. 그런데 x, y만 가지고 봤을 때는 어떤 관계가 보이는가? 물론 하나로 잘 뭉쳐있다고 할 수는 있다. 하지만 linear 한 관계는 찾아보기 힘들 것이다.

image

이제 다른 것을 가지고 살펴보자. y와 z를 가지고 살펴봤을 때는 조금 뭔가 linear 한 관계가 보이는 것 같기도 하지만 그래도 조금 아쉽다.

image

그런데 이제 3개의 variable을 모두 사용하여 이리저리 기울이다 보면 전혀 다른 결과가 나온다. 아래의 그래프를 보자. 완벽한 linear한 관계를 보인다.

image

이와 같이 일부의 데이터로는 관계를 파악할 수 없으나, 다른 데이터가 추가되면 관계가 나타나는 경우가 있다. 이러한 경우를 위해 우리는 multiple regression을 사용해야 하는 것이다. 혹시라도 이러한 그래프가 실제로 존재하는 것인지 의문을 품을 수 있으므로 아래에 예쁘게 첨부하였다. 마우스로 이리저리 움직이면서 데이터를 살펴보도록 하자.

특성 공학

그런데 우리는 이렇게 variable이 3개인 경우 visualization을 통해서 관계를 확인할 수 있다. 그러면 만약 4개인 경우에는 어떻게 할까? 아래 도형처럼 어떻게든 4차원을 생각해서 구현한 다음에 분석을 해야할까? 그건 말이 안 된다. 대신 다른 방법을 사용할 수 있다. feature를 서로 곱하거나 더하는 등 서로 결합하여 새로운 feature를 만들어 개별 feature의 수를 줄이는 것이다. 예를 들어 지난번에 살펴본 농어의 데이터를 생각하자면 농어의 길이와 높이를 곱한 크기라는 새로운 feature를 만들어서 처리하는 것이다. 이렇게 기존의 feature를 활용해 새로운 feature를 만들어내는 것을 특성 공학(Feature Engineering)이라고 한다.

image

[이미지 출처: 위키피디아 Four-dimensional space 문서]

이제 농어의 데이터를 활용하여 이 내용을 살펴보자. 이번에는 농어의 길이, 너비, 높이 데이터를 가지고 볼 것이다. 우선 데이터를 입력하자. 교재에서는 read_csv로 파일을 다운로드 받아서 실행하기는 했지만, 인터넷에서 불러오는 방식은 시간이 지나면 자료 손실의 우려가 있기에 나는 저장하거나, 직접 기록하는 것을 선호한다. 그렇기에 친절히 다 적어두었다. 당연히 내가 손으로 다 적은 것은 아니고 ChatGPT에게 시켰다. 이후 우리는 앞으로 계속 보게 될 데이터프레임(Dataframe)으로 변환하였다. Dataframe은 Numpy의 array와 비슷하다고 생각할 수 있다. 하지만 더 많은 기능을 제공하며, 일종의 엑셀 스프레드시트처럼 볼 수 있다고 생각하면 된다. 참고로 Dataframe은 Pandas 패키지를 통해 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import numpy as np
import pandas as pd

length = np.array([8.4, 13.7, 15.0, 16.2, 17.4, 18.0, 18.7, 19.0, 19.6, 20.0, 21.0, 
       21.0, 21.0, 21.3, 22.0, 22.0, 22.0, 22.0, 22.0, 22.5, 22.5, 22.7, 
       23.0, 23.5, 24.0, 24.0, 24.6, 25.0, 25.6, 26.5, 27.3, 27.5, 27.5, 
       27.5, 28.0, 28.7, 30.0, 32.8, 34.5, 35.0, 36.5, 36.0, 37.0, 37.0, 
       39.0, 39.0, 39.0, 40.0, 40.0, 40.0, 40.0, 42.0, 43.0, 43.0, 43.5, 
       44.0])

height = np.array([2.11, 3.53, 3.82, 4.59, 4.59, 5.22, 5.2, 5.64, 5.14, 5.08, 5.69, 
       5.92, 5.69, 6.38, 6.11, 5.64, 6.11, 5.88, 5.52, 5.86, 6.79, 5.95, 
       5.22, 6.28, 7.29, 6.38, 6.73, 6.44, 6.56, 7.17, 8.32, 7.17, 7.05, 
       7.28, 7.82, 7.59, 7.62, 10.03, 10.26, 11.49, 10.88, 10.61, 10.84, 
       10.57, 11.14, 11.14, 12.43, 11.93, 11.73, 12.38, 11.14, 12.8, 11.93, 
       12.51, 12.6, 12.49])

width = np.array([1.41, 2.0, 2.43, 2.63, 2.94, 3.32, 3.12, 3.05, 3.04, 2.77, 3.56, 
       3.31, 3.67, 3.53, 3.41, 3.52, 3.52, 3.52, 4.0, 3.62, 3.62, 3.63, 
       3.63, 3.72, 3.72, 3.82, 4.17, 3.68, 4.24, 4.14, 5.14, 4.34, 4.34, 
       4.57, 4.2, 4.64, 4.77, 6.02, 6.39, 7.8, 6.86, 6.74, 6.26, 6.37, 
       7.49, 6.0, 7.35, 7.11, 7.22, 7.46, 6.63, 6.87, 7.28, 7.42, 8.14, 
       7.6])

weight = np.array([5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0, 110.0,
       115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0, 130.0,
       150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0, 197.0,
       218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0, 514.0,
       556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0, 820.0,
       850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0, 1000.0,
       1000.0])

data = {
    'length': length,
    'height': height,
    'width': width
}

df = pd.DataFrame(data)

이제 dataframe을 만들었고, 지난번에 했던 것처럼 array로 바꾸고, train data와 test data로 나눠주자. 지금부터는 random_state를 42로 적어야겠다. 절대 내가 반골 기질로 0으로 바꿔서 썼지만 일일이 바꾸기 귀찮아져서는 아니다. 42를 많이 쓰는 것도 그냥 관례적으로 많이 쓰는 것이기에 내가 그 관계를 굳이 반해서 얻을 것도 없기 때문이다.

1
2
3
4
from sklearn.model_selection import train_test_split

perch_full = df.to_numpy()
X_train, X_test, y_train, y_test = train_test_split(perch_full, weight, random_state=42)

변환기

이제 우리가 아까 말했던 feature engineering을 할 것이다. Scikit-learn에서는 특성 전처리를 위한 메소드가 있는데, 이것을 변환기(Transformer)라고 한다.

원래라면 여기서 계속 설명을 이어 나갔어야 헸을 것이다. 그러나, 노트북에 에러가 자꾸 생기는 비극이 발생해 노트북을 초기화했으나, 이후에 작성한 설명 부분을 실수로 저장하지 않고 떠나보내 버렸다. 너무나 안타까운 일이 발생한 것이다. 하지만 어쩔 수 없다. 커밋을 하지 않은 내 죄니 내가 처리해야 한다. 잠시 구구절절한 사연 낭독 시간이 있었다. 다시 transformer 이야기로 돌아가자.

지난번에 우리가 사용했던 아주 간단한 transformer가 있다. Polynomial regression을 우리는 이미 사용했었다. 이것을 우리는 조금 더 편리하게 사용하기 위해 scikit-learn 패키지를 이용하여 작성해 볼 것이다.

1
2
3
4
5
6
from sklearn.preprocessing import PolynomialFeatures

poly = PolynomialFeatures(include_bias=False)
poly.fit(X_train)
X_train_poly = poly.transform(X_train)
X_train_poly.shape

(42, 9)

Scikit-learn을 사용해서 polynomal regression을 한 결과, 우리가 입력한 3가지 feature를 가지고 무려 9개의 feature를 만들어냈다. 그런데 지금 상황에서 봤을 때는 그냥 어떻게 해서 9개를 만들었다는 것은 알 수 있는데, 계수라던가, 조합이 어떻게 되는지는 알 수가 없다. 하지만 걱정하지 않아도 된다. 우리의 위대하신 개발자분들은 그런 것도 다 생각해서 이미 만들어두셨다.

1
poly.get_feature_names_out()

array([‘x0’, ‘x1’, ‘x2’, ‘x0^2’, ‘x0 x1’, ‘x0 x2’, ‘x1^2’, ‘x1 x2’, ‘x2^2’], dtype=object)

이제 어떻게 조합을 했는지 나왔다. 우리 일반적으로 프로그래밍을 할 때 인덱스와 같은 것은 0부터 시작한다는 것은 이미 다 알고 있으리라 짐작한다. 따라서 위 내용을 수식으로 작성하면 아마 아래와 같이 될 것이다. 계수는 아직 모르므로 임의로 $\beta_n$으로 작성해 보겠다.

$\beta_1x_0+\beta_1x_1+\beta_2x_2+\beta_3x_0^2+\beta_4x_0x_1+\beta_5x_0x_2+\beta_6x_1^2+\beta_7x_1x_2+\beta_8x_2^2$

이제 train data를 변형시켰으니 test data로 변형할 차례이다. 가끔 이 부분을 까먹어서 처리를 해놓고 결과가 이상하다고 하는 경우가 있는데, 그건 시험 연습을 할 때 객관식 시험을 본다고 객관식 시험 문제만 공부시켜 놓고 정작 실제 시험에서는 서술형만 있는 시험지를 내놓는 것과 같은 것이다. 우리는 착한 사람이기에 그러한 짓은 해서는 안 된다. 꼭 train data를 변형시켰다면, test data도 변형시키도록 하자. 물론 추가적인 predict를 위한 data도 마찬가지이다.

1
X_test_poly = poly.transform(X_test)

이제 학습을 시키고, 점수를 확인할 시간이다. train data로 평가를 했더니 역시나 점수가 잘 나왔다. 그러면 test data에서도 점수가 잘 나오는지 확인해 보자.

1
2
3
4
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(X_train_poly, y_train)
lr.score(X_train_poly, y_train)

0.9903183436982125

일단 train data의 점수보다 낮게 나와 underfitting의 우려는 적은 것으로 보인다. 사실 이 정도 점수는 좋은 점수라고 할 수 있다. 그런데 우리가 이번에 polynomial regression을 사용했기에 사용하지 않았을 때와 비교를 했을 때 어떤 것이 더 좋은지 확인해 보자.

우리가 굳이 polynomial regression을 사용해서 복잡한 모델을 만들었는데, 간단한 linear regression이 더 score이 높다면 굳이 복잡한 것을 쓸 이유는 없다. 우리는 이것을 항상 기억해야 한다. 만약 두 가지 모델이 있을 때, score에 큰 차이가 없을 경우, 더 간단한 모델을 사용하는 것이 좋다. 물론, 무조건 score가 높아야 하는 경우는 단순히 score이 높은 것을 써야 하겠지만 대부분의 경우, 오컴의 면도날과 같이 필요 없이 복잡하게 만드는 것은 우리만 피곤하게 할 뿐이다.

1
lr.score(X_test_poly, y_test)

0.9714559911594155

그냥 linear regression을 썼을 때, 확실히 polynomial regression을 사용했을 때와 train data의 score에서 차이가 보인다. 이제 test data도 살펴보자.

1
2
3
lr0 = LinearRegression()
lr0.fit(X_train, y_train)
lr0.score(X_train, y_train)

0.9559326821885706

test data를 살펴봤을 때 그 결과는 더 확실히 보인다. 무려 약 0.1 차이이다. 이 정도면 데이콘에서 등수를 엄청나게 올릴 수 있는 차이다. 확실히 polynomial regression을 사용하는 것이 더 나은 것으로 보인다.

1
lr0.score(X_test, y_test)

0.8796419177546366

그런데, 우리는 아까 대충 보고 넘어간 수식을 다시 살펴보도록 하자.

$\beta_1x_0+\beta_1x_1+\beta_2x_2+\beta_3x_0^2+\beta_4x_0x_1+\beta_5x_0x_2+\beta_6x_1^2+\beta_7x_1x_2+\beta_8x_2^2$

우리 수식의 최고차항의 차수는 2이다. 즉, 특성의 최대 차수를 2로 설정한 것이다. 우리는 polynomial regression에서 이러한 차수를 변형시켜 가며 학습시킬 수 있다. 즉, 3차 다항식, 4차 다항식으로 점차 차수를 늘려가면서 할 수 있는 것이다. 그런데, 차수를 한 5 정도로 해보자. 물론 이렇게 하면 score가 올라갈 수는 있겠으나 아래를 보면 무려 feature가 55개가 된다. 이걸 만약 그래프를 주고 해석하라고 하면 어떻게 될까? 아마 우리는 진로를 바꾸게 될 것이다. 지난번에 말한 complexity를 높여 interpretability가 지나치게 낮아져 버리는 것이다.

1
2
3
4
5
poly = PolynomialFeatures(degree=5, include_bias=False)
poly.fit(X_train)
X_train_poly = poly.transform(X_train)
X_test_poly = poly.transform(X_test)
X_train_poly.shape

(42, 55)

이제 train data에 대해서 평가를 해보자. 엄청난 숫자가 나왔다. 이 정도면 거의 틀리는 것이 불가능할 것이다. 하지만 overfitting의 냄새가 난다. 이제 test data로 이 냄새가 맞았는지 확인해 보자.

1
2
lr.fit(X_train_poly, y_train)
lr.score(X_train_poly, y_train)

0.9999999999938143

여기서도 엄청난 숫자가 나왔다. 이 정도면 맞힐 것도 틀릴 것이 확실하다. 역시나 overfitting의 냄새가 맞았다. 이렇게 지나치게 complexity를 높이면 overfitting의 우려가 커질 수 있다. 우리는 항상 complexity와 simplicity 사이의 trade-off 관계에 대해서 알고 있어야 한다. Complexity를 높일 경우, accuracy가 높아지지만, 일반화하기 어려워지고, simplicity를 높일 경우, generalization이 높아지지만, accuracy가 낮아지게 된다. 따라서 우리는 이 사이를 조절하는 것이 목표가 될 것이다.

1
lr.score(X_test_poly, y_test)

-144.40744532797535

표준화

지난번에 말을 했듯이, 일반적으로 지나치게 simple 하게 만들어서 문제가 발생하는 경우는 많지 않다. 우리는 accuracy나 score를 높이기 위한 쪽으로 data를 수정하기에 지나치게 complex 하게 되는 경향이 있다. 따라서 이번에는 complexity가 지나치게 높아지지 않도록 하기 위해 제한, 규제(Regularization)를 하는 방법을 살펴볼 것이다.

하지만, 우리는 이러한 경우에도 standardization을 빼먹어서는 안 된다. 우리는 예전에 했던 것 같이 표준점수를 직접 계산해서 넣을 수도 있으나, 우리는 scikit-learn이 있으니 이것을 써먹어야 한다. 패키지의 StandardScaler를 사용해 보자.

1
2
3
4
5
6
from sklearn.preprocessing import StandardScaler

standard_scaler = StandardScaler()
standard_scaler.fit(X_train_poly)
X_train_scaled = standard_scaler.transform(X_train_poly)
X_test_scaled = standard_scaler.transform(X_test_poly)

릿지 회귀

Regularization 방법에는 여러 방법이 있는데, 우선 릿지(Ridge) 방법을 살펴보자. Ridge Regulariation도 수식을 통해 볼 수는 있지만 아마 적는다면 본능적인 거부반응이 나올 것이기에 적지 않도록 하자. 그냥 이러한 것이 있다는 것만 알아두도록 하자. 그래도 간단하게 설명을 하자면, ridge는 계수를 제곱한 값을 바탕으로 하는 방법이다. 몇 번을 쓰는지 모르겠지만 이제 외울 때가 된 듯한 학습 후 점수 확인 과정을 거치자. 이번에는 조금 점수가 낮아졌다.

1
2
3
4
from sklearn.linear_model import Ridge
ridge = Ridge()
ridge.fit(X_train_scaled, y_train)
ridge.score(X_train_scaled, y_train)

0.9896101671037343

Test data에 대해서 평가를 해보니 이번에는 적절한 수가 나왔다. 아까 overfitting 되었던 점수와 비교하면 엄청난 차이라고 할 수 있다.

1
ridge.score(X_test_scaled, y_test)

0.979069397761538

아까 잠깐 언급했지만, ridge는 계수를 제곱한 것을 바탕으로 한다고 했다. 그런데 이 제곱한 것이 모델에 얼마나 영향을 미치게 할지 수치 조정을 통해 조절을 할 수 있다. 여기서 잠깐 파라미터(Parameter)에 대해서 설명하도록 하자.

파라미터

Parameter(또는 Hyperparameter)는 우리가 조절하는 값으로, 모델의 학습 과정에서 방금 본 것과 같이 얼마나 영향을 미치게 할지, 아니면 KNN 알고리즘에서의 $K$값이나 Polynomial Regression에서의 차수와 같이 우리가 지정하는 값을 의미한다. 이러한 값은 흔히 함수에서의 매개변수와 같이 작성하게 되는데, 이 값을 어떻게 지정하느냐에 따라서 지금까지 봤던 것처럼 score에 매우 큰 영향을 미치게 된다. 그런데 한 가지 의문이 들 수 있을 것이다. 그럼 그 값은 우리가 입력해야 한다고 했는데, 그걸 어떻게 고르지? 그냥, 눈 감고 때려 맞춰야 하나?

이것에 대한 대답은 사실 맞다고 할 수도 있고 아니라고 할 수도 있다. 보통 이러한 parameter는 한 가지가 아니라 여러 가지가 존재하며, 그 조합에 따라 다른 결과를 낸다. 하지만 정말 아무런 답이 없는 것은 아니고, 경험적 근거를 통한 적정 수치 범위가 지정되어 있다. 우리가 polynomial regression에서 일반적으로 $d$, 즉 차수를 최대 3, 4로 유지한다고 했던 것처럼, 일반적으로 사용되는 적정 수준이 존재한다. 하지만 그 범위 내에서는 조절이 가능하므로 우리가 이 범위 내에서 최적의 값을 찾아야 한다.

나중에 아마 나올 것이라고 생각이 되지만, 이러한 경우에도 방법은 존재한다. 우리가 범위를 지정하면 부루트 포스 방법으로 적당한 수만큼 랜덤으로 대입하며 최적의 값을 찾거나, 정말 무식하게 모든 값을 대입해 보며 가장 최적의 값을 찾는 방법이 있다. 그런데, 정말 모든 값을 대입해 보며 값을 찾는 것은 가장 최적의 값을 찾을 수는 있겠으나, parameter나, data가 매우 크거나, 학습에 시간이 걸리는 모델의 경우 엄청나게 많은 시간이 걸릴 수 있으며, 정말 하루 종일 돌려야 할 수도 있다. 이렇게 parameter를 조절해 가는 과정을 파라미터 튜닝(Parameter Tuning)이라고 한다.

만약 이것을 원치 않는다면 돈으로 해결하면 된다. 엔비디아의 그래픽카드가 최근 관심이 크게 증가한 것이 이것 때문이다. 그래픽카드에서 지원하는 데이터의 병렬처리를 통해 많은 계산을 빠른 시간 내에 처리할 수 있기에 학습 시간을 비약적으로 감소시키게 된다. 하지만, 이 경우, 쓸만한 그래픽 카드는 1,000만 원부터 시작하므로, 나중에 연구원이나 교수가 되면 사용하기로 하고 우리는 적당히 만족하면서 진행하자. 아니면 돌려놓고 며칠 기다리면 된다.

이제 ridge regularization에서 parameter를 조절하며 학습을 진행해 보자.

1
2
3
4
5
6
7
8
9
score_train = []
score_test = []
params = [0.001, 0.01, 0.1, 1, 10, 100]

for i in params:
       ridge = Ridge(alpha=i)
       ridge.fit(X_train_scaled, y_train)
       score_train.append(ridge.score(X_train_scaled, y_train))
       score_test.append(ridge.score(X_test_scaled, y_test))

그리고 알파에 따라서 score이 어떻게 되는지 그래프로 확인해 보기로 하자. 이때, score는 지난번에 봤던 $R^2$을 사용할 것이다. 이때, 유의해야 할 것은 alpha 값을 0.001, 0.01, 0.1과 같은 식으로 10의 배수로 했는데, 이렇게 되면 0부터 1 사이에 대부분의 값이 있고, 100까지 차이가 너무 나 그래프로 score를 비교하기에 지장이 생길 것이다. 그렇기에 우리는 로그 변환을 사용할 것이다.

1
2
3
4
5
6
7
import matplotlib.pyplot as plt

plt.plot(np.log10(params), score_train)
plt.plot(np.log10(params), score_test)
plt.xlabel('log$_{10}$(alpha)')
plt.ylabel('$R^2$')
plt.show()

image

그래프를 봤을 때, 파란색 선이 train data에 대한 그래프이고, 주황색 선이 test data에 대한 그래프이다. 우리는 여기서 주황색 선을 잘 살펴봐야 한다. alpha 값이 지나치게 적거나 지나치게 높으면 모두 성능이 낮아진다는 것을 확인할 수 있다. 이때, 파란색 선을 보면 오른쪽의 경우 성능이 낮으므로 오른쪽의 성능 저하는 underfitting에 의한 것이고, 왼쪽의 성능 저하는 overfitting에 의한 결과임을 알 수 있다. 또한, alpha=-1에서 test data에 대한 score이 제일 높으므로 -1이 여기서는 최적의 alpha 값임을 알 수 있다.

이러한 방식으로 우리는 parameter를 조절해 가며 overfitting과 underfitting을 막기 위한 방법을 찾는 것이다. 이번에는 라쏘 회귀도 살펴보도록 하자.

라쏘 회귀

Ridge regularization과 마찬가지로 라쏘(Lasso) regularization도 진행해 보자. Lasso Regularization도 ridge와 동일하게 규제를 하는 것이나, lasso는 계수의 절댓값을 기준으로 regulatization을 적용한다. 또한, 계수의 크기를 줄일 때, lasso는 계수를 0까지 줄일 수 있다는 것이 ridge와의 차이점이다. 이는 매우 큰 차이인데, ridge는 아무리 feature가 쓸모가 없더라도 최종 학습까지 가져가야 하는데, lasso는 필요 없는 feature라면 계수를 0으로 바꾸어 차원 축소를 할 수 있다는 것이 다른 점이다. 이것에 대해서는 나중에 기회가 되면 다시 다뤄보기로 하고, 코드로 넘어가자.

우선, 앞서 한 것과 마찬가지로 학습 후, train data에 대한 score를 보자.

1
2
3
4
from sklearn.linear_model import Lasso
lasso = Lasso()
lasso.fit(X_train_scaled, y_train)
lasso.score(X_train_scaled, y_train)

0.989789897208096

Test data에 대한 score도 ridge와 마찬가지로 적절한 것을 확인할 수 있다.

1
lasso.score(X_test_scaled, y_test)

0.9800593698421884

마지막으로, lasso에 대한 parameter를 수정해 가며 score를 비교해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
score_train = []
score_test = []
params = [0.001, 0.01, 0.1, 1, 10, 100]

for i in params:
       lasso = Lasso(alpha=i, max_iter=10000)
       lasso.fit(X_train_scaled, y_train)
       score_train.append(lasso.score(X_train_scaled, y_train))
       score_test.append(lasso.score(X_test_scaled, y_test))

plt.plot(np.log10(params), score_train)
plt.plot(np.log10(params), score_test)
plt.xlabel('log$_{10}$(alpha)')
plt.ylabel('$R^2$')
plt.show()

image

이번 그래프를 봤을 때, ridge와 마찬가지로 오른쪽이 underfitting, 왼쪽이 overfitting의 결과로 인한 성능 저하임을 알 수 있다. 그런데, 이번은 ridge와는 조금 추세가 다른데, underfitting에 의한 영향이 크게 나타남을 알 수 있다. 그리고 최적의 alpha 값은 -1도 나쁘지 않지만, 1임을 알 수 있다.

마지막으로, 아까 말했던 lasso의 특징을 살펴보도록 하자. Ridge와는 다르게 feature의 계수를 0으로 만들어 불필요한 feature를 제거할 수 있다고 했었다. 이게 실제로 이루어졌는지 확인해 보자. 결과를 보니, 3개를 제외하고는 다 사라진 것을 알 수 있다. 이렇게 lasso를 사용하면 불필요한 feature를 제거하여 모델을 조금 더 단순하게 만들 수 있다.

1
lasso.coef_

array([ 0. , 0. , 0. , 0. , 1.19398959, 0. , 0. , 0. , 0. , 0. , 0. , 0. , 80.03201415, 168.24077938, 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ])

This post is licensed under CC BY 4.0 by the author.