AI

[혼자 공부하는 머신러닝+딮러닝] 회귀 알고리즘과 모델 규제 - 특성 공학과 규제

래모 2023. 10. 29. 15:12

다중 회귀

여러 개의 특성을 사용한 선형 회귀를 다중 회귀 라고 부른다

특성이 2개면 타킷값과 함께 3차원 공간을 형성하고 선형 회귀 방정식

타깃 = a * 특성1 + b * 특성2 + 절편

은 평면이 된다

 

이번 에제에서는 농어의 길이뿐만 아니라 농어의 높이와 두께도 함께 사용한다

기존의 특성을 사용해 새로운 특성을 뽑아내는 작업을 특성공학 이라고 한다.

 

사이킷런의 변환기

사이킷런은 특성을 만들거나 전처리하기 위한 다양한 클래스를 제공한다

사이킷런에서는 이런 클래스를 변환기라고 부른다

사이킷런의 모델 클래스에 일관된 fit(), score(), predict() 메서드가 있는 것처럼 변환기 클래스는 모두 fit(), transform() 메서드를 제공한다.

 

이번에 사용해볼건 PolynomialFeatures 변환기 클래스!

from sklearn.preprocessing import PolynomialFeatures

poly = PolynomialFeatures()
poly.fit([[2,3]])
print(poly.transform([[2,3]])) # [[1. 2. 3. 4. 6. 9.]]

여기에서 2개의 특성(원소)을 가진 샘플 [2,3]이 6개의 특성을 가진 샘플

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

로 바꿔었다.

 

PolynomialFeatures 클래스는 기본적으로 각 특성을 제곱한 항을 추가하고 특성끼리 서로 곱한 항을 추가한다

무게 = a * 길이 + b * 높이 + c * 두께 + d * 1

선형 방정식의 절편을 항상 값이 1인 특성과 곱해지는 계수로가 볼 수 있다. 이렇게 보면 특성은 (길이, 높이, 두께, 1)이 된다. 하지만 사이킷런의 선형 모델은 자동으로 절편을 추가하므로 굳이 이렇게 특성을 만들 필요가 없다. =>include_bias = False로 지정!

 

poly = PolynomialFeatures(include_bias=False)
poly.fit(train_input)
train_poly = poly.transform(train_input) # [[2. 3. 4. 6. 9.]]
print(train_poly.shape) # (42, 9)

 

 

다중 회귀 모델 훈련하기

from sklearn.linear_model import LinearRegression

lr = LinearRegression()
lr.fit(train_poly, train_target)

print(lr.score(train_poly, train_target)) # 0.9903183436982125
print(lr.score(test_poly, test_target)) # 0.9714559911594159

과소적합의 문제는 나타나지 않는 걸로 보인다

 

특성을 더 많이 추가하면 어떨까? 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)) # 0.999999999999769

거의 완벽한 점수다!! test는 어떨까?

 

print(lr.score(test_poly, test_target)) # -144.40490595353674

왓..?

 

특성의 개수를 늘리면 훈련 세트에 대해서는 거의 완벽하게 학습할 수 있지만 훈련 세트에 너무 과대적합되므로 테스트 세트에서는 형편없는 점수를 만들 수 있다

 

특성을 다시 줄여야한다!

 

규제

규제란 머신러닝 모델이 훈련 세트를 너무 과도하게 학습하지 못하도록 훼방하는 것을 말한다

과대적합 하지 않도록!

 

특성의 스케일을 생각해보자

특성의 스케일이 정규화 되지 않은면 여기에 곱해지는 계수 값도 차이가 나게 된다

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

그럼! 규제를 하기 전에 먼저 정규화를 해야한다!

사이킷런에서 제공하는 StandardScaler 클래스를 사용해보자

 

from sklearn.preprocessing import StandardScaler

ss = StandardScaler()
ss.fit(train_poly) 
# 객체 ss 초기화 후 train_poly를 사용해 객체 훈련

train_scaled = ss.transform(train_poly)
test_scaled = ss.transform(test_poly)

 

선형 회귀 모델에 규제를 추가한 모델을 릿지라쏘라고 부른다

릿지계수를 제곱한 값을 기준으로 규제를 하고

라쏘계수의 절댓값을 기준으로 규제를 적용한다

일반적으로 릿지를 조금 더 선호하긴 한다

 

릿지 회귀

릿지와 라소 모두 sklearn.linear_model 패키지 안에 있다

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

선형 회귀에서는 거의 완벽했던 train 점수가 조금 낮아졌다

테스트 점수는 정상으로 돌아왔다

 

릿지와 라쏘 모델을 사용할 때 규제의 양을 임의로 조절할 수 있다.

모델 객체를 만들 때 alpha 매개변수로 규제의 강도를 조절한다

alpha 값이 크면 규제 강도가 세지므로 계수 값을 더 줄이고 조금 더 과소적합되도록 유도한다

alpha 값이 작으면 계수를 줄이는 역할이 줄어들고 선형 회귀 모델과 유사해지므로 과대적합될 가능성이 크다

 

적절한 alpha 값을 찾는 한 가지 방법은 alpha 값에 대한 R^2 값의 크래프를 그려 보는 것이다.

 

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

그래프도 같이 그려볼 것이다!

 

import matplotlib.pyplot as plt

train_score = []
test_score = []

alpha_list = [0.0001, 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.show()

파란색이 훈련세트 이고 주황색이 테스트 세트이다. 

이 그래프의 왼쪽을 보면 훈련 세트와 테스트 세트의 점수 차이가 아주 크다

훈련 세트에는 잘 맞고 테스트 세트에는 형편없는 과대적합 전형적인 모습이다

반대로 오르쪽 편은 훈련 세트와 테스트 세트의 점수가 모두 낮아지는 과소 적합으로 가는 모습을 보인다

 

적절한 alpha 값은 두 그래프가 가장 가깝고 테스트 세트의 점수가 가장 높은 -1 즉 10의 -1승인 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.9903815817570366
0.9827976465386955
'''

 

라쏘 회귀