Machine Learning/Basic

[3-3] 특성 공학과 규제

jwjwvison 2021. 4. 6. 00:42

이 포스팅은 혼자 공부하는 머신러닝 + 딥러닝 책을 공부하고 정리한것 입니다


 다항 회귀로 농어의 무게를 어느 정도 예측할 수 있지만, 여전히 훈련 세트보다 테스트의 점수가 높다. 이 문제를 해결 하기 위해서는 하나의 특성을 사용하여 선형 회귀모델을 훈련시키는 방법이 아닌 여러 개의 특성을 사용한 선형 회귀 방법인 다중 회귀(multiple regression)방법을 사용해야 한다.

 

다중 회귀

이번에는 농어의 길이뿐만 아니라 농어의 높이와 두께도 함께 사용할 것이다. 또한 이전 모델처럼 3개의 특성을 각각 제곱하여 추가한다. 거기다가 각 특성을 서로 곱해서 또 다른 특성을 만들것이다. 이렇게 기존의 특성을 사용해 새로운 특성을 뽑아내는 작업을 특성공학(feature engineering)이라고 부른다

 

 

데이터 준비

import pandas as pd

df=pd.read_csv('https://bit.ly/perch_csv')
perch_full=df.to_numpy()
print(perch_full)

타깃 데이터는 이전과 동일한 방식으로 준비한다

import numpy as np

perch_weigth = 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_weigth,random_state=42)

이 데이터를 사용해 새로운 특성을 만든다

 

 

사이킷런의 변환기

 사이킷런은 특성을 만들거나 전처리하기 위한 다양한 클래스를 제공한다. 사이킷런에서는 이런 클래스를 변환기(transformer) 라고 부른다. 변환기 클래스는 fit(), transform() 메서드를 제공한다. 

 사용할 변환기는 PolynomialFeatures 클래스 이다.

from sklearn.preprocessing import PolynomialFeatures

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

PolynomialFeatures 클래스는 기본적으로 각 특성을 제곱한 항을 추가하고 특성끼리 서로 곱한 항을 추가한다. 1은 왜 추가 되었을까?

사실 선형 방적식의 절편을 항상 값이 1인 특성과 곱해지는 계수라고 볼 수 있다. 이렇게 놓고 보면 특성은 (길이, 높이, 두께, 1) 이 된다. 하지만 사이킷런의 선형 모델은 자동으로 절편을 추가하므로 굳이 이렇게 특성을 만들 필요가 없다. include_bias=False로 지정하여 다시 특성을 변환한다.

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

절편을 위한 항이 제거되고 특성의 제곱과 특성끼리 곱한 항만 추가되었다. 이제 이 방식으로 train_input에 적용한다.

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

get_feature_names() 메서드를 호출하면 9개의 특성이 각각 어떤 입력의 조합으로 만들어졌는지 알려 준다.

poly.get_feature_names()

test_poly=poly.transform(test_input)

 

 

다중 회귀 모델 훈련하기

다중 회귀 모델을 훈련하는 것은 선형 회귀 모델을 훈련하는 것과 같다. 사이킷런의 LinearRegression 클래스를 임포트하고 앞에서 만든 train_poly를 사용해 모델을 훈련시킨다.

from sklearn.linear_model import LinearRegression

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

농어의 길이뿐만 아니라 높이와 두께를 모두 사용했고 각 특성을 제곱하거나 서로 곱해서 다항 특성을 더 추가했다. 특성이 늘어나면 선형회귀의 능력은 매우 강하다는 것을 알 수 있다.

print(lr.score(test_poly,test_target))

테스트 세트에 대한 점수는 높아지지 않았지만 농어의 길이만 사용했을 때 있던 과소적합 문제는 더이상 나타나지 않았다. 

 

특성을 더 많이 추가하면 어떨까? 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)

만들어진 특성의 개수가 무려 55개나 된다.

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

print(lr.score(test_poly,test_target))

거의 완벽한 점수였지만 테스트 세트에 대한 점수는 음수이다. 무슨 의미일까?

특성의 개수를 크게 늘리면 선형 모델은 아주 강력해진다. 후년 세트에 대해 거의 완벽하게 학습할수 있다. 그러나 이런 모델은 훈련 세트에 너무 과대적합되므로 테스트 세트에서는 형편없는 점수를 만든다.

 

규제

규제(regularzation)는 머신러닝 모델이 훈련 세트를 너무 과도하게 학습하지 못하도록 훼방하는 것을 말한다. 즉 모델이 훈련 세트에 과대적합되지 않도록 만드는 것이다. 선형 회귀 모델의 경우 특성에 곱해지는 계수(또는 기울기) 의 크기를 작게 만드는 일이다. 다음 그림을 보면 왼쪽은 훈련 세트를 과도하게 학습했고, 오른쪽은 기울기를 줄여 보다 보편적인 패턴을 학습한 상황이다.

 앞서 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가 준비되었다.

선형회귀 모델에 규제를 추가한 모델을 릿지(ridge) 와 라쏘(lasso)라고 부른다. 두 모델은 규제를 가하는 방법이 다르다. 릿지는 계수를 제곱한 값을 기준으로 규제를 적용하고, 라쏘는 계수의 절대값을 기준으로 규제를 적용한다. 일반적으로 릿지를 조금 더 선호한다. 두 알고리즘 모두 계수의 크기를 줄이지만 라쏘는 아예 0으로 만들 수도 있다.

 

릿지 회귀

릿지와 라쏘 모두 sklearn.linear_model 패키지 않에 있다.

from sklearn.linear_model import Ridge

ridge=Ridge()
ridge.fit(train_scaled,train_target)
print(ridge.score(train_scaled,train_target))

print(ridge.score(test_scaled,test_target))

테스트 점수가 정상으로 돌아왔다. 릿지와 라쏘 모델을 사용할 때 규제의 양을 임의로 조절할 수 있다. 모델 객체를 만들 때 alpha 매개변수로 규제의 강도를 조절한다. alpha값이 작으면 계수를 줄이는 역할이 줄어들고 선현 회귀 모델과 유사해지므로 과대적합될 가능성이 크다.

적절한 alpha값을 찾는 한 가지 방법은 alpha값에 대한 R^2 값의 그래프를 그려보는 것이다.

import matplotlib.pyplot as plt

train_score=[]
test_score=[]

alpha_list=[0.001,0.01,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.show()

적절한 alpha 값은 두 그래프가 가장 가깝고 테스트 세트의 점수가 가장 높은 -1, 즉 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))

 

라쏘 회귀

from sklearn.linear_model import Lasso

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

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))

 

plt.plot(np.log10(alpha_list),train_score)
plt.plot(np.log10(alpha_list),test_score)
plt.show()

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으로 만들 수 있다고 했는데 라쏘 모델의 계수는 coef_ 속성에 저장되어 있다. 이중 0인것을 헤아려 보면

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

55개의 특성을 모델에 주입했지만 라쏘 모델이 사용한 특성은 15개 밖에 되지 않는다. 이런 특징 때문에 라쏘 모델을 유용한 특성을 골라내는 용도로도 사용할 수 있다.

 

 

결론


 선형 회귀 알고리즘을 사용해 농어의 무게를 예측하는 모델을 훈련시켰지만 훈련 세트에 과소적합되는 문제가 발생했다. 이를 위해 농어의 길이뿐만 아니라 높이와 두께도 사용하여 다중 회귀 모델을 훈련시켰다. 또한 다항 특성을 많이 추가하여 훈련 세트에서 거의 완벽에 가까운 점수를 얻는 모델을 훈련했다. 특성을 많이 추가하면 선형 회귀는 매우 강력한 성능을 낸다. 그러나 특성이 너무 많으면 선형 회귀 모델을 제약하기 위한 도구가 필요하다.

 이를 위해 릿지 회귀와 라쏘 회귀가 있다. 이 모델의 규제 양을 조절하기 위해 최적의 alpha값을 찾아 적용시키면 더 좋다.

'Machine Learning > Basic' 카테고리의 다른 글

[4-2] 확률적 경사 하강법  (0) 2021.04.07
[4-1] 로지스틱 회귀  (0) 2021.04.06
[3-2] 선형 회귀  (0) 2021.04.05
[3-1] k-최근접 이웃 회귀  (0) 2021.04.05
[2-2] 데이터 전처리  (0) 2021.04.04