본문 바로가기
인공지능/머신러닝

[머신러닝] kNN(k-Nearest Neighbors, k-최근접 이웃) 알고리즘

by 클레어몬트 2025. 2. 17.

가장 간단하면서도 강력한 지도 학습(Supervised Learning) 알고리즘 중 하나!

 

ㅁk-최근접 이웃(k-Nearest Neighbors, kNN) 알고리즘: 주어진 데이터 포인트에 대해 가장 가까운 k개의 이웃을 참조하여 분류(Classification)하거나 회귀(Regression)를 수행

1. 새로운 데이터 포인트가 주어지면, 가장 가까운 k개의 이웃을 찾는다

2. 다수결(Majority Voting) 원칙을 사용하여 분류 문제를 해결한다
3. 평균을 내어 회귀 문제를 해결한다

 


※ k값 선택 방법
k값은 너무 작거나 크면 성능이 저하될 수 있다. 일반적으로 다음과 같은 방법을 사용하여 최적의 k값을 찾는다
- 홀수 선택: 데이터가 2개의 클래스로 나뉘어 있을 경우, 동률을 방지하기 위해 k를 홀수로 선택한다
- 교차 검증(Cross-Validation) 활용: 다양한 k값을 시도하며 검증 데이터셋의 성능을 평가한다

from sklearn.model_selection import cross_val_score

# 다양한 k값에 대해 교차 검증 수행
k_values = range(1, 21)
scores = [cross_val_score(KNeighborsClassifier(n_neighbors=k), X_train, y_train, cv=5).mean() for k in k_values]

# 최적의 k값 찾기
optimal_k = k_values[scores.index(max(scores))]
print(f'최적의 k 값: {optimal_k}')

 

 

[kNN의 작동 원리]
1. 거리 계산: 새로운 데이터 포인트와 기존 데이터 포인트 간의 거리를 계산한다
※ 거리를 측정하는 방법으로 유클리드 거리(Euclidean Distance), 맨해튼 거리(Manhattan Distance) 등이 사용된다


2. 이웃 선택: 가장 가까운 k개의 데이터를 선택한다

3. 결과 결정:

  • 분류(Classification): "다수결 투표"를 통해 클래스를 결정
  • 회귀(Regression): k개의 이웃의 "평균"값을 사용하여 예측값을 결정



[kNN 구현 방법 (Python)] by scikit-learn 라이브러리


(데이터 준비)

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score

# 데이터 로드
iris = load_iris()
X, y = iris.data, iris.target

# 데이터 분할 (학습 데이터 80%, 테스트 데이터 20%)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 데이터 정규화 (표준화)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

 


(kNN 모델 학습 맟 평가)

# kNN 모델 학습
k = 5
knn = KNeighborsClassifier(n_neighbors=k)
knn.fit(X_train, y_train)

# 예측 및 정확도 평가
y_pred = knn.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f'kNN 분류 정확도: {accuracy:.2f}')




[kNN의 장점과 단점]

(장점)
✅ 단순하고 이해하기 쉬움: 직관적인 알고리즘으로, 구현이 간단하다
비선형 데이터에 유용함: 결정 경계가 비선형적인 경우에도 좋은 성능을 보일 수 있다
모델 학습이 필요 없음: 단순히 데이터를 저장하고 예측 시 계산만 수행하면 된다

(단점)
계산 비용이 큼: 모든 데이터 포인트와의 거리를 계산해야 하므로 데이터가 많아지면 속도가 느려진다
고차원 데이터에서 성능 저하: 차원의 저주(Curse of Dimensionality)로 인해 거리 기반 알고리즘의 성능이 떨어질 수 있다
k값 선정이 어려움: k값을 적절히 선택하지 않으면 과적합(overfitting) 또는 과소적합(underfitting)이 발생할 수 있다

 

 

(kNN 활용 사례)

  • 이미지 인식: 숫자 손글씨 데이터(MNIST) 분류
  • 추천 시스템: 유사한 사용자 기반 추천
  • 이상 탐지(Anomaly Detection): 이상 데이터 탐지
  • 유전자 데이터 분석: 유전자 패턴을 이용한 분류

 

 

 

(kNN 알고리즘 실습 코드)

# %%
## 필요한 라이브러리, 모듈 로딩
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from collections import Counter
from tqdm import tqdm
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

# %%
## iris 데이터 가져와서 사용
iris = load_iris()
df = pd.DataFrame(np.c_[iris['data'],iris['target']],columns=iris['feature_names']+['label'])

# %%
df.head()

# %%
## 데이터를 학습 데이터, 테스트 데이터, 검증 데이터로 분할
x = df[iris['feature_names']]
y = df['label']
train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.2, random_state=42)
train_x, val_x, train_y, val_y = train_test_split(train_x, train_y, test_size=0.33, random_state=42)

# %%
def knn_predict(data,X,y,k=1):
    distances = []
    for val in X:
        distance = np.linalg.norm(data-val)
        distances.append(distance)

    distances = np.array(distances)
    if k < len(distances):
        idx = np.argpartition(distances,k)
        prediction = Counter(y[idx[:k]]).most_common()[0][0]
    else:
        prediction = Counter(y).most_common()[0][0]
    return prediction

# %%
## 검증 데이터를 토대로, 최적의 k 찾기
knn_val_error = []
ks = range(1,len(train_x))
for k in tqdm(ks):
    predict = []
    for j in range(len(val_x)):
        data = val_x.values[j]
        data = np.array(data)
        predict.append(knn_predict(data,train_x.values,train_y.values,k))

    predict = np.array(predict)
    ktre = 1 - (val_y == predict).mean()
    knn_val_error.append(ktre)

fig = plt.figure(figsize=(8,8))
fig.set_facecolor('white')

plt.plot(ks, knn_val_error)
plt.xlabel('k',fontsize=15)
plt.show()

# %%
min_val_error = min(knn_val_error)
opt_k = knn_val_error.index(min_val_error)+1 ## optimal k

# %%
print(opt_k)

# %%
## 최적 k를 토대로 테스트 데이터를 이용해서 모형을 평가
k = opt_k
knn_test_error = []

predict = []
for j in range(len(test_x)):
    data = test_x.values[j]
    data = np.array(data)
    predict.append(knn_predict(data,train_x.values,train_y.values,k))

predict = np.array(predict)
kte = 1-(test_y == predict).mean()

# %%
## 모형 평가 결과, 에러율 0
print(kte)

# %%
## scikit learn 라이브러리 이용
from sklearn.neighbors import KNeighborsClassifier

neigh = KNeighborsClassifier(n_neighbors=opt_k)
neigh.fit(train_x.values, train_y.values)

knn_test_error = []

predict = []
for j in range(len(test_x)):
    data = test_x.values[j]
    data = np.array(data)
    data = np.expand_dims(data, axis=0)
    predict.append(neigh.predict(data))

predict = np.squeeze(predict)
kte = 1-(test_y == predict).mean()

# %%
## 다시 모형 평가 결과, 에러율 0
print(kte)