본문 바로가기
국비 교육/머신러닝, 딥러닝

[머신러닝] 분류분석 : K-NN

by 육츠 2023. 11. 22.
Contents 접기

K-Nearest Neighbors(K-최근접 이웃)

새로운 데이터를 입력받으면 기존 클러스터에서 모든 데이터와 유클리드 기반 거리를 측정 후 가장 많은 속성을 가진 클러스터에 할당하는 분류 알고리즘이다. 새 데이터가 어떤 카테고리에 속하는지 알기 위해서는 가까이에 있는 k개의 정답데이터를  보고 추론한다.

 

유클리드거리

2차원 평면에 서로 다른 두 점 A(a1,a2) 와 B(b1,b2) 가 있을때, 둘 사이의 거리를 구하는 방법
A와 B의 대각선 거리를 구하는 식은 √(a1-b1)^2 + (a2-b2)^2 으로 표현된다.

 

여기서 k는 홀수로 잡아야 동등한 값이 나오지 않게 하기 위해 홀수로 지정하는 것이 좋다.

 

[실습] 붓꽃 품종 분류

패키지 로딩

K-NN은 neighbors 의 KNeighborsClassifier 를 사용한다.

from sklearn.datasets import load_iris
from sklearn.neighbors import KNeighborsClassifier 
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix



데이터 로딩 및 분할

x,y = load_iris(return_X_y = True) 

x_train, x_test, y_train, y_test = train_test_split(x,y,test_size= 0.3,
                                                   random_state= 10, stratify= y)

 

모델 생성 및 학습

model = KNeighborsClassifier(n_neighbors = 3 ) # n_neighbors : k의 값
model.fit(x_train,y_train)

 

예측 및 평가

y_hat = model.predict(x_test)
con_mat = confusion_matrix(y_test,y_hat)
print(con_mat)
[[15  0  0]
 [ 0 15  0]
 [ 0  0 15]]

정확히 분류한 것을 알 수 있다.

 

최적의 K 찾기

k는 적절한 분류의 값을 찾는 것이기 때문에 임의적으로 값을 넣어야한다.
하지만, 그 넣는 값 중에 최적의 값이 무엇인지는 찾을 수 있다.

import numpy as np

k = 10
acc_score = np.zeros(k)
for k in range(1,k+1):
    model = KNeighborsClassifier(n_neighbors=k).fit(x_train,y_train)
    y_hat = model.predict(x_test)
    acc = accuracy_score(y_test,y_hat)
    acc_score[k-1] = acc

max_index = np.argmax(acc_score)

print(acc_score)
print(f'최적의 k는 {max_index+1} 이며 정확도는 {acc_score[max_index]:.3f} 이다.')
최적의 k는 1 이며 정확도는 1.000 이다.
[1.         1.         1.         0.97777778 1.         1.
 1.         1.         1.         0.97777778]

np.argmax()를 이용하여 최적의 k의 인덱스의 위치를 찾은 것이므로 출력때에는 인덱스 자리에 +1을 한다. (k를 1~10 까지 넣었으므로) 하지만 이 데이터에서는 acc_score를 보면 대부분 높은 확률의 정확도를 가지고 있기 때문에 큰 영향이 없다는 것을 알 수 있다.

 

[실습] MNIST 손글씨 분류

MNIST(Modified National Institute of Standard and Technology) : 손으로 쓴 숫자들로 이루어진 데이터베이스이다.

데이터로딩

위에 패키지에 필요한 패키지만 더하여 사용하였다.
fetch_openml('mnist_784', version =1, parser = 'pandas') 옵션을 주어 판다스의 데이터프레임으로 읽어올 수 있게 한다.

from sklearn.datasets import fetch_openml    # 데이터 제공

mnist = fetch_openml('mnist_784', version =1, parser = 'pandas') # 번치 객체 형태
print(type(mnist))

 

데이터 모양 확인

전체 70,000 개의 이미지와 784 개의 특성값 (28x28 픽셀 이미지)을 가지고 있다.

이처럼 데이터셋에서 읽어오는 친구들은 독립변수와 종속변수를 .data / .target 으로 읽는 것이 가능하다.

x = mnist.data
y = mnist.target

print(x.shape, y.shape)
print(type(x))
display(x.head())
(70000, 784) (70000,)

 

숫자 데이터 이미지화

import matplotlib.pyplot as plt

img_data = x.iloc[0].values.reshape(28,28) # 2차원 배열
plt.imshow(img_data, cmap='binary')
plt.axis('off')
plt.show()

0번째  행의 값을 가져와 2차원 배열로 만들어준 후 imshow()를 통해 이미지를 보여준다.

 

학습데이터, 평가데이터 분리

(특이점) MNIST 데이터셋은 이미 학습데이터(앞쪽 6만개), 평가데이터(뒤쪽 만개)로 나뉘어져 있기 때문에 train_test_split() 함수를 이용해 랜덤으로 값을 나누는 것이 아닌 나뉘어진 데이터를 따로 지정하여야 한다.

x_train, x_test, y_train, y_test = x[:60000], x[60000:],y[:60000], y[60000:]

 

모델 생성및 학습

' n_neighbors ' : k 값을 설정하는 옵션이다.

knn = KNeighborsClassifier(n_neighbors= 5)
knn.fit(x_train,y_train)

 

예측 및 평가

y_hat = knn.predict(x_test)
print(f'정확도: {accuracy_score(y_test,y_hat):.3f}')
정확도: 0.969

 

from sklearn.metrics import roc_auc_score

pred_proba = knn.predict_proba(x_test)
print(f'AUC : {roc_auc_score(y_test,pred_proba, multi_class = "ovr"):.3f}') # 1에 근접한 값
AUC : 0.995

높은 비율의 정확도가 나오는 것을 알 수 있다.

 

예측값 확인

2차원 배열로 넣었으나 예측할때에는 1차원으로 차원을 축소시켜준다.

knn.predict(img_data.reshape(1,-1))
array(['5'], dtype=object)

예측값이 맞음을 알 수 있다.