Koo's.Co

[ML] K-NeighborsRegressor (K-최근접 이웃 회귀) 본문

DataScience

[ML] K-NeighborsRegressor (K-최근접 이웃 회귀)

kth321 2022. 8. 4. 06:04

1. K-NeighborsRegressor(K-최근접 이웃 회귀 알고리즘)

K-최근접 이웃 모델은 분류 모델뿐만 아니라 회귀 모델도 만들 수 있습니다.

분류 모델에서는 범주형 데이터를 예측한다면, 회귀 모델에서는 연속형 데이터를 예측합니다.

근접한 이웃들의 값을 확인

분류 모델과 마찬가지로 train 데이터가 입력되면 데이터를 저장하고 있다가 새로운 값들이 들어오면 근접한 이웃들의 평균값을 계산해 예측값으로 반환합니다.

 

오늘은 농어의 길이와 무게 데이터를 이용해 K-최근접 이웃 회귀 모델을 공부해보겠습니다.

 

농어의 길이와 무게 데이터

농어의 길이와 무게 데이터. GitHub Gist: instantly share code, notes, and snippets.

gist.github.com

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.figure(figsize=(16, 12))
plt.scatter(perch_length, perch_weight, s=100)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

데이터를 보면 다항 회귀를 적용하면 좋을 것 같은데 오늘은 K-최근접 이웃을 사용해보겠습니다.

from sklearn.model_selection import train_test_split

perch_length = perch_length.reshape(-1, 1)
train_input, test_input, train_target, test_target = train_test_split(perch_length, perch_weight,
                                                    random_state=42)

K-최근접 이웃 모델은 입력 데이터를 2차원 배열로 받기 때문에 먼저 입력 데이터를 2차원 배열로 만들고 테스트 데이터를 나눠주었습니다.

 

이미 가공돼 있는 데이터이고 예측값이 target의 평균으로 계산되기 때문에 정규화를 거치지 않고 바로 모델에 넣어보겠습니다.

from sklearn.neighbors import KNeighborsRegressor

knr = KNeighborsRegressor(n_neighbors=3)
knr.fit(train_input, train_target)
print(knr.predict([[35]])) # 698.666667

모델을 통해 구한 예측치를 확인해보겠습니다.

import matplotlib.pyplot as plt

dists, idxs = knr.kneighbors([[35]]) # 예측치 주위 이웃들의 거리와 인덱스를 반환

plt.figure(figsize=(16, 12))
plt.scatter(train_input, train_target, s=100, color='black')
plt.scatter(train_input[idxs], train_target[idxs], s=100, marker='D') # 입력값의 이웃들
plt.scatter(35, 698.6667, s=100, color='red', marker='^')
plt.xlabel('length', fontsize=20)
plt.ylabel('weight', fontsize=20)
plt.show()

모델이 반환한 예측값과 이웃들을 확인할 수 있다

2. 알고리즘의 문제점

모델을 통해 target을 정상적으로 예측하는 것을 확인했습니다. 하지만 K-최근접 이웃 모델에는 엄청난 문제점이 있습니다. 학습된 데이터는 별도의 처리 없이 모델에 저장되어 있는 상태입니다. 이웃들의 평균값을 계산해 예측값을 반환하는 부분이 문제입니다.

print(knr.predict([[50]])) # 1033.3333
print(knr.predict([[100]])) # 1033.3333

?? 길이에 엄청난 차이가 있지만 둘 다 동일한 예측값을 반환받았습니다.

distances, indexes = knr.kneighbors([[100]])

plt.figure(figsize=(16, 12))
plt.scatter(train_input, train_target, s=100, color='black')
plt.scatter(train_input[indexes], train_target[indexes], marker='D', s=100)
plt.scatter(50, 1033, marker='^', s=100, color='red')
plt.xlabel('length', fontsize=20)
plt.ylabel('weight', fontsize=20)
plt.show()

분명 이전까지는 무게가 길이에 비례하여 증가하였는데 새로운 데이터는 비례관계를 보이지 않습니다. 값이 커져도 근접한 이웃들의 평균값을 예측값으로 사용하기 때문입니다. K-최근접 이웃 모델에서는 학습된 데이터의 범위를 넘어서는 데이터가 새로 유입되면 모델이 정확한 값을 예측하지 못하게 됩니다.


참고서적

혼자 공부하는 머신러닝+딥러닝, 2020, 박해선

위키백과 - K-최근접 이웃 알고리즘

Comments