본문 바로가기

AI/기계학습(Machine Learning)

[기계학습/ML]7. 회귀 알고리즘(2) - 다중 회귀, 릿지(Ridge), 라쏘(Lasso)

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
반응형