파이썬 KNN (K-Nearest Neighber) 데이터 분석

K-근접이웃(KNN, K-Nearest Neighber)

K-근접이웃(K-nearest neighber)기법은 분류 및 회귀분석을 위한 대표적인 비모수 학습기법의 일종으로, 소수의 근접이웃들만을 이용하여 예측하는 알고리즘이기 때문에 매우 기초적이고 단순하며, 데이터에 대한 사전 정보가 부족할 때 유용하게 사용될 수 있다. K-근접이웃은 예측하고자 하는 대상 데이터와 특징 벡터 공간에서 가장 유사한 K-개의 표본 학습 데이터를 지칭하고 k-개의 표본 학습 데이터로부터 공통의 성질 또는 패턴을 분석함으로써 대상 데이터에 대한 예측을 수행한다. K-근접이웃에서 사용자가 주의해야할 유일한 부분은 바로 K값을 어떻게 설정하냐는 것이다. K값이 커질수록 많은 근접이웃을 포함하기 때문에 예측 정확도는 향상될 수 있지만 k값을 지나치게 높게 잡게 되면 불필요한 잡음 데이터까지도 포함시킬 수 있다.즉, K-근접이웃기법은 K값에 결과가 매우 민감하게 반응하기 때문에 k값을 신중하게 결정하여야 한다.

1. K-근접이웃의 정의

KNN(K-근접이웃기법)은 특징 벡터 공간에서 대상 데이터와 가장 근접한 특성을 지니는 K개의 근접이웃들을 기반으로 한다. 때문에 대상 데이터와 이웃 데이터 간의 유사도를 계산하는 것이 이 기법의 핵심이라고 할 수 있다. 데이터 간의 유사도를 측정 하기 위해서 유클리드 거리(Euclidean distance)를 가장 대중적으로 사용하며, 그 외에도 두 데이터 간의 유사도를 측정하기 위한 측도로서 코사인 거리, 맨하탄 거리 등을 사용할 수 있다.

  • KNN 모형의 분류 예Knn

그림[10.1] KNN 모형분류 (출처: https://upload.wikimedia.org/wikipedia/commons/e/e7/KnnClassification.svg)

위의 그림은 분류를 위한 KNN 모형을 그림으로 표시한 것이다. 네모그룹과 세모그룹으로 분류하는 상황에서 중앙의 초록색 원은 어떤 그룹으로 분류되어야 할까? 결과는 K값에 따라 결정된다. 만약 k=3(실선)이라면 세모 2개, 네모 1개로 세모그룹으로 분류될 것이고, k=5(점선)라면 세모 2개, 네모 3개로 네모그룹으로 분류될 것이다. 만약 분류가 아닌 회귀문제라면 이웃들의 종속변수 평균이 예측값이 된다.

KNN은 다른 학습기법 모델들과 비교했을 때 학습이라고 표현할 절차가 없다. 그저 초록색 원처럼 새로운 데이터가 들어왔을 때, 기존 데이터들과의 거리를 재서 이웃을 찾아내는게 전부이기 때문이다. KNN의 이러한 특성때문에 학습모델을 별도로 구축하지 않는다고 하여 게으른 모델(Lazy model), 혹은 관측치 기반 모델(Instance-based Learning)이라고도 한다. 별도의 학습모델 생성없이 관측치만으로 분류/회귀 작업을 진행한다.

KNN의 장점

- 알고리즘이 매우 간단하여 구현이 쉽다.
- 데이터가 수치형 데이터일 경우, 분류작업의 성능이 좋다.


KNN의 단점

- 학습 데이터의 양이 많으면 분류 속도가 느려진다.
- K값에 매우 민감하며, 최적의 K값을 정하기 위한 명확한 방법이 존재하지 않는다.



2. K-근접이웃 실습

2-1. 데이터 분할 및 정규화

KNN기법은 특징 벡터 공간에서 두 데이터 간에 유사도를 기반으로 예측을 수행하기 때문에 분석에 이용할 변수 중에 범주형 데이터가 있다면 이를 사전에 수치형으로 변형해주어야 하며, 분석변수를 정규화하는 과정이 필요하다. 또한 학습기법의 일종이기 때문에 학습용, 검증용 데이터를 분할하는 사전 과정이 필요하다. 다음 예제를 풀어보자

예제 (1)

백화점 데이터에서 총 매출액, 방문빈도, 구매 카테고리수, 구매유형을 변수로 하여 분할 및 정규화를 진행해보자.

In [74]:

#데이터 불러오기
import pandas as pd

data= pd.read_csv('C:\\python\\백화점.csv', engine='python')
data.head(7)

Out[74]:

고객ID이탈여부총 매출액방문빈도1회 평균매출액할인권 사용 횟수총 할인 금액고객등급구매유형클레임접수여부음향 적절성안내 표지판 설명친절성신속성책임성정확성전문성D1D2D3
010400708017235711154451406666666001
121316840014226314223509952406653666001
23026807801814893261860451416677667001
340594660017349800151951416666656001
450137459507318830192463501206556656100
560332361026127831203481451406555665001
67023693406394890303809451104654554000

7 rows × 42 columns

In [88]:

a=data[['총 매출액','방문빈도','구매 카테고리 수','구매유형']]

X = a.iloc[:, :-1].values # X: 총 매출액, 방문빈도, 구매 카테고리 수
y = a.iloc[:, 3].values # y: 이탈여부

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.30) 
# 학습용, 검증용 데이터 분할, 학습용 데이터의 크기는 0.3(임의 설정 가능)

#변수의 정규화
from sklearn.preprocessing import StandardScaler  
scaler = StandardScaler()  
scaler.fit(X_train) # .fit(): 정규화에 사용될 변수의 평균 및 표준편차를 계산한다.


X_train = scaler.transform(X_train) # .transform(): 센터링 및 확장을 통해 정규화를 수행한다.
X_test = scaler.transform(X_test) 
C:\Users\gksmf\anaconda3\lib\site-packages\sklearn\utils\validation.py:475: DataConversionWarning: Data with input dtype int64 was converted to float64 by StandardScaler.
  warnings.warn(msg, DataConversionWarning)

iloc[컬럼번호, 컬럼번호]
순서를 나타내는 정수 기반의 2차원 인덱싱, 데이터프레임에서 numpy행렬과 같이 쉼표를 사용한 2차원 인덱싱을 지원한다.
.value를 넣음으로써 인덱싱한 값들만 추출한다.

sklearn.model_selection.train_test_split 모듈
데이터 분석 시 학습용, 검증용 등의 데이터 분할을 위해 사용되는 모듈

sklearn.preprocessing.StandardScaler 모듈
범위가 서로 다른 값들을 일정한 분산에 맞게 정규화시켜주는 모듈

예제 (1) 해설

KNN기법을 쓰기에 앞서, 분석변수X와 타겟변수y를 설정해 주어야 한다. 분석에 사용할 변수들을 a로 따로 뽑아준 후, iloc를 이용해 각각의 변수를 인덱싱해준다. 그 다음 학습용 데이터와 검증용데이터를 각각 나누어 준 후, 분석변수의 정규화 작업을 진행한다. 변수값의 크기가 모두 일정하지 않기 때문에 정규화를 하지 않을 경우, 거리를 기반으로 분석을 진행하는 KNN은 정확한 결과값을 낼 수 없다. 때문에 분석을 시작하기 전, 전처리 단계에서 정규화를 필수로 진행해 주어야 한다. 먼저 모듈을 불러온 후, fit(X_train)로 평균 및 표준편차를 계산한다. 그 후에 transform()으로 모두 정규화시킨다.



2-2. K-근접이웃 학습 및 평가

KNN은 학습을 통해 튜닝하는 변수가 아닌, 사용자가 지정해야 하는 세팅이 존재하는데, 이러한 튜닝 옵션을 하이퍼파라미터(Hyperparameter)라고 부른다. KNN의 하이퍼파라미터는 탐색할 이웃의 수(K)와 이웃간의 거리 측정 방식 2가지가 존재하며, 앞서 언급했던 유클리드 거리, 코사인 거리, 맨하탄 거리 등이 거리 측정 방식에 포함된다. 다음 예제를 풀어보자.

예제 (2)

전처리한 백화점 데이터로 KNN분석을 진행한 후 알고리즘을 평가해보자(K=5)

In [168]:

#교육 및 예측
from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=5, metric='minkowski') #n_neighpors K값, 이상적인 값 없음
clf.fit(X_train, y_train)

y_pred = clf.predict(X_test)#예측
sklearn.neighbors.KNeighborsClassifier 모듈
K-근접이웃 기법을 구현한 모듈로 n_neighbors, metric, weights 등으로 파라미터를 설정할 수 있다.

code세부 내용
n_neighbors =K값, 숫자로 표현한다. (기본값=5)
metric=거리측정방식, (기본값=minkowski) minkowski(유클리드와 맨해튼 혼합) euclidean manhattan
weights=가중치 ‘uniform’: 거리에 따라 가중치 부여하지 않음 ‘distance’: 거리에 따라 가중치 부여

In [169]:

#알고리즘 평가
from sklearn.metrics import confusion_matrix, classification_report  
print(confusion_matrix(y_test, y_pred))  
print(classification_report(y_test, y_pred))  
[[  0   0   0  11]
 [  0  84   1  14]
 [  2   7  31  12]
 [  0  10   0 128]]
             precision    recall  f1-score   support

          1       0.00      0.00      0.00        11
          2       0.83      0.85      0.84        99
          3       0.97      0.60      0.74        52
          4       0.78      0.93      0.84       138

avg / total       0.80      0.81      0.79       300

skleart.metrics.confusion_matrix 모듈
'분류결과표'라고도 하며 타겟의 기존 값과 모형의 결과값이 일치하는 지를 갯수로 표현한 것이다.

예측값 0예측값 1
기존값 0기존값이 0, 예측값이 0인 표본의 수기존값이 0, 예측값이 1인 표본의 수
기존값 1기존값이 1, 예측값이 0인 표본의 수기존값이 1, 예측값이 1인 표본의 수
sklearn.metrics.classfication_report 모듈
모형의 성능을 평가하는 모듈로 정밀도(precision), 재현율(recall), F1-score을 구해준다.

In [170]:

print("학습용 데이터셋 정확도: {:.3f}".format(clf.score(X_train, y_train)))
print("검증용 데이터셋 정확도: {:.3f}".format(clf.score(X_test, y_test)))
학습용 데이터셋 정확도: 0.876
검증용 데이터셋 정확도: 0.810
예제 (2) 해설

n_neighbor=5로 지정하여 k값을 5로 설정하였고, clf.fit(X_train, y_train)로 데이터를 학습시킨 후, 학습된 알고리즘을 기반으로 X_test데이터를 예측하였다. 결과적으로 정밀도 80%, 재현율 81% f1-score 79% 정확도 81%로 이상적인 알고리즘으로 평가되었다.

예제 (2-1)

전처리한 백화점 데이터로 KNN분석을 진행한 후 알고리즘을 평가해보자(K=9, metric=’euclidean’)

In [153]:

e_clf = KNeighborsClassifier(n_neighbors=3, metric='euclidean') #n_neighpors K값, 이상적인 값 없음
e_clf.fit(X_train, y_train)

e_y_pred = e_clf.predict(X_test)#예측

In [154]:

print(confusion_matrix(y_test, e_y_pred))  
print(classification_report(y_test, e_y_pred))  
[[  0   0   1  10]
 [  0  85   0  14]
 [  1   7  37   7]
 [  1  11   0 126]]
             precision    recall  f1-score   support

          1       0.00      0.00      0.00        11
          2       0.83      0.86      0.84        99
          3       0.97      0.71      0.82        52
          4       0.80      0.91      0.85       138

avg / total       0.81      0.83      0.81       300

In [155]:

print("학습용 데이터셋 정확도: {:.3f}".format(e_clf.score(X_train, y_train)))
print("검증용 데이터셋 정확도: {:.3f}".format(e_clf.score(X_test, y_test)))
학습용 데이터셋 정확도: 0.889
검증용 데이터셋 정확도: 0.827
예제 (2-1) 해설

n_neighbor=3로 지정하여 k값을 3로 설정하였고, metric=’euclidean’으로 거리측정방식을 유클리드 거리로 선택했다. e_clf.fit(X_train, y_train)로 데이터를 학습시킨 후, 학습된 알고리즘을 기반으로 X_test데이터를 예측하였다. 결과적으로 정밀도 81%, 재현율 83% f1-score 81% 정확도 82.7%로 평가되었다. 역시나 이상적인 평가수치임을 확인할 수 있다



3. K-근접이웃의 이상적인 K값 찾기

KNN은 K값 설정에 따라 분석 결과가 상이하게 변하기 때문에 적합한 K값을 선택하는 것이 매우 중요하다. 그러나 아쉽게도 어느 데이터에나 적합하게 활용되는 K값은 존재하지 않는다. 다만 K값을 비교하면서 상대적으로 나은 K값을 선택하는 방법론은 존재한다. 바로 K값에 따른 평균 오류를 계산해서 오류가 적은 K값을 찾아내는 것이다. 그러나 이 방법 또한 K값 설정에 대한 절대적인 지표가 되지는 않는다. 그저 K값을 비교함에 따라 사용자의 선택에 도움을 주기 위함임을 염두해야 한다. 다음 예제를 풀어보자.


예제(3)

KNN분석에 대해 이상적인 K값을 찾아보자.

In [87]:

#모델에 적합한 K값 구하기
import matplotlib.pyplot as plt
import numpy as np

error = []

# k값 1-40까지 반복 실행, 평균 오류를 계산해서 error목록에 출력
for i in range(1, 40):  
    knn = KNeighborsClassifier(n_neighbors=i)
    knn.fit(X_train, y_train)
    pred_i = knn.predict(X_test)
    error.append(np.mean(pred_i != y_test))

# x축: K값, y축: 평균 오류인 k값에 대한 오류비율표 작성    
plt.figure(figsize=(10, 5))  
plt.plot(range(1, 40), error, color='green', marker='.', markerfacecolor='red', markersize=15)
plt.xlabel('K')  
plt.ylabel('Mean Error')  
plt.show

Out[87]:

<function matplotlib.pyplot.show(*args, **kw)>
예제 (3) 해설

for문을 이용해서 K값이 1~40일 때의 KNN을 반복실행한 후, 반복실행한 결과에서 오류값의 평균을 구해 그래프를 작성했다. K값이 4일 때 평균 오류가 가장 낮았으며, 그 이후로는 평균 오류가 계속 증가하는 것을 확인할 수 있다. 평균 오류를 기준으로 보았을 때 이상적인 K값은 4이다.

예제 (3-1)

KNN분석에 대해 이상적인 K값을 찾아보자.(metric=’euclidean’)

In [171]:

error = []

for i in range(1, 40):  
    knn = KNeighborsClassifier(n_neighbors=i,metric='euclidean')
    knn.fit(X_train, y_train)
    pred_i = knn.predict(X_test)
    error.append(np.mean(pred_i != y_test))
 
plt.figure(figsize=(10, 5))  
plt.plot(range(1, 40), error, color='green', marker='.', markerfacecolor='red', markersize=15)
plt.xlabel('K')  
plt.ylabel('Mean Error')  
plt.show

Out[171]:

<function matplotlib.pyplot.show(*args, **kw)>
예제 (3-1) 해설

K값이 3일 때 평균 오류가 가장 낮았으며, 그 이후로는 평균 오류가 계속 증가하는 것을 확인할 수 있다. 평균 오류를 기준으로 보았을 때 이상적인 K값은 3이다.

error: Content is protected !!
Scroll to Top