본문 바로가기
Programming/Python

[ML] Feature engineering, 다중회귀, 릿지회귀, 라쏘회귀

Feature enginerring(특성공학): 기존 특성, 데이터를 바탕으로 새로운 특성을 만드는 작업

 

 

이번엔 데이터를 파일에 근거해서 불러온다(pandas)

 

 

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np

mpl.rcParams['font.family'] = 'Malgun Gothic' # 그래프에 한글 설정
mpl.rcParams['axes.unicode_minus'] = False # 그래프에 마이너스 기호 깨지는 문제 해결

df = pd.read_csv('https://bit.ly/perch_csv')  # length, height, width 3열로 된 데이터이다.
perch_full = df.to_numpy()  # df -> numpy 배열로 변환
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]
     )

from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(
                                                   perch_full, perch_weight, random_state=42)

데이터 준비, 훈련, 테스트 셋 분리 내용이다.

 

 

PolynomialFeatures()로 다항식을 바로 만들 수 있다. 

특성 (예제)

from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures() # 객체화하고
poly.fit([[2, 3]])   # 학습할 데이터를 fit 안에 리스트로 넣어준다.
print(poly.transform([[2, 3]]))  # 그리고 transform으로 변형 확정을 한다.

[[1. 2. 3. 4. 6. 9.]]   <- 기본 1 곱셈, 원본 2, 원본 3, 2의 2 * 2, 2 * 3, 3 * 3.       <- 이렇게 구성되어서 다항식을 만든다.

사이킷런 Linear model은 자동 절편 추가 有이므로 1(자동 절편항)을 생략하려면 다음과 같이 작성한다.

물론 1이 있어도 사이킷런은 자동절편을 무시하긴 하지만 여기선 혼돈 피하기위해 명시적 표현함

 

 

include_bias = False 옵션 활성화

poly = PolynomialFeatures(include_bias=False)  #1을 생략
poly.fit([[2, 3]])   # 학습할 데이터를 fit 안에 리스트로 넣어준다.
print(poly.transform([[2, 3]]))  # 그리고 transform으로 변형 확정을 한다.

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

 

 

 

어떻게 해당 자료가 형성되었는지 호출하는 함수 poly.get_feature_names_out()

poly.get_feature_names_out()

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

 

 

 

 

 

데이터에 적용하자

poly = PolynomialFeatures(include_bias=False)  # 객체화
poly.fit(train_input)                # 훈련 데이터 대입
train_poly = poly.transform(train_input)  # 훈련 변형 적용
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)) # 정확도 검증
print(lr.score(test_poly, test_target))

0.9903183436982124

0.9714559911594134

 

확실히 특성 추가로 인해 정확도가 올라갔다.

 

 

더 추가하려면? degree를 통해 최고차항을 지정한다.

poly = PolynomialFeatures(degree=5, include_bias=False)

poly.fit(train_input)
train_poly = poly.transform(train_input)
test_poly = poly.transform(test_input)


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

0.9999999999991098   <- 확실히 훈련 세트 정확도는 높아졌지만

-144.40579242684848  <- 지나친 overfitting이므로 테스트에서는 음수가 출력되었다. 형편없는 결과!

 

print(train_poly.shape)

(42, 55)    전체 데이터 42에서 만들어진 특성 개수가 55개이다. 데이터 < 특성개수 이므로 overfitting은 당연한 결과!

 

 

 이 overfitting 문제를 어떻게 해결해야할까?

 

 

 

regulation(규제): 머신러닝 과도학습 훼방법. linear에서는 특성 계수 작게 만들기. 보통 Ridge, Lasso로 한다.

Ridge regression: 계수 제곱값을 기준으로 규제 적용 (Lasso보다 더 많이 쓰임)

Lasso regression: 계수 절댓값을 기준으로 규제 적용 (0으로 만들 수도 있음)

현재 특성이 여러개이므로 모두 다 표준화를 해야한다. StandardScaler를 사용하여 기본세팅을 만들자.

from sklearn.preprocessing import StandardScaler

ss = StandardScaler() # 객체화
ss.fit(train_poly)  # 훈련; 문제의 5차 과적합 data이다. 

train_scaled = ss.transform(train_poly)
test_scaled = ss.transform(test_poly)  # 변형 적용

해당 data는 모두 [0, 1] 에서 값이 재설정되었다.

 

 

 

Ridge (계수 제곱값 기준)

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

0.9896101671037343     <- train 정확도가 내려갔지만

0.9790693977615398     <- test의 음수 상태가 해결되었다.

 

ridge와 lasso는 alpha 값 수정을 통해 규제 강도 조절 可 <- 자동 최적이 아니고 사람이 지정해야함(hyperparameter)

alpha값이 크면 규제가 크고 작으면 작다.

최적 alpha값을 찾으려면? -> 결정계수(R2) 그래프 그려본다.

 

 

 

 

import matplotlib.pyplot as plt

train_score = []
test_score = []  # score() 결과를 저장할 list 정의

alpha_list = [0.001, 0.01, 0.1, 1, 10, 100]  # 10배 단위로 바뀌는 alpha값

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))  # list에 값 저장 append 함수
    
plt.plot(np.log10(alpha_list), train_score)  #상용로그 np.log10()로 값을 받아야한다.
plt.plot(np.log10(alpha_list), test_score)   # 0.003 -> -3,  0.01 -> -2로 출력됨
plt.legend(['Train score', 'Test score'], loc = 'best')
plt.show()

이 그래프 결과에 따르면 -1(alpha = 0.1) 일 때test score 점수가 제일 좋다.

 

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.9903815817570368
0.9827976465386954

 

train > test 이면서 둘 다 정확도가 높게 나왔다!

 

 

 

 

 

Lasso (계수 절댓값 기준)

from sklearn.linear_model import Lasso  # ridge와 코드 작성방식은 동일하다. 이름만 바뀜

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.9800593698421884

 

default 결과도 ridge default만큼 결과가 좋다. alpha값도 찾아보자 (코드 동일. lasso로 이름만 바뀜)

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) # max_iter: 지정 반복횟수 설정. 기본값은 1000
    lasso.fit(train_scaled, train_target)      # 이 반복횟수가 부족하면 convergence warning 뜸.
    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.legend(['Train score', 'Test score'], loc = 'best')
plt.show()

 

여기서 최적 alpha = 10  (1값)이다. 반영해보자

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    -> 앞선 결과보다 훈련 값은 떨어지면서 test값 상승, 그러나 과적합은 피하였다.

 

 

 

Lasso가 0으로 만든 계수는 총 몇 개인지 알아보자

print(np.sum(lasso.coef_ == 0))  # np.sum()은 True = 1, False = 0 으로 덧셈이 可

40   <- 55개 특성중 40개가 0이 되버렸다! 즉, lasso가 사용한 특성은 15개 뿐이다.

 

이와같이 lasso는 유용한 특성이 무엇인지 골라내는 용도로 사용되기도 한다.

 

 

 

728x90
반응형