이전 글에서 특성을 길이만 보니 일직선으로만 그래프가 그려서 성능이 좋지않아 길이를 제곱한 특성도 넣었더니 더 잘 예측하는 것을 확인할 수 있었다. 근데 여전히 훈련 셋보다 테스트 셋의 점수가 높았다.(underfitting) 이 문제를 해결하려면 제곱보다 더 고차항을 넣어야 할 것 같은데 얼만큼 더 고차항을 넣어야 할지모르고 수동으로 이렇게 고차항을 넣기도 힘들다. 그래서 가지고 있는 특성끼리를 서로 곱해서 또 다른 새로운 특성을 만드는 작업을 할 수 있다.
이 예제에서는 농어의 길이뿐만 아니라 농어의 높이와 두께도 함께 사용햇다. 그리고 3개의 특성을 각각 제곱하여 추가하고 각 특성을 서로 곱해서 또 다른 특성을 만들었다. 즉 '농어 길이 x 농어 높이'를 새로운 특성으로 만들 것이다. 이렇게 기존의 특성을 사용해 새로운 특성을 뽑아내는 작업을 특성 공학이라고 부른다.
이때까지는 데이터를 직접 하나하나 입력하였다. 하지만 이런 번거로운 작업 대신 판다스를 사용하면 인터넷에서 데이터를 바로 다운로드하여 사용할 수 있다. 데이터프레임은 판다스의 핵심 데이터 구조이다. 넘파이 배열과 비슷하게 다차원 배열을 다룰 수 있지만 훨씬 더 많은 기능을 제공한다. 또 데이터프레임은 넘파이 배열로 쉽게 바꿀 수도 있다.
import pandas as pd
df = pd.read_csv('https://bit.ly/perch_csv_data')
perch_full = df.to_numpy()
print(perch_full)
타깃 데이터는 이전과 동일한 방식으로 준비하고 perch_full과 perch_weight를 훈련 셋과 테스트 셋으로 나눈다.(코드는 이전과 동일하니 생략)
사이킷런은 특성을 만들거나 전처리하기 위한 다양한 클래스를 제공한다. 사이킷런에서는 이런 클래스를 변환기라고 부른다. 사이킷런의 모델 클래스에 일관된 fit(), score(), perdict()메서드가 있는 것처럼 변환기 클래스는 모두 fit(), transform()메서드를 제공한다. 우리가 사용할 변환기는 PolynomialFeatures 클래스이다. 먼저 이 클래스를 사용하는 방법을 알아보자.
특성 2와 3으로 이루어진 샘플 하나를 적용했다. 배열안의 숫자들이 다 특성의 값을 말하는 것인데 특성이 아주 많아진 것을 확인할 수 있다. PolynomialTeatures클래스는 기본적으로 각 특성을 제곱한 항을 추가하고 특성끼리 서로 곱한 항을 추가한다. 2, 3을 각기 제곱한 4와 9가 추가되었고, 2와 3을 곱한 6이 추가되었다. 그렇다면 1은 왜 추가 되었을까? 다음의 식을 한번 보자.
무게 = a x 길이 + b x 높이 + c x 두께 + d x 1
사실 선형 방정식의 절편은 항상 값이 1인 특성과 곱해지는 계수라고 볼 수 있다. 이렇게 놓고 보면 특성은 (길이, 높이, 두께, 1)이 된다. 하지만 사이킷런의 선형 모델은 자동으로 절편을 추가하므로 굳이 이렇게 특성을 만들 필요가 없다. include_bias = False로 지정하여 다시 특성을 변환하겠다.
Q. ' 선형 방정식의 절편은 항상 값이 1인 특성과 곱해지는 계수 '라는 게 무슨 말이야?
A. 선형 방정식에서의 절편이란, 그래프가 y축을 지나는 지점의 값을 의미한다. 예를 들어, 선형 방정식이 `y = ax + b`라면, 여기서 `b`는 절편이 된다. 이 절편은 x가 0일 때 y의 값이며, 선이 y축을 지나는 지점을 결정한다.
예를 들어, 위에서 언급한 선형 방정식 `y = ax + b`를 조금 변형하여 `y = ax + b*1`로 표현할 수 있다. 여기서 `b*1`은 "값이 1인 특성"에 곱해지는 계수(절편)를 나타낸다. 이 방식으로 모든 데이터 포인트에 대해 1의 값을 가진 추가적인 특성을 도입하고, 이 특성에 대한 계수로서 절편 `b`를 모델에 포함시킬 수 있다.
절편을 특성으로 직접 사용하지 않는 이유는 머신러닝 라이브러리들, 특히 사이킷런 같은 라이브러리가 모델에 자동으로 절편을 추가하기 때문이다. 이러한 라이브러리들은 선형 모델을 구현할 때 절편을 모델 파라미터로 자동으로 관리하며, 사용자가 별도로 절편을 특성으로 추가할 필요가 없다.
절편을 위한 항이 제거되고 특성의 제곱과 특성끼리 곱한 항만 추가되었다. (사실 따로 지정하지 않아도 사이킷런 모델은 자동으로 특성에 추가된 절현 항을 무시한다.)
이제 이 방식으로 train_input에 적용하자.
PolynomialFeatures클래스는 9개의 특성이 어떻게 만들어졌는지 확인하는 방법을 제공한다. get_feature_names_out()메서드를 호출하면 9개의 특성이 각각 어떤 입력의 조합으로 만들어졌는지 알려준다.
이제 테스트 셋도 훈련 셋의 특성에 맞도록 변환해준다.
#테스트 셋도 특성수 똑같이 맞춰준다.
test_poly = poly.transform(test_input)
이어서 변환된 특성을 사용하여 다중 회귀 모델을 훈련하자. 다중 회귀 모델을 훈련하는 것은 선형 회귀 모델을 훈련하는 것과 같다. 다만 여러 개의 특성을 사용하여 선형 회귀를 수행하는 것 뿐이다.
훈련 셋 점수를 보니 특성을 늘리니 선형 회귀의 능력이 매우 강해진 것을 확인 할 수 있다. 하지만 테스트 셋에 대한 점수는 높아지지 않았지만 농어의 길이만 사용했을 때 있던 과소적합 문제는 더이상 나타나지 않았다.
특성을 더 많이 추가하면 어떨까? 3, 4제곱 항을 넣는 것처럼. PolynomailFeatures클래스의 degree 매개변수를 사용하여 필요한 고차항의 최대 차수를 지정할 수 있다. 5제곱까지 특성을 만들어 출력해 보자.
만들어진 특성의 개수가 무려 55개나 된다. train_poly배열의 열의 개수가 특성의 개수이다. 훈련 셋에서는 거의 완벽한 점수를 보이고 있지만 테스트 셋에 대한 점수는 형편없음을 알 수 있다. 왜 그런걸까? 특성의 개수를 크게 늘리면 선형 모델은 테스트셋에 꽉 맞게 학습할 수 있다. 하지만 이런 모델은 훈련 세트에만 너무 과대적합되므로 테스트 셋에서는 점수가 낮은 것이다.
이 문제를 해결하려면 다시 특성을 줄여야한다. 적절히 특성 수를 만드는 방법이 필요하다. 이어서 바로 알아보자.
규제
규제는 머신러닝 모델이 훈련 세트를 너무 과도하게 학습하지 못하도록 훼방하는 것을 말한다. 즉 모델이 훈련 세트에 과대적합되지 않도록 만드는 것이다. 선형 회귀 모델의 경우 특성에 곱해지는 계수(또는 기울기)의 크기를 작게 만드는 일이다. 앞서 55개의 특성으로 훈련한 선형 회귀 모델의 계수를 규제하여 휸련 세트의 점수를 낮추고 대신 테스트 세트의 점수를 높여 보자.
그 전에 특성의 스케일도 고려해야한다. 특성의 스케일이 정규화되지 않으면 여기에 곱해지는 계수 값도 차이 나게 된다. 그래서 규제를 적용하기 전에 먼저 정규화를 해야한다. 이전에는 평균과 표준편차를 직접 구해 특성을 표준점수로 바꾸었다. 이번에는 사이킷런에서 제공하는 StandardScaler클래스를 사용할 것이다. 이 클래스도 변환기 중 하나이다.
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_poly)
train_scaled = ss.transform(train_poly)
test_scaled = ss.transform(test_poly)
이제 표준점수로 변환한 train_scaled와 test_scaled가 준비되었다.
선형 회귀 모델에 규제를 추가한 모델을 릿지와 라쏘라고 부른다. 릿지는 계수를 제곱한 값을 기준으로 규제를 적용하고, 라쏘는 계수의 절댓값을 기준으로 규제를 적용한다. 일반적으로 릿지를 조금 더 선호한다.
Q. 제곱한 값을 기준으로, 절댓값을 기준으로 뭘 어쩐다는 거야??
A. 각각의 cost function을 보자.
여기서 MSE는 평균 제곱 오차, 는 모델의 계수, 은 계수의 개수, 는 규제의 강도를 조절하는 하이퍼파라미터이다. 가 크면 규제가 강해져 계수의 값이 작아지고, 모델이 데이터에 덜 민감해지며 과대적합 위험이 줄어든다. 반대로 가 작으면 규제의 영향이 줄어들고, 릿지 회귀 모델은 일반 선형 회귀 모델과 비슷해진다.
라쏘 회귀 역시 를 사용하여 규제의 강도를 조절한다. 라쏘 회귀의 특징 중 하나는 일부 계수를 정확히 0으로 만들 수 있다는 점이다. 이는 모델에서 해당 특성을 완전히 제거하는 효과를 가지며, 이로 인해 라쏘 회귀는 특성 선택(feature selection)이 자연스럽게 이루어지는 모델로 볼 수 있다.
릿지 회귀
테스트 세트 점수가 정상으로 돌아왔다. 확실히 많은 특성을 사용했음에도 불구하고 훈련 세트에 너무 과대적합되지 않아 테스트 세트에서도 좋은 성능을 내고 있다.
릿지와 라쏘 모델을 사용할 때 규제의 강도를 조절하는 alpha값을 조절해보자. alpha값이 크면 규제 강도가 세지므로 계수 값을 더 줄이고 조금 더 과소적합되도록 유도한다. 적절한 alpha 값을 찾는 한 가지 방법은 alpha값에 대한 결정계수값의 그래프를 그려 보는 것이다. 훈련 세트와 테스트 세트의 점수가 가장 가까운 지점이 최적은 alpha값이 된다.
다음 코드는 alpha값을 0.001에서 100까지 10배씩 늘려가며 릿지 회귀 모델을 훈련한 다음 훈련셋과 테스트셋의 점수를 파이썬 리스트에 저장한다.
이제 그래프를 그려보자. alpha값을 0.001부터 10배씩 늘렸기 때문에 이대로 그래프를 그리면 그래프 왼쪽이 너무 촘촘해진다. alpha_list에 있는 6개의 값을 동일한 간격으로 나타내기 위해 로그 함수로 바꾸어 지수로 표현할 것이다. 즉 0.001은 -3, 0.01은 -2가 되는 식이다.
파란색은 훈련 셋 그래프, 아래는 테스트 셋 그래프이다. 이그래프의 왼쪽으로 보면 훈련 세트와 테스트 세트의 점수 차가 아주 크다. 전형적인 과대적합의 모습이다. 반대로오른쪽은 훈련 셋과 테스트 셋의 점수가 모두 낮아지는 과소적합인 모습을 보인다.
적절한 alpha값은 두 그래프가 가장 가깝고 테스트 셋의 점수가 가장 높은 -1, 즉 10의 -1승인 0.1이다. alpha값으 0.1로 하여 최종 모델을 훈련하겠다.
이 모델은 훈련 셋과 테스트 셋의 점수가 비슷하게 모두 높고 과대적합과 과소적합의 균형을 맞추고 있다. 다음으로는 라쏘 모델을 훈련해보도록 하자.
라쏘 회귀
라쏘 모델을 훈련하는 것은 릿지와 매우 비슷하다. Ridge클래스를 Lasso 클래스로 바꾸는 것이 전부이다.
이 그래프도 왼쪽은 과대적합을 보여주고 있고, 오른쪽으로 갈수록 훈련 셋과 테스트 셋의 점수차가 좁혀지고 있다. 가장 오른쪽은 아주 크게 점수가 떨어진다. 이 지점은 과소적합되는 모델일 것이다. 라쏘 모델에서 최적의 alpha값은 1, 즉 10의 1승 10이다. 이 값으로 다시 모델을 훈련하자.
라쏘 모델은 계수 값을 아예 0으로 만들 수 있다. 라쏘 모델의 계수는 coef_속성에 저장되어 있다. 이 중에 0 인 것을 헤아려 보자.
정말 많은 계수가 0이 되었다. 55개의 특성을 모델에 주입했지만 라쏘 모델이 사용한 특성은 15개 밖에 되지 않았다. 이런 특징 때문에 라쏘 모델을 유용한 특성을 골라내는 용도로도 사용할 수 있다.
'AI > 혼공파 머신러닝+딥러닝' 카테고리의 다른 글
[ML 05-1] 결정 트리 (0) | 2024.05.15 |
---|---|
[ML 04-2] 확률적 경사 하강법 (0) | 2024.05.09 |
[ML 04-1] 로지스틱 회귀 (0) | 2024.04.04 |
[KNN, ML 03-2] 선형회귀(Linear Regression) (0) | 2024.03.27 |
[KNN 03-1] k-최근접 이웃 알고리즘 (데이터 전처리) (1) | 2024.03.26 |