[머신러닝] K-최근접 이웃 회귀 개념과 예제
K-최근접 이웃 회귀 개념과 예제
K-최근접 이웃 알고리즘
지도 학습 알고리즘은 분류와 회귀로 나뉜다.
K-최근접 이웃 분류(K-NN Classification)
K-최근접 이웃 분류는 새로운 샘플(초록색 원)이 주어졌을 때 가장 가까운 K개의 샘플들을 보고 샘플이 속하는 클래스를 판단하는 알고리즘이다.
K-최근접 이웃 회귀(K-NN Regression)
K-최근접 이웃 회귀는 새로운 데이터를 클래스 중 하나로 분류하는 것이 아닌 임의의 수치를 예측하는 알고리즘이다.
회귀 : 두 변수 사이의 상관관계를 분석하는 방법
아래 그래프를 보자.
분류와 같이 예측 하려는 샘플에 가장 가까운 K개의 샘플을 선택한다. 하지만 회귀 이므로 주변의 샘플들은 어떤 클래스(그룹)가 아닌 임의의 수치다. 따라서 새로운 샘플(주황색 별)을 예측 할 때는 주변의 수치들의 평균을 구하면 된다.
주황색 별 = (30 + 30 + 2) / 3
계산에 따라 새로운 샘플의 예측 타깃값은 26.6666 이 된다.
데이터 준비하기
이제부터 파이썬에서 K-최근접 이웃 회귀 알고리즘을 사용해 모델을 훈련 시켜 보자. 우선 데이터를 준비 하자.
height를 보고 weight를 예측 하도록 해야 하므로 height가 특성이 되고 weight가 타깃이 된다.
import numpy as np
height = np.array([10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,
20.5,21.0,22.0,23.0,24.0,25.0,26.0,27.5,28.0,29.0,
30.0,31.0,32.0,33.0,34.0,35.0,36.0,37.0,37.5,38.0,
39.0,40.0,41.0,42.0,43.0,44.0,45.0,46.0,47.0,48.0,
49.0,50.0,51.3,52.0,53.0,54.0,55.0,56.0,57.0,58.0
])
weight = np.array([100.0,101.9,102.0,103.0,104.0,105.0,106.0,107.0,108.0,109.0,
110.0,110.0,111.0,120.0,121.0,122.0,124.0,126.0,127.0,128.0,
130.0,135.0,137.0,138.0,140.0,150.0,151.0,155.0,160.0,162.0,
170.0,180.0,190.0,200.0,300.0,400.0,500.0,600.0,700.0,800.0,
850.0,850.0,860.5,870.7,880.0,890.0,900.0,920.0,930.0,950.0
])
산점도 그려보기
산점도를 그려보자. 아래 예제를 실행 시키보면
import matplotlib.pyplot as plt #그래프 그리는 패키지 import
plt.scatter(height, weight, color='red')
plt.xlabel('height')
plt.ylabel('weight')
plt.show()
다음과 같은 그래프가 나온다.
height가 커짐에 따라 weight도 늘어나는 것을 확인 할 수있다.
훈련 세트와 테스트 세트 나누기
train_test_split 메서드를 사용하여 훈련세트와 테스트 세트를 나눠 주자.
random_state를 42로 지정해주면 프로그램을 껐다 켜도 훈련세트와 테스트 세트의 샘플 순서가 유지된다.
from sklearn.model_selection import train_test_split
train_input, test_input, train_target, test_target = train_test_split(
height, weight, random_state=42)
reshape()
사이킷런에서 모델 훈련을 하기 위해서는 훈련 세트를 2차원 배열로 바꿔줘야 한다.
일단 훈련 세트를 2차원 배열로 바꾸기 전에 reshape() 메서드에 대해 알아보자.
넘파이에서 배열의 크기를 바꿀 수 있는 reshape() 메서드를 제공한다.
아래 예제를 읽어보면 이해가 될거다.
reshape() 메서드를 사용하여 1차원 배열을 2차원 배열로 바꾸는 방법이다.
shape() 메서드는 배열의 크기를 반환해 준다.
array = np.array([1,2,3,4])
print(array.shape) #원소 개수 확인
array = array.reshape(2,2)
print(array.shape) #2차원으로 바꾸기
결과 :
(4,)
(2, 2)
훈련 세트 2차원 배열로 바꾸기
총 샘플의 개수는 50개이며 훈련 세트는 37개, 테스트 세트는 13개다.
이때 reshape(37,1) 대신 reshape(-1,1) 로 -1로 지정하면 배열의 크기를 나머지 원소 개수로 자동으로 채울 수 있다.
결과를 보니 2차원 배열로 잘 바뀌었다.
train_input = train_input.reshape(-1,1)
test_input = test_input.reshape(-1,1)
print(train_input.shape, test_input.shape)
결과 : (37, 1) (13, 1)
모델 훈련 시키기
사이킷런의 KNeighborsRegressor 클래스를 import 한다.
K-최근접 이웃 분류와 마찬가지로 fit()메서드로 모델을 훈련을 시킨다.
from sklearn.neighbors import KNeighborsRegressor
knr = KNeighborsRegressor()
knr.fit(train_input, train_target)
모델 평가하기
score() 메서드를 사용하여 모델을 평가해보자.
print(knr.score(test_input, test_target))
결과 : 0.9931400780315257
점수가 0.9931400780315257이 나왔다.
회귀에서는 임의의 수치를 맞춘다고 했다. 이때 정확한 수치를 맞추는 것은 거의 불가능에 가깝다. 예측하는 값이나 타깃 모두 임의의 수치이기 때문이다.
이 점수는 결정계수(coefficient determination) 또는 R² 이라고 부른다.
점수 계산식은 다음과 같다.
R² = 1 - (타깃 - 예측)²의 합 / (타깃 - 평균)²의 합
만약 타깃의 평균을 예측한다면 R²는 0에 가까워지고 예측이 타깃에 가까워지면 1에 가까운 값이 된다.
평균 절댓값 오차 구하기
결정계수 값이 0.99가 나왔다. 이 정도면 좋은 값이다.
이번에는 타깃과 예측의 평균 절댓값 오차를 구해보자.
from sklearn.metrics import mean_absolute_error
test_prediction = knr.predict(test_input)
#평균 절댓값 오차 계산
mae = mean_absolute_error(test_target, test_prediction)
print(mae)
결과 : 15.303076923076922
결과를 보니 15.3 정도 타깃 값과 다르다는 것을 확인 할 수 있다.
훈련 세트 평가하기
테스트 세트가 아닌 훈련세트의 결정계수 점수를 확인해 보자.
print(knr.score(train_input, train_target))
결과 : 0.9931061310417463
이전 테스트 세트의 점수는 0.9931400780315257 이다.
훈련 세트와 비교 하면 테스트 세트의 점수가 더 높다.
이 상황은 좋지 못한 현상이다. 모델은 훈련 세트에서 훈련하고 테스트 세트로 평가 하기 때문에, 훈련 세트가 더 좋은 점수가 나와야 하는 것이 정상이다.
과소적합
이 처럼 훈련 세트보다 테스트 세트의 점수가 높거나 두 점수가 모두 너무 낮은 경우에 과소적합(underfitting)되었다고 말한다. 모델이 너무 단순하거나 훈련세트에서 올바른 훈련이 되지 않아서 발생하는 문제다.
과대적합
과소적합과 반대로 훈련세트의 점수가 굉장히 좋은데, 테스트 세트에서 점수가 심하게 나쁘다면 과대적합(overfitting)되었다고 말한다.
과소적합 해결하기
우리는 지금 과소적합 문제가 발생했다. 이를 해결하기 위해서는 모델을 좀 더 복잡하게 만들거나 훈련세트와 테스트 세트의 크기를 키워야한다.
우리는 현재 훈련세트와 테스트 세트의 크기를 키울순 없으니 모델을 더 복잡하게 만들어야한다.
모델을 더 복잡하게 만들기 위한 방법으로는 이웃의 개수 K개수를 줄이면 된다.
사이킷런의 K개수의 디폴트 값이 5다. 이 값을 3으로 낮춰보자.
knr.n_neighbors=3
K개수도 줄였겠다. 다시 한번 훈련 시켜보자.
knr.fit(train_input, train_target)
print(knr.score(train_input, train_target))
결과 : 0.9961022961438378
오, K개수 줄이기 전인 0.9931400780315257 보다 0.003 정도 결정계수 점수가 잘나온다.
다시 테스트 세트의 점수를 확인해 보자.
print(knr.score(test_input, test_target))
결과 : 0.9953881527291476
결과를 보니 테스트 세트가 훈련 세트보다 낮아져 과소적합 문제를 해결 했다.
어? 테스트 세트가 훈련 세트보다 낮으면 과대적합 아닌가요? 그렇지 않다. 테스트 세트와 훈련 세트가 심하게 차이가 나는 것을 과대적합이라고 본다. 현재 차이는 아주 정상적인 점수다.
전체 소스코드
import numpy as np
height = np.array([10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,
20.5,21.0,22.0,23.0,24.0,25.0,26.0,27.5,28.0,29.0,
30.0,31.0,32.0,33.0,34.0,35.0,36.0,37.0,37.5,38.0,
39.0,40.0,41.0,42.0,43.0,44.0,45.0,46.0,47.0,48.0,
49.0,50.0,51.3,52.0,53.0,54.0,55.0,56.0,57.0,58.0
])
weight = np.array([100.0,101.9,102.0,103.0,104.0,105.0,106.0,107.0,108.0,109.0,
110.0,110.0,111.0,120.0,121.0,122.0,124.0,126.0,127.0,128.0,
130.0,135.0,137.0,138.0,140.0,150.0,151.0,155.0,160.0,162.0,
170.0,180.0,190.0,200.0,300.0,400.0,500.0,600.0,700.0,800.0,
850.0,850.0,860.5,870.7,880.0,890.0,900.0,920.0,930.0,950.0
])
# import matplotlib.pyplot as plt #그래프 그리는 패키지 import
#
# plt.scatter(height, weight, color='red')
#
# plt.xlabel('height')
# plt.ylabel('weight')
# plt.show()
#훈련세트,테스트 세트 나누기. random_stata를 42로 하면 프로그램을 껐다켜도 훈련세트와 테스트 세트의 샘플이 유지된다.
from sklearn.model_selection import train_test_split
train_input, test_input, train_target, test_target = train_test_split(
height, weight, random_state=42)
# 2차원으로 바꾸기
train_input = train_input.reshape(-1,1)
test_input = test_input.reshape(-1,1)
print(train_input.shape, test_input.shape)
#모델 훈련 시키기
from sklearn.neighbors import KNeighborsRegressor
knr = KNeighborsRegressor()
knr.fit(train_input, train_target)
#모델 평가하기
print(knr.score(test_input, test_target))
#절댓값 오차 평균 구하기
from sklearn.metrics import mean_absolute_error
test_prediction = knr.predict(test_input)
#평균 절댓값 오차 계산
mae = mean_absolute_error(test_target, test_prediction)
print(mae)
#훈련 세트 평가하기
print(knr.score(train_input, train_target))
#과소적합 해결하기
knr.n_neighbors=3
#K개수 줄이고 훈련 시키기
knr.fit(train_input, train_target)
print(knr.score(train_input, train_target))
#K개수 줄이고 테스트 세트 평가하기
print(knr.score(test_input, test_target))
참조
혼자 공부하는 머신러닝 + 딥러닝(박해선, 한빛 미디어) : https://hongong.hanbit.co.kr/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9D/
혼자 공부하는 머신러닝 + 딥러닝(깃 허브) : https://github.com/rickiepark/hg-mldl
댓글
댓글 쓰기