AI/혼자 공부하는 머신러닝+딥러닝 책

챕터 03 - 3

codehunter 2023. 7. 15. 22:36

이제 다항 회귀로 농어의 무게를 어느정도 예측할 수 있지만, 좀 더 손을 봐서 결정계수와 예측율을 높여보자.

 

사실 3-2 에서 주어진 데이터는 농어의 길이말고 높이와 두께 데이터도 있는데 이걸 활용하면 더 높은 효과를 낼수 있을거라 한다. 3-2에서 하나의 특성을 사용하여 선형회귀 모델을 훈련시켰는데 그 특성을 여러개로 늘려서 다항회귀를 사용했었고 여러 개의 특성을 사용한 선형회귀를 다중회귀라고 해서 차이가 있다.

 

이번 챕터에서는 길이, 무게, 높이 뿐 만아니라 3개의 특성을 각각 제곱해서 추가하고 거기다가 각 특성을 서로 곱해서 또 다른 특성을만든다고 함. 이렇게 기존의 특성을 사용해 새로운 특성을 뽑아내는 작업을 특성 공학이라고 한다.

 

또한 이번에는 넘파이보다 좀 더 데이터 분석기능(주로 행렬)이 강화된 판다스라는 라이스러리도 사용해보자.

농어 데이터를 csv파일 형식으로 받아서 준비한다.

타깃 데이터는 그대로 사용하고 훈련세트, 테스트 세트로 나누자

특성 공학을 이용해 학습해보기 특성 2, 3을 가지고 특성의 가짓수를 늘려보자

특성 2, 3으로 2개의 특성이 있었는데 6개의 특성으로 늘어났다.

PolynomialFeatures 클래스는 기본적으로 각 특성을 제곱한 항을 추가하고 특성끼리 서로 곱한 항을 추가한다. 1은 원래 선형방정식의 절편을 항상 값이 1인 특성과 곱해지는 계수라고 볼 수 있다.

 

사이킷런은 자동으로 절편을 추가하므로 굳이 이런 특성을 만들 필요는 없다고 함. 즉 디폴트임 디폴트값이라서 당연히 없앨수도 있음.

이걸로 원래 계산식에 적용하면

즉 첫번째, 두번째, 세번째 특성에다가

첫번째^2, 두번째^2, 세번째^2 특성이 추가됐고

첫번째 x 두번째, 첫번째 x 세번째, 두번째 x 세번째 특성이 추가된것을 알수있다.

 

이제 테스트 세트도 특성을 적용하고 다시 학습을 해서 점수를 내면

훈련세트 점수가 테스트 세트점수보다 높게 나온걸 확인 할 수 있다.

 

특성을 더 많이 추가할 수 있다. 3제곱, 4제곱, 5제곱까지 특성 만들어보면

특성 가짓수가 무려 55개나 되는걸 알수 있다. 이렇게 특성 가짓수가 많아지면 좋은거 아닌가라고 생각할 수 있는데 훈련세트와 테스트세트로 다시 훈련해보고 결정계수 값을 살펴보자.

보다시피 훈련세트에 대해서는 거의 완벽에 가까운 값이 나오지만 테스트 세트에 대해서는 이상한 값이 나온다. 

즉 훈련세트에 너무 과적합되어 있는 상태이다. 이걸 조절해야 하는데 먼저 특성 개수를 줄이면 당연히 좋아질것이고 규제라는 방법도 있다. 규제는 머신러닝 모델이 훈련세트를 너무 과도하게 학습하지 못하도록 훼방하는 것을 말한다.

 

규제를 적용하기 위해서는 계수값들이 정규화되어 있어야 한다. 2장에서 작업한 평균과 표준편차를 직접 구해서 특성을 표준점수로 바꾸는 방법도 있지만 사이킷런에서 그러한 기능(StandardScaler)을 제공하고 있다.

 

선형 회귀 모델에 규제를 추가한 모델을 릿지와 라쏘라고 부르는데

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

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

일반적으로 릿지를 조금 더 선호한다고 함. 하지만 라쏘는 계수의 크기를 0으로 만들수도 있는 기능이 있어 특성중에 유용한 특성을 골라내는 용도로도 사용한다고 함.

일반 선형회귀때 거의 100% 가까웠던 훈련세트 점수와 - 값이 나왔던 테스트세트 점수가 정상으로 돌아왔다. 많은 특성을 사용했어도 과대적합도 발생하지 않고 테스트세트 점수도 향상되었다.

 

릿지와 라쏘 모델을 사용할때 규제의 양을 임의로 조절할 수 있다고 함. alpha 매개변수로 규제의 강도를 조절하는데 alpha값이 크면 규제강도가 세지므로 계수값을 더 줄이고 조금 더 과소 적합되도록 유도하고 alpha 값이 작으면 계수를 줄이는 역할이 줄어들고 선형회귀 모델과 유사해짐.

적당한 alpha값을 찾으려면 alpha값에 대한 R^2값의 그래프를 그려보면 된다고 함. 훈련세트와 테스트세트의 점수가 가장 가까운 지점이 최적의 alpha값이 됨.

 

alpha 값을 0.001에서 100까지 10배씩 늘려가며 릿지회귀 모델을 훈련한 다음 훈련세트와 테스트세트의 점수를 리스트에 저장

이제 그래프를 그려야 하는데 alpha값을 0.001부터 10배씩 늘렸기 때문에 그대로 그리면 왼쪽이 너무 촘촘해짐 alpha_list에 있는 6개의 값을 동일한 간격으로 나타내기 위해 로그 함수로 바꾸어 지수로 표현한다. 0.001은 -3, 0.01은 -2, 0.1은 -1... 식으로

위가 훈련세트, 아래가 테스트 세트 그래프이다. alpha값이 -3 은 훈련세트와 테스트 세트의 점수 차이가 아주 크고 이후로는 점점 차이가 작아지다가 alpha 값이 -1 은 넘어가면 간격이 다시 벌어진다. 즉 가장 좋은값은 -1 이다. 즉 10^{-1} = 0.1 이다 alpha값을 0.1로 해서 다시 훈련하면

 

이렇게 찾은 -1로 다시 훈련하면 

이 모델은 훈련세트와 테스트 세트의 점수가 비슷하게 모두 높고 과대적합과 과소적합 사이에서 균형을 맞추고 있다.

 

이제 라쏘라는 것도 한번 사용해보는데 사용법은 릿지와 비슷하다.

 

역시 최적값을 찾기 위한 그래프를 그려보면

라쏘는 최적의 alpha 인덱스 값이 1인걸 알수 있다.

릿지 모델과 비슷하게 잘 훈련된 걸 알 수 잇다. 근데 라쏘 모델은 릿지와 다른 특징하나가 있는데 계수값을 아예 0으로 만들 수 있다고 한다. 라쏘 모델의 계수는 coef_ 속성에 저장되어 있다. 이 중 0인 것을 세어보면

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

이상으로 챕터 3장을 마친다.

 

최종코드

## 특성 공학
### 데이터 준비

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

### 훈련세트, 테스트세트로 나누기

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

# 2개의 특성2와 3으로 이루어진 샘플 하나를 적용
poly.fit([[2, 3]])

# [0] = 1, 디폴트의 선형 방정식의 절편값
# [1] = 2, 원래 2
# [2] = 3, 원래 3
# [3] = 4, 2의 제곱
# [4] = 6, 3의 제곱
# [5] = 9, 2x3
print(poly.transform([[2, 3]]))
poly.get_feature_names_out()

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

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

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

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

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

## 규제 사용해보기
### 데이터 준비

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

### 릿지 사용해보기

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 값 찾기

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

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

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

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

 

모델 파라미터 : 릿지나 라쏘 값중에 alpha 값은 자동으로 정해지는게 아니라 사람이 지정을 해야 하는 값이다.

이렇게 머신러닝 모델의 파라미터를 모델 파라미터, 그중에 머신러닝이 학습할 수 없고 사람이 알려줘야 하는 파라미터를 하이퍼 파라미터라고 부른다.

'AI > 혼자 공부하는 머신러닝+딥러닝 책' 카테고리의 다른 글

챕터 04 - 2  (0) 2023.07.21
챕터 04 - 1  (0) 2023.07.17
챕터 03 - 2  (0) 2023.07.15
챕터 03 - 1  (0) 2023.07.10
챕터 02 - 2  (1) 2023.07.08