KNN_실습

동동·2026년 3월 29일

deeplearning

목록 보기
1/2

keras dataset을 이용한 KNN 실습을 해보았다.

KNN?

KNN은 데이터 분류나 회귀에 사용되는 매우 직관적인 머신러닝 알고리즘이다.

새로운 데이터가 들어왔을 때, 그 데이터와 가장 가까운 거리에 있는 k개의 이웃 데이터를 확인하여 다수결로 결과를 결정한다.

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

절차는 다음과 같다.

먼저 새로운 데이터와 기존의 모든 데이터 사이의 거리를 계산한다. (유클리드 계산)

다음으로 거리가 가장 가까운 이웃 데이터 k개를 뽑는다.

다수결로 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:


라이브러리: 순수 파이썬 (Built-in) 설명: 학습 데이터(X_train, y_train)와 테스트 데이터(X_test), 그리고 기준이 될 이웃의 수(k)를 입력받는 함수를 정의합니다. 테스트 데이터의 개수만큼 반복문(for)을 돌면서 각각의 테스트 데이터 하나(dat)에 대해 예측을 수행할 준비를 합니다.

distance = L2_distance(X_train, dat)


라이브러리: 사용자 정의 함수 (아마 노트북 위쪽 어딘가에 직접 구현해 두셨을 것입니다.) 설명: 하나의 테스트 데이터(dat)와 모든 학습 데이터(X_train)들 사이의 유클리디안 거리(L2 norm)를 계산하여 distance라는 배열에 저장합니다.

k_indices = np.argsort(distance)[:k]


라이브러리: NumPy (np.argsort) 설명: np.argsort()는 배열의 값들을 오름차순으로 정렬했을 때, 그 숫자들의 원래 '인덱스(순서)'를 반환하는 재미있는 함수입니다. 즉, 거리가 가장 짧은(가장 가까운) 데이터의 인덱스가 배열의 맨 앞에 오게 됩니다. 뒤에 붙은 [:k]는 파이썬의 슬라이싱 기법으로, 정렬된 인덱스 중에서 맨 앞부터 k개만 쏙 뽑아오겠다는 뜻입니다. (가장 가까운 k개의 데이터 위치 확보!)

k_labels = y_train[k_indices]


라이브러리: NumPy 배열 인덱싱 설명: 위에서 찾은 가장 가까운 데이터들의 위치(인덱스) 정보를 가지고, 실제 학습 데이터의 정답지(y_train)에서 그 위치에 해당하는 진짜 라벨(클래스)들을 쏙쏙 뽑아옵니다.

unique_labels, counts = np.unique(k_labels, return_counts=True)


라이브러리: NumPy (np.unique) 설명: np.unique는 배열 안에 중복을 없앤 고유한 값들을 찾아줍니다. 여기에 return_counts=True 옵션을 주면, 아주 편리하게도 각 고유한 값이 **몇 번씩 등장했는지(빈도수)**까지 세어서 counts에 넘겨줍니다. (예: k_labels가 [1, 1, 1, 0, 0] 이라면 -> unique_labels는 [0, 1], counts는 [2, 3]이 됩니다.)

majority_vote = unique_labels[np.argmax(counts)]


라이브러리: NumPy (np.argmax) 설명: np.argmax()는 배열 안에서 가장 큰 값의 '인덱스'를 반환합니다. 여기서는 counts (빈도수)가 가장 큰 값의 위치를 찾습니다. 찾은 인덱스를 다시 unique_labels에 대입하여, 최종적으로 득표수가 가장 많은(가장 빈번하게 등장한) 라벨을 majority_vote로 결정합니다.

pred.append(majority_vote)
return np.array(pred)


라이브러리: 순수 파이썬 리스트 객체 패서드 (pred.append) / NumPy (np.array) 설명: 예측한 결과를 맨 처음에 만든 pred 리스트에 차곡차곡 쌓아둡니다. 모든 예측이 끝나면 파이썬의 리스트 형태인 pred를 수학적 연산이 편리한 Numpy 배열(array) 형태로 변환하여 반환(return)합니다.

# 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()

한계

동일 클래스 내 변형, 배경의 혼란, 조명 변화, 변형, 가려짐 등에 의해 이미지 분류에 적합하지 않을 수 있음

0개의 댓글