요약: X(단일 데이터 혹은 배열) 와 y(정답)로 1:1 매칭된 데이터들을 학습(fit) 시켜준 후, 테스트 데이터인 X' 를 주었을 때 기존 학습 데이터를 기반으로 가까운 정답을 내 뱉는다.
KNeighborsClassifier 와의 차이점 : KNeighborsClassifier 의 경우는 기존의 학습 데이터들과 가까운 것들의 다수결로 하나를 정해 결과를 뱉는, 다시 말해 1 or 0 의 데이터를 돌려준다면(분류), KNeighborsRegressor 의 경우는 가까운 점들끼리의 평균값을 돌려준다.(회귀) 가령 0 이나 1 이 아니라 0.99521 같은 실수값을 돌려준다.
예를 들면, KNeighborsClassifier 는 빙어인지 도미인지 구분하기 위해 쓰인다면, KNeighborsRegressor 는 무게가 몇 g 인지 예측하기 위해 쓰인다.
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.show()
훈련 데이터(train)와 테스트 데이터로 나눈다.
train_test_split 함수가 알아서 적당량씩 나눠준다.
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)
print(len(train_input))
print(len(test_input))
print(len(train_target))
print(len(test_target))
출력:
42
14
42
14
어떤 데이터들로 나눠졌는지 확인해 보자.
print(train_input)
print(test_input)
출력:
[19.6 22. 18.7 17.4 36. 25. 40. 39. 43. 22. 20. 22. 24. 27.5
43. 40. 24. 21. 27.5 40. 32.8 26.5 36.5 13.7 22.7 15. 37. 35.
28.7 23.5 39. 21. 23. 22. 44. 22.5 19. 37. 22. 25.6 42. 34.5]
[ 8.4 18. 27.5 21.3 22.5 40. 30. 24.6 39. 21. 43.5 16.2 28. 27.3]
아, 그런데 X 데이터는 fit() 에 넣으려면 2차원 배열 형태여야 한다.
train_input = train_input.reshape(-1, 1)
test_input = test_input.reshape(-1, 1)
print(train_input)
print(test_input)
출력:
[[19.6]
[22. ]
[18.7]
[17.4]
[36. ]
[25. ]
[40. ]
[39. ]
[43. ]
[22. ]
[20. ]
[22. ]
[24. ]
[27.5]
[43. ]
[40. ]
[24. ]
[21. ]
[27.5]
[40. ]
[32.8]
[26.5]
[36.5]
[13.7]
[22.7]
[15. ]
[37. ]
[35. ]
[28.7]
[23.5]
[39. ]
[21. ]
[23. ]
[22. ]
[44. ]
[22.5]
[19. ]
[37. ]
[22. ]
[25.6]
[42. ]
[34.5]]
[[ 8.4]
[18. ]
[27.5]
[21.3]
[22.5]
[40. ]
[30. ]
[24.6]
[39. ]
[21. ]
[43.5]
[16.2]
[28. ]
[27.3]]
이제 fit 시켜보자.
from sklearn.neighbors import KNeighborsRegressor
knr = KNeighborsRegressor()
knr.fit(train_input, train_target)
score1 = knr.score(train_input, train_target)
score2 = knr.score(test_input, test_target)
print(score1) # 훈련 데이터로 평가
print(score2) # 테스트 데이터로 평가
출력:
0.9698823289099254
0.992809406101064
잘 fit 되었고, score 는 0.99 정도 나왔다. 즉, 정확도가 99% 정도 된다는 의미.
그런데, 보통 학습 데이터와 테스트 데이터가 동일하면 100% 나올걸로 예상하지만, 오히려 정확도가 96%로 더 낮았다. 이건 이상한 일이지만 이 모델의 특성이라고 한다. 아무래도 데이터의 평균값으로 결과를 예측하는것이니 이런 결과가 나올 수도 있을 것이다.
그리고, 평균 오차를 구하는 방법이 있다.
from sklearn.metrics import mean_absolute_error
prediction = knr.predict(test_input)
mae = mean_absolute_error(test_target, prediction)
mae
출력:
19.157142857142862
테스트 데이터로 평가했을 때 약 19g 만큼의 오차가 있다고 한다.
fit 시에 n 값(근접 이웃의 숫자)을 조절해서 성능을 조정할 수 있다.
x = np.arange(5, 45).reshape(-1, 1)
for n in [1, 5, 10]:
knr.n_neighbors = n
knr.fit(train_input, train_target)
prediction = knr.predict(x)
plt.scatter(train_input, train_target)
plt.plot(x, prediction)
plt.title(f"n_neighbors = {n}")
plt.xlabel("length")
plt.ylabel("weight")
plt.show()
점으로 표시된 것은 훈련데이터들이고, 선으로 표시된 것은 예측값이다.
가장 위의 그래프를 보면 근접 이웃이 1개라서 그런지 훈련데이터와 동기화 되는 경향이 크다.
반대로 가장 아래 그래프처럼 이웃이 10개면 좀 둔감해지는 경향이 있다.