728x90
반응형
Pupbani는 다항 회귀로 농어의 무게를 어느 정도 예측할 수 있었다.
하지만 모델이 과소적합이 된 것이 자꾸 신경쓰였다.
이러한 문제점을 해결하려면 제곱보다 고차항을 넣어야하는데 얼만큼 고차항을 넣어야하는지 모르겠고 수동으로 넣는 것도 힘들었다.
그래서 정 선배에게 물어보기로 했다.
정 선배는 다음과 같은 답변을 주었다.
- "길이 데이터만 사용하니까 그렇지! 선형 회귀는 특성이 많을 수록 더 좋다고 ~"
- "높이, 두꼐, 길이를 모두 함께 다항 회귀에 적용해봐"
Pupbani는 선배의 말대로 해보기로 했다.
다중 회귀(Multiple Regression)
- 다중 회귀는 기존에 사용했던 회귀 모델 처럼 1개의 특성을 쓰는 것이 아니라 여러 개의 특성을 사용하는 모델이다.
- 특성의 개수에 따라 모델이 학습하는 것이 틀려진다.
- 1개의 특성 사용 - 직선

- 2개의 특성 사용 - 평면

- 이렇게 2개의 특성을 사용하면 3차원 공간을 형성하고 선형 회귀 방정식이

- 과 같이 평면의 방정식이 된다.
- 특성이 많은 고차원에서는 선형 회귀가 매우 복잡한 모델을 표현할 수 있다.
- 특성 공학(Feature Engineering)
- 기존 특성을 사용해 새로운 특성을 뽑아내는 작업
- Ex) 각 특성을 서로 곱해서 또 다른 특성 만들기
데이터를 준비
- 판다스(Pandas)라는 데이터 분석 라이브러리를 통해 csv 파일을 데이터프레임(dataframe)으로 가져올 수 있다.
- read_csv() 함수를 통해 csv 파일을 불러올 수 있다.
import pandas as pd
// 농어의 길이, 높이, 두께
df = pd.read_csv('https://bit.ly/perch_csv')
- 불러온 데이터는 데이터프레임 타입이기 때문에 numpy로 바꿔야한다.
- to_numpy() 메서드로 데이터프레임을 numpy 배열로 바꿀 수 있다.
// 데이터프레임 -> numpy 배열로
perch_full = df.to_numpy()

- 타겟 데이터인 농어의 무게 데이터도 가져온다.
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)
변환기(Transformer)
- 사이킷런은 특성을 만들거나 전처리하기 위한 다양한 클래스를 제공한다.
- 이 클래스들을 변환기(Transformer)라고 부른다.
- 변환기는 fit(), transform() 메서드를 제공한다.
- Pupbani는 PolynomialFeatures 변환기를 사용하려고 한다.
- sklearn의 preprocessing 패키지에 포함되어 있다.
PolynimialFeatures 변환기 연습
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures()
poly.fit([[2, 3]])
print(poly.transform([[2, 3]]))

- [2,3]으로 이루어진 특성을 변환하면 [1, 2, 3, 4, 6, 9]가 나온다.
- 어떻게 나온 것일까?
- 제일 처음의 1은 고정
- feature1 , feature2, f1의 제곱, f1 * f2, f2의 제곱

- include_bias 매개변수를 False를 지정하면 제일 처음에 1을 제거한 특성이 나온다.
- get_feature_names() 메서드를 호출하면 변환된 특성이 각각 어떤 조합으로 만들어 졌는지 알려준다.
print(poly.get_feature_names())

- 훈련 세트에 적용해 보겠다.
- 사이킷런의 선형 모델은 자동으로 bias를 추가하므로 bias 특성을 만들 필요 없으므로 include_bias를 False로 지정한다.
poly = PolynomialFeatures(include_bias=False)
poly.fit(train_input)
train_poly = poly.transform(train_input)
print(train_poly.shape)

- 테스트 세트에도 적용한다.
test_poly = poly.transform(test_input)
다중 회귀 모델 훈련하기
- 변환기를 적용한 데이터로 선형 회귀 모델을 학습한다.
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(train_poly, train_target)
print(f"훈련 세트 결정계수 : {lr.score(train_poly, train_target).round(3)*100}%")
print(f"테스트 세트 결정계수 : {lr.score(test_poly, test_target).round(3)*100}%")

- 훈련 세트의 점수가 엄청 많이 올라갔다.
- 특성이 많을 수록 선형 회귀의 학습 능력이 많이 올라간다는 것을 알 수 있다.
- 과소적합 문제가 해결되었다.
- 하지만 테스트 세트의 점수와 훈련 세트의 점수가 차이가 좀 나게 되었다. - 과대적합
- 여기서 특성을 더 추가하면 어떻게 될까?
- PolynomialFeatures 클래스의 degree 매개변수 값을 조정하여 몇 차 방정식을 만들지 정할 수 있다.
- degree = 5 : 5차 방정식
poly = PolynomialFeatures(degree=5, include_bias=False)
poly.fit(train_input)
train_poly = poly.transform(train_input)
print(train_poly.shape)

- 특성이 55개가 되었다.
- 학습 후 모델 점수를 구해 보았다.
test_poly = poly.transform(test_input)
lr.fit(train_poly, train_target)
print(f"훈련 세트 결정계수 : {lr.score(train_poly, train_target).round(3)*100}%")
print(f"테스트 세트 결정계수 : {lr.score(test_poly, test_target).round(3)*100}%")

- 테스트 세트의 결정계수가 음수가 나왔다.
- 모델이 훈련 세트에 너무 과대적합되어 테스트 세트에서는 형편 없는 점수를 만든 것이다.
- 이렇게 과대적합된 모델은 규제를 통해서 과대적합을 줄일 수 있다.
규제(Regularization)
머신러닝 모델이 훈련 세트를 너무 과도하게 학습하지 못하도록 방해하는 것을 말한다.
- 선형 회귀 모델의 경우는 특성에 곱해지는 계수의 크기를 작게 만드는 일

- 먼저 데이터의 스케일을 동일하게 한다.(표준화)
- 스케일이 동일하지 않으면 변환기 사용시 곱해지는 계수 값도 차이가 나기 때문이다.
- 특성 스케일링을 해야한다.
- 사이킷런이 제공하는 대표적인 스케일링 클래스
- 표준화 : StandardScaler
- 정규화 : MinMaxScaler
- 앞에서 사용했던 표준점수(표준화)를 사용한다.
- 사이킷런의 StandardScaler 클래스를 사용하여 쉽게 구할 수 있다.
- PolynomialFeatures 클래스를 사용해 특성의 개수를 늘린 데이터를 표준점수화 한다.
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_poly)
train_scaled = ss.transform(train_poly)
test_scaled = ss.transform(test_poly)
- 표준점수로 변환한 데이터 세트가 준비되었다.
릿지 회귀(Ridge Regression)
- 릿지는 선형 회귀 모델에 규제(L2 Norm)를 추가한 모델이다.
- 계수를 제곱한 값을 기준으로 규제를 적용한다.

- 릿지 회귀 모델로 학습을 해본다.
from sklearn.linear_model import Ridge
ridge = Ridge()
ridge.fit(train_scaled, train_target)
print(f"훈련 세트 결정계수 : {ridge.score(train_scaled, train_target).round(4)*100}%")
print(f"테스트 세트 결정계수 : {ridge.score(test_scaled, test_target).round(4)*100}%")

- 확실하게 과대 적합이 줄어 들었고 테스트 세트에서도 좋은 성능을 유지하고 있다.
- 모델 객체를 만들때 사람이 지정해줘야하는 파라미터가 있는데 이를 "하이퍼 파라미터(Hyperparameter)"라고한다.
- 릿지에서는 alpha라는 값이다.
- alpha 값에 따라 규제 강도가 결정된다. - 커질 수록 규제 강화
- 적절한 alpha 값을 찾는 방법은 alpha 값에 대한 결정 계수의 그래프를 그려 보는 것이다.
- alpha 값을 바꿀 때 마다 score() 메서드의 결과를 저장할 리스트
import matplotlib.pyplot as plt
train_score = []
test_score = []
- alpha 값을 0.001 ~ 100까지 10배 씩 늘려가면서 결정계수 값을 저장한다.
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))
- 이렇게 만들어진 리스트를 시각화한다.
- alpha값을 이대로 그리면 그래프가 너무 촘촘해지기 때문에 log10() 함수로 지수로 표현한다.
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 지점 즉 alpha가 0.1일 때 모델이 가장 좋은 성능을 가진다.
- 이 alpha 값으로 학습을 진행해 본다.
ridge = Ridge(alpha=0.1)
ridge.fit(train_scaled, train_target)
print(f"훈련 세트 결정계수 : {ridge.score(train_scaled, train_target).round(2)*100}%")
print(f"테스트 세트 결정계수 : {ridge.score(test_scaled, test_target).round(2)*100}%")

- 테스트 세트의 점수가 비슷하게 모두 높고 과대적합과 과소적합 사이에서 균형을 맞추고 있다.
라쏘 회귀(Lasso Regression)
- 라쏘는 선형 회귀 모델에 규제(L1 Norm)를 추가한 모델이다.
- 계수의 절댓값을 기준으로 규제를 적용한다.(계수값을 0으로 만들 수 있다.)

- 라쏘 모델로 학습을 해본다.
from sklearn.linear_model import Lasso
lasso = Lasso()
lasso.fit(train_scaled, train_target)
print(f"훈련 세트 결정계수 : {lasso.score(train_scaled, train_target).round(4)*100}%")
print(f"테스트 세트 결정계수 : {lasso.score(test_scaled, test_target).round(4)*100}%")

- 라쏘도 릿지 처럼 alpha 하이퍼 파라미터로 규제를 강화할 수 있다.
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))

- 오른쪽으로 갈 수록 훈련 세트와 테스트 세트의 점수가 좁혀지고 있고 가장 오른쪽은 아주 크게 점수가 떨어진다.(0으로 수렴)
- 최적의 alpha값은 1 즉, 10이다.
- alpha를 10으로 모델을 훈련한다.
lasso = Lasso(alpha=10)
lasso.fit(train_scaled, train_target)
print(f"훈련 세트 결정계수 : {lasso.score(train_scaled, train_target).round(4)*100}%")
print(f"테스트 세트 결정계수 : {lasso.score(test_scaled, test_target).round(4)*100}%")

- 훈련 세트와 테스트 세트의 점수가 거의 유사하다.
- 모델이 잘 훈련된 것 같다.
- 라쏘 모델은 계수가 0으로 수렴할 수 있다.
- 0으로 수렴하는 계수를 찾아보자.
- 라쏘의 계수는 coef_ 속성에 저장되어 있다.
print(f"0으로 수렴하는 계수의 개수 : {np.sum(lasso.coef_ == 0)}개")

이렇게 Pupbani는 특성을 새로만드는 법, 규제를 거는법을 통해 적합한 머신러닝 모델을 개발할 수 있게 되었다.
728x90
반응형
'AI > 기계학습(Machine Learning)' 카테고리의 다른 글
[기계학습/ML]9. 확률적 경사 하강법 - 손실 함수, 에포크 (0) | 2022.10.23 |
---|---|
[기계학습/ML]8. 회귀 알고리즘(3) - 로지스틱 회귀 (0) | 2022.10.23 |
[기계학습/ML]6. 회귀 알고리즘(1) - K-최근접 이웃 회귀, 선형회귀 (0) | 2022.10.23 |
[기계학습/ML]5. 데이터 전처리 - 표준점수, 브로드캐스팅 (0) | 2022.10.22 |
[기계학습/ML]4. 훈련 세트와 테스트 세트 - 샘플링 편향(Numpy 사용) (1) | 2022.10.22 |