본문 바로가기
Study/혼자 공부하는 머신러닝

혼자하는 공부하는 머신러닝 + 딥러닝 - 특성 공학과 규제

by Wanooky 2022. 3. 9.

앞선 글에서는 고차항을 넣을 때 수동으로 고차항을 넣었다.

 

여러 특성을 사용한 선형 회귀를 다항 회귀라고 부르는데, 사이킷런에서 제공하는 편리한 도구를 사용해서 해보려고 한다.

 

판다스를 이용해 데이터를 불러오려고 한다. 판다스는 다른 카테고리에서 더 자세히 다뤄보겠다.

 

import pandas as pd
df = pd.read_csv('https://bit.ly/perch_csv_data')
perch_full = df.to_numpy()
print(perch_full)

#그리고 타깃 데이터도 생성하겠다.
import numpy as np
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_test_split으로 나눈다.

from sklearn.model_selection import train_test_split
train_input, test_input, train_target, train_input = train_test_split(perch_full, perch_weight, random_state = 42)

이제 새로운 특성들을 만들어보자. 이런 특성들을 만들기 위해 '변환기' 클래스를 사용한다.

대표적인 변환기는  PolynomialFeatures 클래스가 있다.

 

from sklearn.preprocessing import PolynomialFeatures

이 클래스로 간단히 예를 들어보자.

poly = PolynomialFeatures()
poly.fit([[2,3]])
print(poly.transform([[2,3]]))

[[1. 2. 3. 4. 6. 9.]]

2와 3으로 만들 수 있는 조합들이 나온다. 

1이 추가되는 이유는 자동으로 절편을 추가하기 때문에, 처음 PolynomialFeatures(include_bias = False) 로 지정하면

1이 생성되지 않는다.

 

이를 이용하여 트레이닝 셋을 변환해보자.

poly = PolynomialFeatures(include_bias = False)
poly.fit(train_input)
poly.transform(train_input)
print(train_poly.shape)

(42, 9)

9개의 조합이 나오는데, 9개의 조합이 어떤 입력의 조합인지 확인하는 함수는

get_feature_names() 함수이다.

poly.get_features_names()

['x0', 'x1', 'x2', 'x0^2', 'x0 x1', 'x0 x2', 'x1^2', 'x1 x2', 'x2^2']

훈련 셋도 변환기로 변환하고 다중 회귀 모델을 훈련해보자.

test_poly = poly.transform(test_input)

from sklearn.linear_model import LinearRegression
lr = LinearRegression
lr.fit(train_poly, train_target)
print(lr.score(train_poly, train_target))

0.9903183436982124

print(lr.score(test_poly, test_target))

0.9714559911594134

PolynomialFeatures 클래스에 degree를 사용하여 최대 차수를 지정할 수 있다.

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)

lr.fit(train_poly, train_target)
print(lr.score(train_poly, train_target))

0.9999999999991097

print(lr.score(test_poly, test_target))

-144.40579242684848

위 모델은 너무 과대적합이 되어있다. 그래서 별로다.

 

어떤 방법으로 과대적합을 방지할까?

규제를 활용한다. 규제는 머신러닝 모델이 훈련 셋을 너무 과도하게 학습하지 못하도록 하는 것이다.

선형 회귀 모델 같은 경우는 계수의 크기를 작게하는 것이다.

 

규제 적용하기 전에 먼저 정규화를 사용한다

2장에서는 직접 했지만, 여기서는 사이킷런에서 제공하는 StandardScaler 클래스를 이용한다.

 

from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_poly)
train_scaled = ss.transfrom(train_poly)
test_scaled = ss.trainsform(test_poly)

 

선형 회귀에 규제를 추가한 모델은 두가지가 있다.

 

먼저 릿지(Ridge)

릿지는 계수를 제곱한 값을 기준으로 규제를 적용한다.

기본적으로 릿지를 더 선호한다.

 

라쏘(Lasso)

라쏘는 계수의 절댓값을 기준으로 규제를 적용한다.

라쏘는 아예 계수를 0으로 만들 수 있다.

 

먼저 릿지 회귀 먼저 진행해보자.

#릿지 회귀
from sklearn.linear_model import Ridge
ridge = Ridge()
ridge.fit(train_scaled, train_target)
ridge.score(train_scaled, train_target)

0.9896101671037343

ridge.score(test_scaled, test_target)

0.9790693977615397

 

릿지 회귀에서 alpha 값으로 규제의 강도를 조절하는데, alpha 값이 커지면 규제강도가 세진다.

적절한 alpha 값을 찾는 것은 alpha 값에 대한 결정계수 값 그래프를 그리는 것이다.

 

한번 알파 값들을 찾아보자.

import matplotlib.pyplot as plt
train_score = []
test_score = []

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')
ply.show()

파란색이 훈련 셋, 주황색이 테스트 셋이다.

고르는 기준은 두 그래프가 가장 가까워지는 알파값을 택하면 된다. 이때 값은 -1이니까

0.1이 알파값이 된다.

 

ridge = Ridge(alpha = 0.1)
ridge.fit(train_scaled, train_target)
ridge.score(train_scaled, train_target)
ridge.score(test_scaled, test_target)

0.9903815817570366
0.9827976465386926

이번엔 라쏘 회귀.

 

from sklearn.linear_model import Lasso
lasso = Lasso()
lasso.fit(train_scaled, train_target)
lasso.score(train_scaled, train_target)
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 i in alpha_list:
	lasso = Lasso(alpha=i, 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))
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, 10일때다.

 

lasso = Lasso(alpha = 10)
lasso.fit(train_scaled, train_target)
lasso.score(train_scaled, train_target)
lasso.score(test_scaled, test_target)

0.9888067471131867
0.9824470598706695

 

앞에서 라쏘 회귀는 일부 계수 값을 0으로 만들 수도 있다. 이 값이 몇개인지 찾아보자.

print(np.sum(lasso.coef_ == 0))

40

40개의 계수가 0이다.