[혼자공부하는 머신러닝 + 딥러닝] 7_특성공학과 규제
6. 특성 공학과 규제
1. 들어가며
- 이전에 다항회귀로 농어의 무게를 어느정도 예측이 가능했지만, 여전히 훈련 세트보다 테스트 세트의 점수가 더 높은 문제가 있었다.
- 선형회귀는 특성이 많을수록 효과가 좋다. 기존에는 1개의 특성을 사용하는 직선형 모델이였다. 2개의 특성을 사용하게되면 평면을 학습한다. 이처럼 고차원에서의 선형회귀는 매우 복잡한 모델을 표현할 수 있다.
- 기존의 특성을 사용해 새로운 특성을 뽑아내는 작업을 특성공학이라고 부릅니다.
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge
from sklearn.linear_model import Lasso
import matplotlib.pyplot as plt
root = 'https://bit.ly/perch_csv_data'
df = pd.read_csv(root)
perch_full = df.to_numpy()
print(perch_full[:5])
[[ 8.4 2.11 1.41]
[13.7 3.53 2. ]
[15. 3.82 2.43]
[16.2 4.59 2.63]
[17.4 4.59 2.94]]
perch_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])
train_input, test_input, train_target, test_target = train_test_split(perch_full, perch_weight, random_state = 42)
- 사이킷런은 특성을 만들거나 전처리하기 위한 다양한 클래스를 제공한다. 이런 클래스를 변환기라고 부른다. 사이킷런의 모델 클래스에 일관된 fit(), score(), predict()메서드가 있는 것처럼 변환기 클래스는 fit(), transform()메서드를 제공한다.
2. 특성공학
poly = PolynomialFeatures(include_bias = False)
poly.fit([[2, 3]])
print(poly.transform([[2,3]]))
[[2. 3. 4. 6. 9.]]
- 특성이 아주 많이 만들어졌다. PolynomialFeatures 클래스는 기본적으로 각 특성을 제곱한 항을 추가하고 특성끼리 서로 곱한 항을 추가한다. 이후 include_bias = False를 추가해 1을 반환해준다.
- 이를통해 절편을 위한 항이 제거되고, 특성의 제곱과 특성끼리 곱한 항만 추가되었다.
poly.fit(train_input)
train_poly = poly.transform(train_input)
print(train_poly.shape)
(42, 9)
- 9개의 특성은 가지는 훈련셋을 만들었다. 해당 특성이 어떻게 만들어 졌는지 알아보고 싶을 경우 get_feature_names() 메서드를 호출하면 9개의 특성이 어떻게 만들어졌는지 알 수 있다.
poly.get_feature_names()
['x0', 'x1', 'x2', 'x0^2', 'x0 x1', 'x0 x2', 'x1^2', 'x1 x2', 'x2^2']
- ‘x0’는 첫번째 특성을 의미하고 ‘x0^2’는 첫 번째 특성의 제곱, ‘x0 x1’은 첫 번째 특성과 두 번째 특성의 곱을 나타내는 형식으로 만들어졌다.
- 밑에서 테스트 세트도 변환해 준다.
test_poly = poly.transform(test_input)
lr = LinearRegression()
lr.fit(train_poly, train_target)
print(lr.score(train_poly, train_target))
print(lr.score(test_poly, test_target))
0.9903183436982124
0.9714559911594134
- 특성이 늘어나면서 점수가 아주 높아졌다. 농어의 길이 뿐 아니라 높이와 두께 모두 사용했고, 각 특성들을 제곱하거나 곱해서 특성을 추가했다.
- 이로써 과소적합의 문제를 해결했다.
- 이를 통해 특성이 늘어나면 선형 회귀 능력은 매우 강하다는 것을 알 수 있다.
- 만약 특성을 더 많이 추가하면 어떻게 될까? 최대 차수를 5제곱까지 특성을 만들어 출력해보자.
poly = PolynomialFeatures(degree=5, include_bias = False)
poly.fit(train_input)
train_poly = poly.transform(train_input)
test_poly = poly.transform(test_input)
print(train_poly.shape)
(42, 55)
- 이를 통해 만들어진 특성이 무려 55개 나 된다. 이를 이용해 다시 선형회귀를 훈련시켜보도록 한다.
lr.fit(train_poly, train_target)
print(lr.score(train_poly, train_target))
print(lr.score(test_poly, test_target))
0.9999999999991097
-144.40579242684848
- 결과를 살펴보면 훈련세트에 대한 점수는 아주 좋다. 하지만 반대로 테스트세트의 점수는 아주 큰 음수가 나온다.
- 이를 통해 알 수 있는것들은 다음과 같다.
- 특성의 개수를 늘리면 선형 모델은 아주 강력해 진다.
- 훈련세트에 대해 거의 완벽하게 학습할 수 있다.
- 하지만 과대적합의 문제가 발생해 테스트 세트에서는 점수가 잘 나오지 않는다.
- 문제를 해결하기 위해서는 특성을 다시 줄이는 방법이 있다. 하지만 이외에도 또 다른 방법이 존재한다.
3. 규제
- 규제는 머신러닝 모델이 훈련 세트를 너무 과도하게 학습하지 못하도록 훼방하는 것을 말한다.
- 선형 회귀 모델에서는 특성에 곱해지는 계수(or 기울기)의 크기를 작게 만드는 것을 말한다.
- 앞서 55개의 특성으로 훈련한 선형 회귀 모델의 계수를 규제하여 훈련 세트의 점수를 낮추고 대신 테스트 세트의 점수를 높여보도록 하겠다.
- 여기서 일단 스케일을 고려해 보아야 한다. 스케일이 정규화 되지 않으면 곱해지는 계수 값도 차이가 나기 때문이다. 계수 값의 크기가 서로 다르면 공정하게 제어되기 힘들기 때문이다.
ss = StandardScaler()
ss.fit(train_poly)
train_scaled = ss.transform(train_poly)
test_scaled = ss.transform(test_poly)
- StandardScaler 클래스의 객체 ss를 초기화한 후 PolynomialFeatures 클래스로 만든 train_poly를 사용해 훈련한다.(테스트 세트도 같이 해줘야 한다!!)
- 선형 회귀 모델에 규제를 추가한 모델을 릿지, 라쏘라고 부른다.
- 릿지는 계수를 제곱한 값을 기준으로 규제를 적용한다.
- 라쏘는 계수의 절댓값을 기준으로 규제를 적용한다.
- 일반적으로는 릿지를 더 선호하는 경향이 있다. 두 알고리즘 모두 계수의 크기를 줄이지만 라쏘는 아예 0을 만들 수도 있다.
1. 릿지 회귀
ridge = Ridge()
ridge.fit(train_scaled, train_target)
print(ridge.score(train_scaled, train_target))
print(ridge.score(test_scaled, test_target))
0.9896101671037343
0.9790693977615386
- 선형 회귀에서 거의 완벽에 가까워던 점수가 조금 낮아졌다. 테스트 세트 점수도 정상으로 돌아왔다.
- 무조건 많은 특성을 사용한다 해서 좋은 성능을 내는 것은 아니다.
- 릿지와 라쏘 모델을 사용할 때 규제의 양을 임의로 조절할 수 있다. 모델 객체를 만들 때 alpha 매개변수로 규제의 강도를 조절한다. alpha값이 크면 규제 강도가 세져 계수 값을 줄이고, 과소적합되도록 유도한다. 반대로 값이 작으면 개수를 줄이는 역할이 줄고, 선형회귀 모델과 유사해지므로 과대적합될 가능성이 크다.
- 적절할 alpha 값을 찾는 한 가지 방법은 alpha 값에 대한 결정계수값의 그래프를 그려보는 것이다.
- 훈련세트와 테스트세트의 점수가 가장 가까운 지점이 최적의 alpha 값이 된다.
train_score = []
test_score = []
- alpha값을 0.001에서 100까지 10배씩 늘려가며 릿지 회귀 모델을 훈련한 다음 훈련 세트와 테스트 세트의 점수를 파이썬 리스트에 저장한다.
alpha_list = [0.001, 0.01, 0.1, 1, 10, 100]
for alpha in alpha_list:
ridge = Ridge(alpha=alpha)
ridge.fit(train_scaled,train_target)
train_score.append(ridge.score(train_scaled, train_target))
test_score.append(ridge.score(test_scaled, test_target))
plt.plot(np.log10(alpha_list), train_score)
plt.plot(np.log10(alpha_list), test_score)
plt.xlabel('alpha')
plt.ylabel('R^2')
plt.show()
- 위는 훈련세트 그래프이며, 아래는 테스트세트 그래프이다. 가장 가깝고 테스트 세트 점수가 가장 높은 -1 즉 0.1이다.
- alpha 값을 0.1로 해서 최종 모델을 훈련하자.
ridge = Ridge(alpha=0.1)
ridge.fit(train_scaled, train_target)
print(ridge.score(train_scaled, train_target))
print(ridge.score(test_scaled, test_target))
0.9903815817570366
0.9827976465386922
- 이 모델은 훈련세트와 테스트세트의 점수가 비슷하게 모두 높고 과대적합과 과소적합 사이에서 균형을 맞추고 있다.
2. 라쏘 회귀
lasso = Lasso()
lasso.fit(train_scaled, train_target)
print(lasso.score(train_scaled, train_target))
print(lasso.score(test_scaled, test_target))
0.989789897208096
0.9800593698421883
- 릿지만큼 좋은 점수를 보여준다. 라쏘 모델도 매개변수를 이용해 규제의 강도를 조절할 수 있다.
train_score = []
test_score = []
alpha_list = [0.001, 0.01, 0.1, 1, 10, 100]
for alpha in alpha_list:
lasso = Lasso(alpha=alpha, max_iter=10000)
lasso.fit(train_scaled,train_target)
train_score.append(lasso.score(train_scaled, train_target))
test_score.append(lasso.score(test_scaled, test_target))
/Users/janghyeseong/Desktop/PR/PR2/lib/python3.9/site-packages/sklearn/linear_model/_coordinate_descent.py:530: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 18778.697957792876, tolerance: 518.2793833333334
model = cd_fast.enet_coordinate_descent(
/Users/janghyeseong/Desktop/PR/PR2/lib/python3.9/site-packages/sklearn/linear_model/_coordinate_descent.py:530: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 12972.821345404844, tolerance: 518.2793833333334
model = cd_fast.enet_coordinate_descent(
plt.plot(np.log10(alpha_list), train_score)
plt.plot(np.log10(alpha_list), test_score)
plt.xlabel('alpha')
plt.ylabel('R^2')
plt.show()
- 라쏘에서는 10이 가장 최적의 알파값이다.
lasso = Lasso(alpha=10)
lasso.fit(train_scaled, train_target)
print(lasso.score(train_scaled, train_target))
print(lasso.score(test_scaled, test_target))
0.9888067471131867
0.9824470598706695
- 모델 훈련이 아주 적절하다. 특성을 많이 사용했지만 릿지와 마찬 가지로 라쏘 모델이 과대적합을 잘 억제하고 테스트 세트의 성능을 높여주었다.
4. 마치며
- 이번 시간에는 특성공학을 통해 새로운 특성을 만들고 이를 통해 보다 많은 특성을 고려해 예측 정도를 올리는 법을 배웠다.
- 이 과정에서 과대 과소 적합문제가 발생하고 이를 해결하기 위해 규제를 사용해 적절하게 특성을 조정하는 방법을 배웠다.
- 이런 규제는 릿지회귀와 라쏘회귀가 존재하고, 각각의 방법을 알아보았다.
- 어려워서 한번 더 읽어봐야겠다.
Leave a comment