티스토리 뷰
chapter.3 회귀 알고리즘과 모델 규제¶
03-1 k-최근접 이웃 회귀¶
- k-최근접 이웃 회귀¶
지도 학습 알고리즘은 크게 분류와 회귀로 나뉜다. 분류는 앞서 배웠지만 샘플을 몇 개의 클래스 중 하나로 분류하는 문제이다. 회귀는 클래스 중 하나로 분류하는 것이 아니라 임이의 어떤 숫자를 예측하는 문제이다.
예를 들면 내년도 경제 성장률을 예측하거나 배달이 도착할 시간을 예측하는 것이 회귀 문제이다. 회귀는 정해진 클래스가 없고 임의로 수치를 출력한다.
k-최근접 이웃 회귀는 앞서 배운 k-최근접 이웃 알고리즘과 마찬가지로 분류와 똑같이 예측하려는 샘플에 가장 가까운 샘플 k개를 선택한다. 하지만 회귀이기 때문에 이웃한 샘플의 타깃은 어떤 클래스가 아니라 임의의 수치이다. 이웃 샘플의 수치를 사용해 새로운 샘플 x의 타깃을 예측하는 간단한 방법으로는 이 수치들의 평균을 구하면 된다.
- 데이터 준비¶
예측에 필요한 농어의 데이터를 준비하자.
import numpy as np
perch_length = np.array(
[8.4, 13.7, 15.0, 16.2, 17.4, 18.0, 18.7, 19.0, 19.6, 20.0,
21.0, 21.0, 21.0, 21.3, 22.0, 22.0, 22.0, 22.0, 22.0, 22.5,
22.5, 22.7, 23.0, 23.5, 24.0, 24.0, 24.6, 25.0, 25.6, 26.5,
27.3, 27.5, 27.5, 27.5, 28.0, 28.7, 30.0, 32.8, 34.5, 35.0,
36.5, 36.0, 37.0, 37.0, 39.0, 39.0, 39.0, 40.0, 40.0, 40.0,
40.0, 42.0, 43.0, 43.0, 43.5, 44.0]
)
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]
)
먼저 이 데이터가 어떤 형태를 띠고 있는지 산점도를 그려보자.
import matplotlib.pyplot as plt
plt.scatter(perch_length, perch_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
농어의 길이가 커짐에 따라 무게도 늘어나는 것을 볼 수 있다. 농어 데이터를 머신러닝 모델에 사용하기 전에 훈련 세트와 테스트 세트로 나누자.
from sklearn.model_selection import train_test_split
train_input, test_input, train_target, test_target = train_test_split(
perch_length, perch_weight,random_state=42)
이번 예제에서는 특성을 1개만 사용하므로 수동으로 2차원 배열을 만들어야 한다. 넘파이 배열은 크기를 바꿀 수 있는 reshape() 메서드를 제공한다.
test_array = np.array([1,2,3,4])
print(test_array.shape)
(4,)
test_array는 (4,) 배열인 것을 확인했으니 이제 (2,2) 크기로 바꿔보자.
test_array = test_array.reshape(2,2)
print(test_array.shape)
(2, 2)
이제 이 메서드를 이용해 train_input과 test_input을 2차원 배열로 바꿔보자. train_input의 크기는 (42,)이다. 이를 (42,1)로 바꾸려면 train_input.reshape(42,1)과 같이 사용하면 된다. 넘파이에서는 배열의 크기를 자동으로 지정하는 기능도 제공한다. 크기에 -1을 지정하면 나머지 원소 개수로 모두 채우라는 의미이다.
train_input = train_input.reshape(-1,1)
test_input = test_input.reshape(-1,1)
print(train_input.shape, test_input.shape)
(42, 1) (14, 1)
- 결정계수¶
사이킷런에서 k-최근점 이웃 회귀 알고리즘을 구현한 클래슨느 KNeighborsRegressor이다. 이 클래스의 사용법은 KNeighborsClassifier와 매우 비슷하다.
from sklearn.neighbors import KNeighborsRegressor
knr = KNeighborsRegressor()
# k-최근접 이웃 회귀 모델을 훈련한다.
knr.fit(train_input, train_target)
KNeighborsRegressor()
print(knr.score(test_input, test_target))
0.992809406101064
이 점수는 무엇을 얘기하는 걸까? 분류의 경우는 테스트 세트에 있는 샘플을 정확하게 분류한 개수의 비율이다. 즉 정확도이다. 회귀에서는 정확한 숫자를 맞힌다는 것은 거의 불가능하다. 예측하는 값이나 타깃 모두 임의의 수치이기 때문이다. 회귀의 경우 조금 다른 값으로 평가를 하는데 이 점수를 결정계수라고 부른다. 이름이 조금 어렵지만 계산 방식은 간단하다. 각 샘플의 타깃과 예측한 값의 차이를 제곱하여 더한다. 그다음 타깃과 타깃의 평균의 차이를 제곱하여 더한 값으로 나눈다. 만약 타깃의 평균 정도를 예측하는 수준이라면 결정계수는 0에 가까워지고 예측이 타겟에 가까워지면 1에 가까운 값이 된다.
하지만 정확도처럼 결정계수가 직감적으로 얼마나 좋은지 이해하기는 어렵다. 사이킷런은 아래 코드처럼 sklearn.metrics 패키지 아래 여러가지 측정 도구를 제공한다. 이 중에서 mean_absolut_error는 타깃과 예측의 절댓값 오차를 평균하여 반환한다.
from sklearn.metrics import mean_absolute_error
# 테스트 세트에 대한 예측을 만든다.
test_prediction = knr.predict(test_input)
# 테스트 세트에 대한 평균 절댓값 오차를 계산한다.
mae = mean_absolute_error(test_target, test_prediction)
print(mae)
19.157142857142862
결과에서 예측이 평균적으로 19g 정도 타깃값과 다르다는 것을 알 수 있다. 지금까지는 훈련 세트를 사용해 모델을 훈련하고 테스트 세트로 모델을 평가했다. 그런데 훈련 세트를 사용해 평가해 보면 어떨까? 즉 score()메서드에 훈련세트를 전달하여 점수를 출력해 보는 것이다. 이 값은 테스트 점수와 다를 것이다.
- 과대적합 vs 과소적합¶
앞에서 훈련한 모델을 사용해 훈련 세트의 결정계수를 확인해보자.
print(knr.score(train_input, train_target))
0.9698823289099254
앞에서 테스트 세트를 사용한 점수와 비교해보자. 모델을 훈련 세트에 훈련하면 훈련 세트에 잘 맞는 모델이 만들어진다. 이 모델을 훈련 세트와 테스트 세트에서 평가하면 두 값중 어느것이 더 높을까? 보통 훈련 세트의 점수가 더 높게 나온다. 왜냐하면 훈련 세트에서 모델을 훈련했으므로 훈련 세트에서 더 좋은 점수가 나온다.
만약 훈련 세트에서 점수가 굉장히 좋았는데 테스트 세트에서는 점수가 굉장히 나쁘다면 모델이 훈련 세트에 과대적합 되었다고 말한다. 즉 훈련 세트에만 잘 맞는 모델이라 테스트 세트와 나중에 실전에 투입하여 새로운 샘플에 대한 예측을 만들 때 잘 동작하지 않을 것이다.
반대로 훈련 세트보다 테스트 세트의 점수가 높거나 두 점수가 모두 너무 낮은 경우에는 모델이 훈련 세트에 과소적합 되었다고 말한다.
앞에서 k-최근점 이웃 회귀로 평가한 훈련 세트와 테스트 세트의 점수중 테스트 점수가 높으니 과소적합니다. 이 문제를 어떻게 해결해야 할까? 모델을 조금 더 복잡하게 만들면 된다. 즉 훈련 점수에 더 잘 맞게 만들면 테스트 세트의 점수는 조금 낮아질 것이다. k-최근접 이웃 알고리즘으로 모델을 더 복잡하게 만드는 방법은 이웃의 개수 k를 줄이는 것이다. 이웃의 개수를 줄이면 훈련 세트에 있는 국지적인 패턴에 민감해지고, 이웃의 개수를 늘리면 데이터 전반에 있는 일반적인 패턴을 따를 것이다. 여기에서 사이킷런의 k-최근접이웃 알고리즘의 기본 k값은 5이다. 이를 3으로 낮추어보자.
# 이웃의 개수를 3으로 설정한다.
knr.n_neighbors = 3
# 모델을 다시 훈련한다.
knr.fit(train_input, train_target)
print(knr.score(train_input, train_target))
0.9804899950518966
k의 값을 줄였더니 훈련 결정계수가 높아졌다. 그럼 이제 테스트 세트의 점수를 확인해 보자.
print(knr.score(test_input, test_target))
0.9746459963987609
예상대로 테스트 세트의 점수는 훈련 세트보다 낮아졌으므로 과소적합 문제를 해결하였다. 또한 두 점수의 차이가 크지 않으므로 이 모델이 과대적합 된것 같지도 않다.
출처 : 혼자 공부하는 머신러닝 + 딥러닝
'혼자공부하는 머신러닝+딥러닝' 카테고리의 다른 글
혼자 공부하는 머신러닝+딥러닝(ch4-1 로지스틱 회귀) (0) | 2021.04.30 |
---|---|
혼자 공부하는 머신러닝+딥러닝(ch3-3 특성 공학과 규제) (1) | 2021.04.29 |
혼자 공부하는 머신러닝+딥러닝(ch3-2 선형회귀) (0) | 2021.04.28 |
혼자 공부하는 머신러닝+딥러닝(ch.2) (0) | 2021.04.26 |
혼자 공부하는 머신러닝+딥러닝(ch.1) (0) | 2021.04.25 |