머신러닝/혼공머신

혼공머신 | Chap 03-3. 특성 공학과 규제

하카데미 2022. 8. 24. 18:30

1. 다중 회귀 multiple regression

여러 개의 특성을 사용한 선형 회귀

 

선형 회귀 모델이 학습하는 것

직선, 평면

 

직선

1개의 특성을 사용했을 때

 

평면

2개이 특성을 사용했을 때

 

특성이 3개일 경우는 상상할 수 없음.

특성이 많은 고차원에서

선형 회귀가 매우 복잡한 모델을 표현할 수 있음.

 

특성 공학 feature engineering

기존의 특성을 사용해 새로운 특성을 뽑아내는 작업

 

2. 데이터 준비

판다스 pandas

데이터 분석 라이브러리

 

데이터프레임 dataframe

판다스의 핵심 데이터 구조.

넘파이 배열로도 바꿀 수 있음.

 

데이터 다운로드

http://bit.ly/perch_csv_data

 

csv 파일

콤마로 나누어져 있는 텍스트 파일

 

import pandas as pd

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

출력 결과의 일부

to_numpy()

데이터프레임을 넘파이 배열로 바꿔주는 메서드

 

 

타깃 데이터

import numpy as np

# http://bit.ly/perch_data
perch_length = np.array([8.4, 13.7, 15.0, 16.2, 17.4, 18.0, 18.7, 19.0, 19.6, 20.0, 21.0,
21.0, 21.0, 21.3, 22.0, 22.0, 22.0, 22.0, 22.0, 22.5, 22.5, 22.7,
23.0, 23.5, 24.0, 24.0, 24.6, 25.0, 25.6, 26.5, 27.3, 27.5, 27.5,
27.5, 28.0, 28.7, 30.0, 32.8, 34.5, 35.0, 36.5, 36.0, 37.0, 37.0,
39.0, 39.0, 39.0, 40.0, 40.0, 40.0, 40.0, 42.0, 43.0, 43.0, 43.5,
44.0])

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)

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)

 

 

3. 사이킷런의 변환기

변환기 transformer

특성을 만들거나 전처리하기 위한 사이킷런의 클래스

 

사이킷런의 변환기 클래스는

fit(), transform() 메서드를 제공

 

추정기 estimator

LinearRegression 같은 사이킷런의 모델 클래스

 

# 사용할 변환기 PolynomialFeatures 클래스
from sklearn.preprocessing import PolynomialFeatures

# 2개의 특성 2와 3으로 이루어진 샘플
poly = PolynomialFeatures()
poly.fit([[2, 3]])
print(poly.transform([[2, 3]]))
# 출력 결과 [[1. 2. 3. 4. 6. 9.]]

 

PolynomialFeatures 클래스

각 특성을 제곱한 항을 추가하고

특성끼리 서로 곱한 항을 추가함.

 

1은 왜 추가되었나?

선형 방정식의 절편을 항상 값이 1인 특성과 곱해지는 계수

1은 왜 추가되었나의 예

사이킷런의 선형 모델은 자동으로 절편을 추가함.

include_bias=False 설정해서 추가하지 않을 수 있음.

# 절편을 포함하지 않는 경우
poly = PolynomialFeatures(include_bias=False)
poly.fit([[2, 3]])
print(poly.transform([[2, 3]]))
# 출력 결과 [[2. 3. 4. 6. 9.]]

 

train_input에 적용

poly = PolynomialFeatures(include_bias=False)
poly.fit(train_input)
train_poly=poly.transform(train_input)
print(train_poly.shape)
# 출력 결과 (42, 9)

poly.get_feature_names_out()
# 출력 결과 ['x0', 'x1', 'x2', 'x0^2', 'x0 x1', 'x0 x2', 'x1^2', 'x1 x2', 'x2^2']

test_poly = poly.transform(test_input)

get_feature_names() → get_feature_names_out() 

9개의 특성이 어떻게 만들어졌는지 확인하는 메서드

* 이슈사항: 메서드명 변경됨.

 

 

4. 다중 회귀 모델 훈련하기

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

 

농어 길이만 사용했을 때의 과소적합 문제 해결!

특성을 더 많이 추가해보자.

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)

lr.fit(train_poly, train_target)
print(lr.score(train_poly, train_target))
# 출력 결과 0.9999999999991098

print(lr.score(test_poly, test_target))
# 출력 결과 -144.40579242684848

 

훈련 세트에 과대적합됨.

특성을 줄여아함.

 

5. 규제

규제 regularization

머신러닝 모델이 훈련 세트를 과도하게 학습하지 못하도록 훼방하는 것.

 

특성의 스케일

특성의 스케일이 정규화되지 않으면

여기에 곱해지는 계수 값도 차이 나게 됨.

일반적으로 선형 회귀 모델에 규제를 적용할 때 계수 값의 크기가 서로 많이 다르면 공정하게 제어되지 않음.

 

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

표준점수로 반환한 값이 담겨있음.

 

StandardScaler 클래스 객체의

mean_ 학습한 평균 저장됨

scale_ 학습한 표준편차 저장됨

 

선현 회귀 모델에 규제를 추가한 모델

릿지 ridge, 라쏘 lasso

 

릿지 ridge

계수를 제곱한 값을 기준으로 규제를 적용

일반적으로 더 선호함.

 

라쏘 lasso

계수의 절대값을 기준으로 규제를 적용

 

릿지와 라쏘 모두 계수의 크기를 줄이지만,

라쏘는 아예 0으로 만들 수도 있음.

 

6. 릿지 회귀

from sklearn.linear_model import Ridge

ridge = Ridge()
ridge.fit(train_scaled, train_target)
print(ridge.score(train_scaled, train_target))
# 출력 결과 0.9896101671037343

print(ridge.score(test_scaled, test_target))
# 출력 결과 0.9790693977615398

 

많은 특성을 사용했음에도 불구하고

훈련 세트에 과대적합되지 않음.

테스트 세트에서도 좋은 성능을 내고 있음.

 

alpha 매개변수로 규제의 강도 조절 가능.

값이 크면 규제 강도가 세지므로

계수 값을 줄이고 과소적합되도록 유도.

값이 작으면 계수를 줄이는 역할이 줄어들고

선형 회귀 모델과 유사해짐.

과대적합 주의!

 

하이퍼파라미터 hyperparameter

사람이 직접 지정해야하는 매개변수.

릿지 모델에서 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, label='train')
plt.plot(np.log10(alpha_list), test_score, label='test')
plt.legend()
plt.show()

그래프의 왼쪽은
훈련 세트와 테스트 세트의 점수 차가 큼.
과대적합의 모습.
그래프의 오른쪽은
훈련 세트와 테스트 세트의 점수가 모두 낮아짐.
과소적합.

 

ridge = Ridge(alpha = 0.1)
ridge.fit(train_scaled, train_target)
print(ridge.score(train_scaled, train_target))
# 출력 결과 0.9903815817570366

print(ridge.score(test_scaled, test_target))
# 출력 결과 0.9827976465386926

 

7. 라쏘 회귀

from sklearn.linear_model import Lasso

lasso = Lasso()
lasso.fit(train_scaled, train_target)
print(lasso.score(train_scaled, train_target))
# 출력 결과 0.9897898972080961

print(lasso.score(test_scaled, test_target))
# 출력 결과 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))

plt.plot(np.log10(alpha_list), train_score, label='train')
plt.plot(np.log10(alpha_list), test_score, label='test')
plt.legend()
plt.show()

lasso = Lasso(alpha = 10)
lasso.fit(train_scaled, train_target)
print(lasso.score(train_scaled, train_target))
# 출력 결과 0.9888067471131867

print(lasso.score(test_scaled, test_target))
# 출력 결과 0.9824470598706695

 

과대적합을 억제하고

테스트 세트의 성능을 높임.

 

계수값이 0인 것 헤아려 보기

print(np.sum(lasso.coef_ == 0))
# 출력 결과 40

np.sum()

배열을 모두 더한 값을 반환