ML

KNeighborsRegressor 첫 걸음

오즈마스터 2022. 4. 28. 10:33

요약: 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개면 좀 둔감해지는 경향이 있다.