머신러닝 & 딥러닝

[머신러닝 & 딥러닝] K-최근접 이웃(K-Nearest Neighbor, KNN)

beginner-in-coding 2025. 4. 14. 17:11

00. 모수 모델(parametric model), 비모수 모델(nonparametric model), 인스턴스 기반 모델

  • 모수 모델: 새로운 데이터 포인트를 분류할 수 있는 함수를 학습하기 위해 훈련 데이터 셋에서 모델 파라미터를 추정
    • 훈련이 끝나면 원본 훈련 데이터셋이 필요 없음
    • 모수 모델 알고리즘 종류: 퍼셉트론, 로지스틱 회귀, 선형 SVM
  • 비모수 모델: 고정된 개수의 파라미터가 아닌 훈련 데이터가 늘어남에 따라 파라미터 개수도 늘어남
    • 비모수 모델 알고리즘 종류: 결정 트리/랜덤 포레스트, 커널 SVM, KNN
  • 인스턴스 기반 모델: 훈련 데이터 셋을 메모리에 저장

01. K-최근접 이웃(K-Nearest Neighbor, KNN)

  • 지도 학습 알고리즘
  • 전형적인 게으른 학습기(lazy learner): 훈련 데이터에서 판별 함수(discriminative function)를 학습하는 대신 훈련 데이터셋을 메모리에 저장
  • 비모수 모델이면서 인스턴스 기반 모델
  • 단계 요약
    1. 숫자 k와 거리 측정 기준을 선택
    2. 분류하려는 샘플에서 k개의 최근접 이웃을 찾음
    3. 다수결 투표를 통해 클래스 레이블(분류)를 할당
  • 장점: 메모리 기반 방식의 분류기이기 때문에, 수집된 새로운 훈련 데이터에 즉시 적용 가능
  • 단점: 새로운 샘플을 분류하는 계산 복잡도
    • 데이터셋의 차원(특성)이 적고, 알고리즘이 트리같은 효율적인 데이터 구조로 구현되어 있지 않으면 최악의 경우 샘플 개수에 선형적으로 증가
    • 훈련 단계가 없음 → 사용한 훈련 샘플을 버릴 수 없음, 즉 대규모 데이터셋에서 저장 공간에 문제 발생

02. 동점 처리

  • 다수결 투표가 동일한 경우, 사이킷런의 KNN은 분류하려는 데이터 포인트에 더 가까운 이웃을 예측으로 선택
  • 만약 이웃끼리의 거리가 같다면, 훈련 데이터셋에서 먼저 나타난 샘플의 클래스 레이블을 선택

03. 숫자 k

  • 적절한 k를 선택하는 이유: 과대 적합과 과소 적합 사이에서 올바른 균형을 잡기 위해
  • 데이터셋의 특성에 맞는 알맞는 거리 측정 지표를 선택해야 함
  • 실수 값을 가진 특성: 유클리디안 거리 사용
    • 유클리디안 거리 사용시 중요 사항: 각 특성이 동일하게 취급되도록 표준화하는 과정이 필요
  • 사이킷런에는 다양한 거리 측정 기준이 존재

04. 차원의 저주(curse of dimensionality)

  • KNN은 차원의 저주 때문에 과대 적합의 문제가 발생하기 쉬움
  • 고정된 크기의 훈련 데이터셋이 차원이 늘어남에 따라 특성 공간이 점점 희소해지는 현상
    • 즉, 고차원 공간에서 가장 가까운 이웃이라도 좋은 추정 값을 만들기에 너무 멀리 떨어져 있어, 거리 측정을 기반으로 분류하는 KNN에서는 불리함을 의미
  • 과대 적합을 막기위한 방법
    • 로지스틱 회귀: 규제 개념 존재
    • 결정 트리나 KNN(규제를 적용할 수 없는 모델): 특성 선택과 차원 축소 기법 사용(즉, 데이터 전처리 과정이 중요)

05. 사이킷런(sklearn)의 KNN 모델 사용

# 사이킷런의 KNN 이용하여 모델 구현하기
from sklearn.neighbors import KNeighborsClassifier  # 사이킷런의 KNN 분류기
from sklearn.datasets import load_iris  # 사이킷런에서 제공하는 붓꽃 데이터셋 이용
import pandas as pd
from sklearn.model_selection import train_test_split #train, test 셋 생성
import matplotlib.pyplot as plt  # 시각화 패키지
from mlxtend.plotting import plot_decision_regions  # 사이킷런 분류 모델이 만든 결정 경계(Decision Boundary) 를 시각화할 때 사용
from sklearn.preprocessing import StandardScaler

# 0. 데이터 준비
iris = load_iris()  # 붓꽃 데이터 가져오기

# 1. 데이터 전처리
df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
data = df[['petal length (cm)', 'petal width (cm)']]  # 원하는 두 개 컬럼만 선택
target = iris.target  # 클래스 레이블 정보

X_train, X_test, y_train, y_test = (
    train_test_split(data, target, random_state=42))  # 학습용 데이터와 테스트용 데이터 나누기(섞음)

sc = StandardScaler()  # 데이터 표준화
X_train_std = sc.fit_transform(X_train)
X_test_std = sc.transform(X_test)

# 2. knn 모델 객체 생성
knn = KNeighborsClassifier(n_neighbors=5,  # 분류하려는 샘플에서 k개의 최근접 이웃 수를 설정
                           p=2,  # 거리 측정 기준을 유클리디안 거리로 설정
                           metric='minkowski')  #minkowski : 유클리디안 거리(p=2) 또는 맨해튼 거리(p=1)를 일반화함

# 3. 모델에 학습용 데이터 학습
knn.fit(X_train_std, y_train)

# 4. 시각화
plot_decision_regions(X_train_std, y_train, clf=knn) 
plt.xlabel('Petal length (cm)')
plt.ylabel('Petal width (cm)')
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

plot_decision_regions(X_test_std, y_test, clf=knn)
plt.xlabel('Petal length (cm)')
plt.ylabel('Petal width (cm)')
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()

# 5. 정확도 확인
train_acc = knn.score(X_train_std, y_train)
test_acc = knn.score(X_test_std, y_test)
print(f"Train Accuracy: {train_acc:.2f}")
print(f"Test Accuracy: {test_acc:.2f}")

학습용 데이터 시각화 결과

 

테스트용 데이터 시각화 결과
학습용과 테스트용 정확도 결과

  • 테스트용 데이터셋의 개수, 즉 샘플이 몇 개 없어서 정확도가 100%가 나오는 것으로 예측됨