keras dataset을 이용한 KNN 실습을 해보았다.
KNN?
KNN은 데이터 분류나 회귀에 사용되는 매우 직관적인 머신러닝 알고리즘이다.
새로운 데이터가 들어왔을 때, 그 데이터와 가장 가까운 거리에 있는 k개의 이웃 데이터를 확인하여 다수결로 결과를 결정한다.

새로운 벡터와 가까운 k개의 벡터들과의 유사도를 vote 하여 어디와 더 유사한지를 분류해낸다.
절차는 다음과 같다.

먼저 새로운 데이터와 기존의 모든 데이터 사이의 거리를 계산한다. (유클리드 계산)
다음으로 거리가 가장 가까운 이웃 데이터 k개를 뽑는다.
다수결로 k개의 이웃 중 가장 많이 포함된 클래스로 새로운 데이터를 분류한다.
k가 너무 작으면 모델이 너무 민감해져 노이즈나 이상치에 영향을 많이 받고, k가 너무 크면 데이터의 세세한 특징을 무시하고 너무 단순해질 수 있다.
주요 특징으로는 다음과 같다.
실습
import numpy as np
import random
import matplotlib.pyplot as plt
from keras.datasets import mnist
# load MNIST data
(train_X, train_y), (test_X, test_y) = mnist.load_data()
# 60000 training dataset // 100000 training dataset
print(train_X.shape, train_y.shape, test_X.shape, test_y.shape)
데이터 로드
# Display Some of the (training) data
sample_index = np.random.choice(60000, size=12)
num_samples = sample_index.size
random_samples = train_X[sample_index]
plt.figure(figsize=(12, 12))
for k in range(num_samples):
plt.subplot(4, 4, k + 1)
plt.imshow(random_samples[k].reshape(28, 28),cmap='Greys')
plt.title(train_y[sample_index[k]])
plt.axis('off')
plt.show()
샘플 추출
# Prepare dataset for training (reshape)
X_train = train_X.reshape(60000,784).astype(float)
X_test = test_X.reshape(10000,784).astype(float)
y_train = train_y
y_test = test_y
X_train.shape, y_train.shape, X_test.shape, y_test.shape
# k-NN training with sklearn
from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors= 5, p = 2) # 5-nearest neighbor // L2 norm
clf.fit(X_train, y_train)
K = 5, L2 norm 사용
# Measure the accuracy of the kNN
from sklearn.metrics import accuracy_score
pred = clf.predict(X_test)
print("Accuracy: ", accuracy_score(y_test, pred))
정확도 측정
# Display test result (predicted labels and actual label)
sample_index = np.random.choice(10000, size=12) # take 12 random sample index
num_samples = sample_index.size
random_samples = test_X[sample_index]
plt.figure(figsize=(12, 12))
for k in range(num_samples):
plt.subplot(4, 4, k + 1)
plt.imshow(random_samples[k].reshape(28, 28),cmap='Greys')
plt.title("True: " + str( test_y[sample_index[k]]) + ", Pred: " + str(pred[sample_index[k]]))
plt.axis('off')
plt.show()
모델 학습 결과 확인
# L2 distance
def L2_distance(x, y):
return np.sqrt( np.sum((x - y)**2, axis=1) )
# Your own K-nearest neighbor
def my_kNN(X_train, y_train, X_test, k):
pred = [] # prediction result
for dat in X_test:
######### Implement your codes here #########################
# measure distance between dat and training data
distance = L2_distance(X_train,dat)
# find k-minimum values (index)
# take the majority vote
# 여기서부터 내 코드
k_indices = np.argsort(distance)[:k]
# 얻은 k개의 인덱스에 해당하는 학습 데이터의 정답 라벨들을 가져옵니다.
k_labels = y_train[k_indices]
# 3. take the majority vote
# np.unique를 사용하여 라벨들의 고유값과 각각의 빈도수(counts)를 계산합니다.
unique_labels, counts = np.unique(k_labels, return_counts=True)
# 빈도수가 가장 높은 라벨(가장 많이 나온 값)을 다수결로 선택합니다.
majority_vote = unique_labels[np.argmax(counts)]
# 예측한 라벨을 리스트에 추가합니다.
pred.append(majority_vote)
return np.array(pred)
실습 코드
def my_kNN(X_train, y_train, X_test, k):
pred = [] # prediction result
for dat in X_test:
distance = L2_distance(X_train, dat)
k_indices = np.argsort(distance)[:k]
k_labels = y_train[k_indices]
unique_labels, counts = np.unique(k_labels, return_counts=True)
majority_vote = unique_labels[np.argmax(counts)]
pred.append(majority_vote)
return np.array(pred)
# run your kNN classifier & measure accuracy
# test your code with only 100 test data
num_test = 100
pred = my_kNN(X_train, y_train, X_test[:num_test], 5)
print("Accuracy: ", accuracy_score(y_test[:num_test], pred))
RUN
# Display Some test results
sample_index = np.random.choice(num_test, size=12) # take 12 random sample index
num_samples = sample_index.size
random_samples = test_X[sample_index]
plt.figure(figsize=(12, 12))
for k in range(num_samples):
plt.subplot(4, 4, k + 1)
plt.imshow(random_samples[k].reshape(28, 28),cmap='Greys')
plt.title("True: " + str( test_y[sample_index[k]]) + ", Pred: " + str(pred[sample_index[k]]))
plt.axis('off')
plt.show()
한계
동일 클래스 내 변형, 배경의 혼란, 조명 변화, 변형, 가려짐 등에 의해 이미지 분류에 적합하지 않을 수 있음