K-최근접이웃(K-nearest neighbor, KNN)이란?
K-최근접이웃 알고리즘의 아이디어는 아주 간단하다. 각 레코드를 다음과 같이 분류 혹은 예측(KNN 회귀라고도 함)한다.
- 특징들이 가장 유사한(즉, 예측변수들이 유사한) k개의 레코드(이웃)를 찾는다.
- 분류: 이 유사한 레코드들 중에 다수가 속한 클래스가 무엇인지 찾은 후에 새로운 레코드를 그 클래스(반응변수)로 분류한다.
- 예측(회귀): 유사한 레코드들의 평균을 찾아서 새로운 레코드에 대한 예측값으로 사용한다.
이와 같이 KNN 알고리즘으로부터 얻게 되는 분류 혹은 예측의 결과들은 필연적으로 이웃과 유사한 경향을 띄게 된다. 결과값을 얻기 위해, 특징들이 유사한 이웃들의 값을 살펴보고 그들의 클래스를 채택하기 때문이다. (혹은, 그 레코드들의 평균을 채택한다.)
KNN 분류의 구체적인 예를 들자면 다음과 같다. KNN을 통해 분류값(반응변수)을 얻고자 하는 어떤 변수 a가 있다. a는 고려할 이웃의 수인 k를 3으로 놓고, 세 개의 가장 가까운 이웃 점들을 검사한다. 2개의 수치형 예측변수를 각각 x축과 y축으로 좌표평면 상에 점으로 나타낸다고 할 때, 이 이웃 점들은 a와 특징들이 비슷하기 때문에 가까운 거리에 위치하게 된다. 즉, 근접한 이웃이 된다. 이 때, 하나의 이웃이 X 클래스에 속하고 두 이웃은 Y 클래스에 속한다면, 가장 가까운 점의 대다수가 Y 클래스에 속하기 때문에 a의 분류값은 Y가 된다. 이렇게 이웃들의 클래스를 확인해서 과반수 의결에 의해 분류값이 정해진다.
한편, KNN 회귀는 KNN 분류와 동일한 과정을 거쳐 이웃들의 값의 평균을 예측값으로 얻는다. 이 때, 거리가 가까운 이웃일수록 평균에 더 많이 기여하도록 가중치를 주기도 한다. 가장 흔한 가중치 스키마는 d가 이웃까지의 거리일 때 각각의 이웃에게 1/d의 가중치를 주는 것이다.
K-최근접이웃 알고리즘의 특징은 다음과 같다.
- 지도학습 알고리즘이다. 예측변수에 따른 정답 데이터를 제공함으로써 이를 기반으로 새로운 변수의 정답을 찾아가는 방법이기 때문이다.
- 가장 간단한 분류/예측 머신러닝 알고리즘 중의 하나이다. 분류는 물론 회귀도 가능하다는 의미이다.
- 모든 예측변수들은 수치형이어야 한다. 이웃과의 유사성을 판단하기 위해 거리를 계산하기 때문이다.
- Lazy model이다. 일반적인 지도학습 알고리즘은 훈련용 데이터와 테스트 데이터를 통해 학습하고, 모델을 피팅하는 과정이 있는데 K-최근접이웃 알고리즘은 이 과정이 없다. 단지, 특징이 유사한 k개의 이웃을 찾아서 그 이웃들의 클래스를 분류값으로 갖거나, 그 이웃들의 평균을 계산하여 예측값으로 얻는 각 경우의 기반한 (instance-based) 계산을 할뿐이다.
- 특징들이 어떤 척도에 존재하는지, 가까운 정도를 어떻게 측정할 것인지, k를 어떻게 설정할 것인지에 따라 예측결과가 달라진다.
K-최근접이웃의 거리 지표들
앞서 언급한 것처럼 이웃과의 유사성은 거리 지표를 통해 결정된다. 두 벡터 사이에 가장 많이 사용되는 지표는 유클리드 거리(Euclidean distance)와 맨해튼 거리(Manhattan distance)이다. 유클리드 거리는 두 점 사이의 직선 거리라고 볼 수 있다. 반면, 맨해튼 거리는 대각선이 아닌 직각 방향으로만 움직일 때 두 점 사이의 거리이다. 해당 지표는 도심지에서 직사각형 건물들 사이를 이동하는 거리에서 착안되었다고 한다.
그런데 두 벡터 사이의 거리를 측정할 때, 상대적으로 큰 스케일에서 측정된 변수(특징)들은 측정치에 미치는 영향이 클 것이다. 예를 들면, 금액 변수들은 몇만원 단위이지만, 비율을 나타내는 변수들은 소수점 단위로서 상대적으로 거의 거리에 영향을 주지 못할 것이다. 이러한 문제는 데이터 표준화를 통해 해결할 수 있다. 파이썬에서는 이를 위해 scikit learn의 StandardScaler 함수를 사용한다. (from sklearn.preprocessing import StandardScaler)
최적의 k 선택하기
k를 잘 선택하는 것은 KNN의 성능을 결정하는 아주 중요한 요소이다. 일반적으로 k가 너무 작으면 데이터의 노이즈 성분까지 고려하는 과대적합(overfitting) 문제가 발생한다. 반대로 k를 너무 크게 하면 결정함수가 너무 과하게 평탄화되어 (oversmoothing) 데이터의 지역 정보를 예측하는 KNN의 기능을 잃어버리게 된다.
최적의 k값을 찾기 위한 일반적인 규칙은 없지만 대체로 데이터의 상태에 따라 방법을 달리 할 수 있다. 데이터의 노이즈가 거의 없고 아주 잘 구조화된 데이터의 경우 k값이 작을수록 잘 동작한다. 반면, 노이즈가 많은 데이터의 경우 k가 클수록 좋다. k는 보통 1에서 20사이에 놓으며, 동률이 나오는 경우를 막기 위해 보통은 홀수를 사용한다.
파이썬으로 K-최근접이웃 분류하기
sklearn의 내장데이터인 iris 데이터로 KNN 분류를 해보았다. 순서는 다음과 같다.
먼저 sklearn, matplotlib, pandas로부터 필요한 함수들을 import 하고, 데이터를 활용하기 쉽게 데이터프레임화 한다.
그리고 훈련 데이터인 예측 변수들의 데이터를 X에 넣고, 목표 변수인 실제 분류값을 y에 할당한다. 그리고 sklearn의 KNeighborsClassifier 함수를 사용하여 파라미터를 설정한 값을 neigh 객체에 할당한다. neigh에 fit을 적용하여 X, y값을 입력한 후, neigh에 predict를 적용하여 X를 입력하면 KNN 분류값을 얻을 수 있다.
또한, 실제값과 KNN 분류값을 비교하기 위해 데이터프레임화 할 수도 있다.
파이썬에서 KNN 분류를 하기 위해 사용하는 KNeighborsClassifier 함수의 주요 파라미터를 살펴보면 다음과 같다. n_neighbors는 근접한 이웃의 개수 k를 의미한다. weights는 거리별 가중치이다. 'uniform'을 입력하면 이웃의 거리가 멀고 가까운 것에 대한 가중치를 두지 않고 단순 거리 계산만 한다. 'distance'를 입력하면 이웃의 거리가 가까우면 가중치를 더 주고, 멀면 가중치를 덜 주도록 거리를 계산한다. 이 때 이 가중치는 각 이웃들의 거리의 역수이다. (거리가 5라면 1/5) 마지막으로, p는 거리지표이다. p값이 1인 경우에는 맨하튼 거리, p값이 2인 경우에는 유클리드 거리, 그리고 그 밖의 임의의 값을 입력하면 민코프스키 거리(Minkowski distance)의 공식을 적용하여 거리를 계산한다.
KNN 분류의 결과는 neigh객체에 score 함수를 적용하고 X, y를 입력함으로써 평균 정확도를 얻을 수 있다. 또한, confusion_matrix를 통해 각 실제값들이 어떤 값으로 예측되었는지 각 케이스들의 개수를 확인할 수도 있다.
iris 데이터의 경우 k의 값이 3일 때보다 5일 때 정확도가 더 높았고, 가중치가 없이 거리를 계산하는 것보다 가중치가 있게 거리를 계산할 때 좀 더 높은 정확도를 얻을 수 있었다.
파이썬으로 K-최근접이웃 분류 결과를 시각화 하기
KNN 분류 결과를 2차원 좌표평면에 시각화 하기 위해서는 예측변수를 2개까지만 사용할 수 있다. 3차원 좌표평면에 그린다면 3개도 사용할 수 있겠지만 그 이상은 시각화 방법이 없는 것 같다.
앞서 KNN 분류를 위해 사용했던 4개의 예측변수 중에서 sepal length와 sepal width의 값만 훈련용 데이터로 사용하여 분류를 실행하고 그 결과를 시각해 보았다. k값은 3, 거리 계산 가중치는 distance, p=2 유클리드 거리의 조건으로 분류를 실시하여 0.93의 평균 정확도를 얻었고 다음과 같은 시각화 결과를 얻었다.
시각화를 위한 구체적인 코드는 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
# 시각화를 위한 패키지
from matplotlib import pyplot as plt
from matplotlib.colors import ListedColormap
import seaborn as sns
from sklearn import neighbors, datasets
import numpy as np
# K-최근접이웃 분류의 시각화를 좌표평면에 하기 위해서는 예측변수를 2개까지만 사용할 수 있음
# sepal length (cm)와 sepal width (cm)의 값만 뽑아 2차 배열로 변환
X2 = np.array(df[predictors[0:2]])
# color map 설정
cmap_light = ListedColormap(['orange', 'cyan','cornflowerblue'])
cmap_bold = ['darkorange', 'c', 'darkblue']
# 앞서 정확도가 가장 높았던 파라미터를 입력함
neigh2 = neighbors.KNeighborsClassifier(n_neighbors=3, weights='distance', p=2)
neigh2.fit(X2, y)
# 분류 경계를 색으로 구분하기 위한 작업
h = .02 # step size in the mesh
x_min, x_max = X2[:, 0].min() - 1, X2[:, 0].max() + 1
y_min, y_max = X2[:, 1].min() - 1, X2[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
Z = neigh2.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 그래프 사이즈 설정
plt.figure(figsize=(8, 6))
# 분류 경계 별 색칠하기
plt.contourf(xx, yy, Z, cmap=cmap_light)
# 각 훈련값의 좌표들을 그린 산점도
sns.scatterplot(x=X2[:, 0], y=X2[:, 1], hue=iris.target_names[y],
palette=cmap_bold, alpha=1.0, edgecolor="black")
# 제목에 KNN 분류 파라미터와 표준 정확도 표기
plt.title("3-Class classification (k=3, distance), mean accuracy=%0.2f" %neigh2.score(X2, y))
plt.xlabel(predictors[0])
plt.ylabel(predictors[1])
plt.show()
|
cs |
잘라지 트하나키, 전문가를 위한 머신러닝 솔루션: 파이썬을 이용한 머신러닝 고급문제 해결기법 (경기: 위키북스, 2019), 42
피터 브루스, 앤드루 브루스, 피터게데크, Practical Statistics for Data Scientists: 데이터 과학을 위한 통계 2판 (서울: 한빛미디어(주), 2021), 272~284
"k-최근접 이웃 알고리즘", 위키백과, https://ko.wikipedia.org/wiki/K-최근접_이웃_알고리즘#cite_note-2
"sklearn.neighbors.KNeighborsClassifier", Scikit Learn, https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html
'Python notes > Data Science & Machine Learning' 카테고리의 다른 글
데이터전처리) Pandas로 각종 데이터 전처리 1탄 (+엑셀 불러오기/저장, 일부 행/열만 추출, 일부 행/열만 삭제, 데이터 수정/추가, 결측치 확인/삭제) (0) | 2021.11.23 |
---|---|
머신러닝) 분류 모델을 평가하는 방법: 혼동행렬 (+다중분류모델 평가 예제) (0) | 2021.09.18 |
머신러닝) 의사결정나무(Decision tree)의 개념 및 실습 (0) | 2021.09.18 |
파이썬) Mac에서도 Tensorflow 사용하는 방법 (+사용 중인 HW 정보 GPU, CPU 확인하기) (0) | 2021.07.05 |
R) 연관성 분석 association 예제 (+Groceries 데이터로) (0) | 2021.06.29 |
댓글