ML/[책] 혼공머신
3) 회귀 알고리즘과 모델 규제 (1)
spring_sunshine
2022. 10. 21. 14:16
3-1) k-최근접 이웃 회귀
- 지도 학습 알고리즘은 크게 분류와 회귀(regression)로 나뉜다. 회귀는 분류 문제처럼 클래스 중 하나로 분류하는 것이 아니라 임의의 어떤 숫자를 예측하는 것이다.
- 예를 들면 내년도 경제 성장률 예측이나 배달 도착할 시간 예측, 농어 무게 예측 등이 있다.
- 두 변수 사이의 상관관계를 분석하는 방법을 회귀라고 한다.
k-최근접 이웃 회귀
- k-최근접 이웃 분류 알고리즘은 예측하려는 샘플에 가장 가까운 샘플 k개를 선택한 후 샘플들의 클래스를 확인하여 다수 클래스를 새로운 샘플의 클래스로 예측한다.
- k-최근접 이웃 회귀 알고리즘은 분류와 똑같이 예측하려는 샘플에 가장 가까운 샘플 k개를 선택한다. (샘플의 타깃은 어떤 클래스가 아닌 임의의 수치)
- 이 샘플들 수치의 평균을 구해서 새로운 샘플의 타깃을 예측한다.
결정계수(R^2)
- 회귀의 경우, 정확한 숫자를 맞추는 것은 거의 불가능하므로 결정계수라는 값을 통해서 평가한다.
- 사이킷런에서 k-최근접 이웃 회귀 알고리즘을 구현한 클래스는 KNeighborsRegressor이다.
- 객체를 생성하고 fit() 메서드로 회귀 모델을 훈련해보자.
- 회귀에서 평가하는 점수를 결정계수(R^2)라고 부른다.
- 각 샘플의 타깃과 예측한 값의 차이를 제곱하여 더하고, 타깃과 타깃 평균의 차이를 제곱하여 더한값으로 나눈다.
- 타깃이 평균 정도를 예측하는 수준이면 결정계수는 0에 가까워지고, 예측이 타깃에 아주 가까워지면 결정계수는 1에 가까운 값이 된다.
from sklearn.metrics import mean_absolute_error
# 테스트세트에 대한 예측을 만듦
test_prediction = knr.predict(test_input)
# 테스트 세트에 대한 평균 절댓값 오차를 계산
mae = mean_absolute_errpr(test_target, test_prediction)
print(mae) // 19.15714...
Overfitting(과대적합) vs Underfitting(과소적합)
- 일반적으로 훈련세트의 점수가 테스트세트의 점수보다 조금 더 높다. 훈련세트와 테스트세트의 점수 차는 작을수록 좋다.
- 훈련세트에서 점수가 좋다가 테스트세트에서 점수가 안좋을 시에 모델이 훈련세트에 과대적합 되었다고 말한다.
- 반대로 훈련세트보다 테스트세트의 점수가 높거나, 두 점수가 모두 낮은 경우는 모델이 훈련세트에 과소적합 되었다고 말한다. 즉 모델이 너무 단순하여 훈련세트에 적절히 훈련되지 않은 경우이다.
- 과소적합이 나타나는 이유: 또다른 원인은 훈련세트와 테스트세트의 크기가 매우 작기 때문이다. 데이터가 작으면 훈련세트의 특징을 따르지 못할 수 있다.
앞서 k-최근접 이웃 회귀로 평가한 점수는 훈련세트보다 테스트세트의 점수가 높으므로 과소적합이다. 이를 해결하기 위해 모델을 좀 더 복잡하게 만드면 되는데, 그 방법은 이웃의 개수 k를 줄이는 것이다. (과대적합일 경우 k를 늘려 모델을 덜 복잡하게 만든다)
이웃의 개수를 줄이면 훈련세트에 있는 국지적인 패턴에 민감해지고, 이웃의 개수를 늘리면 데이터 전반에 있는 일반적인 패턴을 따를 것이다.
# 이웃의 개수를 3으로 설정한다
knr.n_neighbors = 3
# 모델을 다시 훈련한다
knr.fit(train_input, train_target)
print(knr.score(train_input, train_target)) // 0.9804...
# k값을 줄였더니 훈련세트의 결정계수 점수가 높아짐
# 테스트세트의 점수 확인
print(knr.score(test_input, test_target)) // 0.9748...
3-2) 선형 회귀
이제부터는 k-최근접 이웃회귀와 선형 회귀 알고리즘의 차이를 알아보자.
k-최근접 이웃회귀는 가장 가까운 샘플을 찾아 타깃을 평균하기 때문에, 새로운 샘플이 훈련세트의 범위를 벗어나면 엉뚱한 값을 예측하게 된다. (길이 50cm인 농어 == 길이 100cm인 농어)
선형 회귀
- 선형회귀는 비교적 간단하고 성능이 뛰어나서 맨 처음 배우는 머신러닝 알고리즘 중 하나이다.
- 특성이 하나인 경우 어떤 직선을 학습하는 알고리즘이다. → 특성을 가장 잘 나타내는 직선을 찾자!
coef_와 intercept_를 머신러닝 알고리즘이 찾은 값이라는 의미로 모델 파라미터 라고 부른다.
여기서 사용하는 많은 머신러닝의 훈련 과정은 최적의 모델 파라미터를 찾는 것과 같다. (모델 기반 학습)
앞서 훈련세트를 저장하는 것이 훈련의 전부인 k-최근접 이웃에는 모델 파라미터가 없으며, 이를 사례 기반 학습이라 한다.
다항 회귀
- 선형회귀가 만든 직선이 왼쪽 아래로 쭉 뻗어있다. 그렇다면 농어의 무게가 0g 이하로 내려갈 텐데 있을 수 없는 일이다.
- 농어의 길이와 무게에 대한 산점도를 자세히 보면 일직선보단 왼쪽 위로 조금 구부러진 곡선에 가깝기 때문에 최적의 곡선을 찾는 것이 좋아보인다.
- 2차방정식의 그래프를 그리려면 길이를 제곱한 항이 훈련세트에 추가되어야 한다. 넘파이를 사용하여 간단히 농어의 길이를 제곱해서 원래 데이터 앞에 붙인 2차원 배열로 만들어보자.
train_poly = np.column_stack((train_input**2, train_input))
test_poly = np.column_stack((test_input**2, test_input))
print(train_poly.shape, test_poly.shape) // (42, 2) (14, 2)
이제 train_poly를 사용하여 선형회귀 모델을 다시 훈련하자. 여기서 주목할 점은 2차방정식 그래프를 찾기 위해 훈련세트에 제곱 항을 추가했지만 타깃값은 그대로 사용한다는 점이다. (목표값은 어떤 그래프를 훈련하던 바꿀 필요 x)
학습된 모델의 coef_ 속성은 특성에 대한 계수를 포함한 배열이다. 즉 이 배열의 크기는 특성의 개수와 같다. intercept_ 속성에는 절편이 저장되어 있다.
lr = LinearRegression()
lr.fit(train_poly, train_target)
print(lr.predict([[50**2, 50]])) // [1573.98423528]
## 1장에서 훈련한 모델보다 더 높은 값으로 예측
## 테스트할 때 농어길이의 제곱과 원래 길이를 함께 넣어줘야 함
print(lr.coef_, lr.intercept_) // [ 1.01433211 -21.55792498] 116.0502107827827
→ 이 모델은 무게 = 1.01 * 길이^2 - 21.6 * 길이 + 116.05 라는 다항식 그래프를 학습한 것이다.
→ 훈련세트와 테스트세트에 대한 점수가 크게 높아졌다. 그러나 아직 테스트세트의 점수가 더 높은 것으로 보아 과소적합이 아직 남아있는 듯하다.
선형회귀로 훈련세트 범위 밖의 샘플 예측
- k-최근접 이웃회귀를 사용하여 농어의 무게를 예측했을 때 생기는 큰 문제는 훈련세트 범위 밖의 샘플을 예측할 수 없다는 점이다. 이 회귀는 아무리 멀리 떨어져 있더라도 무조건 가장 가까운 샘플의 타깃을 평균하여 예측하기 때문이다.
- 이를 해결하기 위해 훈련세트에 잘 맞는 직선의 방정식을 찾는 선형회귀를 사용하였고, 사이킷런의 LinearRegression 클래스를 사용하여 모델을 훈련하고 예측할 수 있었다.
- 가장 잘 맞는 직선의 방정식을 찾는 다는 것은 최적의 기울기와 절편을 구한다는 의미이다. 이 값들은 선형회귀 모델의 coef_와 intercept_ 속성에 저장되어 있다. 선형회귀 모델은 훈련세트를 벗어난 범위의 데이터도 잘 예측했다. 그러나 모델이 단순하여 농어의 무게를 음수로 나타낼 수도 있다는 문제가 있다!
- 이를 해결하기 위해 다항회귀를 사용했다. 간단히 농어의 길이를 제곱하여 훈련세트에 추가한 다음 선형회귀 모델을 다시 훈련했다. 이 모델은 2차방정식의 그래프 형태를 학습하였고 훈련세트가 분포된 형태를 잘 표현했다. 또 훈련세트와 테스트세트가 선형회귀보다 훨씬 높아졌다. 그러나 테스트세트의 성능이 조금 높은 거승로 보아 아직 과소적합 경향이 남아있다. 조금 더 복잡한 모델을 만들어 이 문제를 해결해보자.