본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로AI › 머신러닝 › 기초

최근접 이웃 알고리즘

목차

k-최근접 이웃 알고리즘(K-Nearest Neighbors, KNN)은 머신러닝에서 가장 직관적이고 간단한 알고리즘 중 하나입니다.

쉽게 말해 “유유상종(類類相從)”, 즉 “비슷한 것끼리 모인다”는 원리를 이용해 새로운 데이터가 어떤 그룹에 속할지 판단하는 방법입니다.

“비슷하다”는 말은 모델 안에서 거리 함수로 바뀝니다. 고객을 구매 성향으로 묶든, 꽃의 품종을 예측하든 어떤 특성을 넣고 각 특성의 단위를 어떻게 맞췄는지가 결과를 좌우합니다. 따라서 KNN은 알고리즘 자체보다 데이터 표현을 연습하기 좋은 출발점입니다.


1. 핵심 개념

KNN은 지도 학습(Supervised Learning) 알고리즘으로, 주로 분류(Classification) 문제에 사용되지만 회귀(Regression) 문제에도 사용할 수 있습니다.

  • 원리: 새로운 데이터 포인트가 주어졌을 때, 그 주변에 있는 가장 가까운 kk개의 데이터를 살펴보고, 그중 가장 많은 데이터가 속한 그룹(다수결)으로 분류합니다.

2. 작동 방식 (Step-by-Step)

KNN이 새로운 데이터(별표 ★)를 분류하는 과정은 다음과 같습니다.

  1. kk값 설정: 몇 명의 이웃을 살펴볼지 결정합니다. (예: k=3k=3)

  2. 거리 계산: 새로운 데이터와 기존의 모든 데이터 간의 거리를 잰 후, 가장 가까운 순서대로 정렬합니다.

    • 거리를 잰다는 것은 주로 유클리드 거리(Euclidean Distance) 공식을 사용합니다.

      d(x,y)=∑i=1n(xi−yi)2d(x, y) = \sqrt{\sum_{i=1}^{n} (x_i - y_i)^2}
  3. 이웃 선택: 가장 가까운 kk개의 데이터를 뽑습니다.

  4. 다수결 투표: 뽑힌 kk개의 이웃 중 어떤 클래스(그룹)가 가장 많은지 셉니다.

  5. 결과 예측: 가장 많은 표를 얻은 클래스로 새로운 데이터를 분류합니다.

숫자로 한 번 계산해 보겠습니다. 특성이 두 개인 평면에서 정답을 아는 점이 다음과 같다고 가정합니다.

훈련 점좌표클래스새 점 (2, 2)와의 거리
A1(1, 1)A2≈1.41\sqrt{2}\approx1.41
A2(1, 2)A11
B1(4, 4)B8≈2.83\sqrt{8}\approx2.83
B2(5, 4)B13≈3.61\sqrt{13}\approx3.61

k=3이면 A2, A1, B1 순서로 가까워서 A가 2표, B가 1표입니다. 새 점은 A로 분류됩니다. 이 계산은 정답 레이블 자체가 거리에 들어가는 것이 아님을 보여 줍니다. 거리는 특성 좌표로 계산하고, 레이블은 이웃을 고른 뒤에 투표에 사용합니다.

flowchart LR
    Q[새 샘플의 특성] --> D[훈련 샘플과 거리 계산]
    D --> S[가까운 k개 선택]
    S --> V[이웃 레이블 투표]
    V --> P[분류 결과]

위 흐름에서 fit은 주로 훈련 샘플을 이웃 탐색에 사용할 수 있게 준비하고, 거리 계산·투표는 predict를 호출할 때 수행합니다. 회귀에서는 다수결 대신 이웃들의 타깃 값을 평균하거나 거리 가중 평균을 구합니다. scikit-learn 이웃 모델 안내를 참고할 수 있습니다.


3. kk값의 중요성

kk를 몇으로 설정하느냐에 따라 결과가 완전히 달라질 수 있습니다.

  • kk가 너무 작을 때 (예: k=1k=1):
    • 데이터의 노이즈(이상치)에 매우 민감해집니다.
    • 모델이 학습 데이터에 너무 딱 맞춰지는 과대적합(Overfitting)이 발생할 수 있습니다.
  • kk가 너무 클 때:
    • 주변의 지역적 특성을 무시하고 전체 데이터의 분포를 따라가게 됩니다.
    • 모델이 너무 단순해지는 과소적합(Underfitting)이 발생할 수 있습니다.
  • 동점: 이진 분류에서 홀수 kk를 쓰면 같은 수의 표가 나오는 일을 줄일 수 있습니다. 다중 클래스에서는 홀수여도 동점이 가능하고, 거리 가중 투표도 결과를 바꿉니다. 홀수라는 규칙보다 검증 데이터에서 kk를 고르는 것이 중요합니다.

4. 장단점 요약

장점 (Pros)단점 (Cons)
직관적임: 가까운 사례를 보여 주며 결과를 설명할 수 있습니다.예측 비용: 데이터가 많거나 차원이 높으면 이웃 탐색이 비쌀 수 있습니다. scikit-learn은 자료에 맞춰 여러 탐색 알고리즘을 선택합니다.
간단한 학습: 복잡한 계수를 최적화하는 과정이 없습니다.저장 공간: 훈련 데이터를 이웃 탐색에 사용할 수 있게 보관해야 합니다.
설정이 비교적 적음: 주로 kk, 거리, 투표 방식을 고릅니다.거리의 의미: 특성 단위와 관련 없는 특성이 결과를 왜곡할 수 있습니다. 스케일링이 필요한지 데이터에 맞춰 확인합니다.

5. 언제 사용하면 좋을까요?

  • 데이터의 양이 아주 방대하지 않을 때
  • 의미 있는 거리 기준을 정의할 수 있고 직관적인 설명이 필요할 때
  • 빠르게 기준 모델(Baseline model)을 만들어보고 싶을 때

붓꽃 데이터로 확인하기

아래는 붓꽃의 네 가지 치수로 품종을 예측하는 예제입니다. 데이터와 정답을 먼저 분리하고, 훈련 데이터에만 표준화 기준을 맞춘다는 순서를 보세요. 코드는 학습 절차를 설명하기 위한 것이며 여기서 실행 결과를 가정하지 않습니다.

import numpy as np
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

# 1. 데이터 준비 (붓꽃 데이터셋 로드)
iris = load_iris()
X = iris.data   # 특성 (꽃받침 길이, 너비 등)
y = iris.target # 타겟 (품종: 0, 1, 2)

# 2. 학습용(Train)과 테스트용(Test) 데이터 분리 (8:2 비율)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)

# 3. 데이터 전처리 (표준화 - Scaling) *중요*
# 거리에서 어떤 특성이 주로 작용하는지 확인하고 필요하면 스케일을 맞춥니다.
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 4. 모델 생성 및 학습
k = 3
knn = KNeighborsClassifier(n_neighbors=k)
knn.fit(X_train_scaled, y_train) # 학습 진행 (실제로는 데이터를 저장하는 과정)

# 5. 예측 및 평가
y_pred = knn.predict(X_test_scaled)
accuracy = accuracy_score(y_test, y_pred)

print(f"설정된 k값: {k}")
print(f"모델 정확도: {accuracy:.2f}")

# 6. 새로운 데이터 예측해보기 (예시)
new_data = np.array([[5.1, 3.5, 1.4, 0.2]]) # 임의의 꽃 데이터
new_data_scaled = scaler.transform(new_data) # 새로운 데이터도 똑같이 스케일링 필요
prediction = knn.predict(new_data_scaled)

print(f"새로운 데이터의 예측 품종: {iris.target_names[prediction][0]}")

# 이 예측을 만든 이웃과 그 거리를 조사한다.
distances, indices = knn.kneighbors(new_data_scaled)
print('이웃 거리:', distances[0])
print('이웃 품종:', iris.target_names[y_train[indices[0]]])

load_iris()의 한 행은 꽃 한 개, 네 열은 꽃받침과 꽃잎의 길이·너비입니다. stratify=y는 각 품종의 비율이 훈련과 테스트에서 비슷하게 유지되도록 합니다. random_state는 같은 분할을 재현하기 위한 값입니다. 이 값을 바꾸어 가장 좋은 테스트 점수를 고르는 용도로 사용해서는 안 됩니다.

StandardScaler().fit_transform(X_train)은 훈련 데이터에서 각 열의 평균과 표준편차를 구해 변환합니다. transform(X_test)는 그 평균과 표준편차를 그대로 사용합니다. 예를 들어 꽃잎 길이가 1cm 차이, 꽃받침 너비가 1cm 차이일 때 각 특성이 예측에 미치는 상대적 영향은 분포에 따라 다릅니다. 무조건 스케일을 맞춘다고 좋아지는 것은 아니지만, 단위의 숫자 크기 때문에 특정 특성이 거리를 지배하는 상황은 줄일 수 있습니다. scikit-learn 데이터 누수 안내는 테스트 데이터에서 변환 규칙을 학습하지 말라고 설명합니다.

fit 다음 predict가 품종 번호를 계산하고, accuracy_score는 테스트 정답과 비교해 맞힌 비율을 돌려줍니다. 정확도가 예를 들어 0.9라면 테스트 10개 중 9개를 맞힌 것과 같은 비율이지만, 이 코드의 실제 점수가 0.9라는 뜻은 아닙니다. 품종마다 몇 개를 틀렸는지 알려면 혼동 행렬을 추가로 봐야 합니다.

마지막 kneighbors는 새 꽃에 가장 가까운 훈련 꽃의 거리와 행 번호를 알려 줍니다. 행 번호로 y_train을 조회하면 어떤 품종들이 투표했는지 확인할 수 있습니다. 이웃들의 거리가 모두 크다면 단순히 다수결 결과를 믿기보다 훈련에 없던 유형인지 조사할 필요가 있습니다.

실제 데이터에 적용할 때

거리 선택: 숫자 특성에 유클리드 거리를 쓸 수도 있지만 범주형 특성이나 문장 표현에는 다른 거리·유사도가 맞을 수 있습니다. 먼저 특성의 뜻과 단위를 정합니다.

kk와 투표 방식: weights='uniform'은 이웃마다 같은 표를 주고, weights='distance'는 가까운 이웃에 더 큰 가중치를 줍니다. 어느 쪽이 적절한지 훈련 부분의 교차 검증으로 비교합니다. 공식 KNeighborsClassifier API에 두 방식이 정의되어 있습니다.

예를 들어 바로 옆의 A 한 개와 조금 멀리 있는 B 두 개가 이웃이라면 균등 투표는 B를 고릅니다. 거리 가중 투표는 A가 충분히 가까울 때 A를 고를 수도 있습니다. 어느 답이 옳은지는 문제의 의미에 따라 다릅니다. 측정 잡음 때문에 A가 우연히 아주 가까워졌다면 가중 방식이 오히려 불안정해질 수 있습니다. 그래서 k, 거리 함수, 가중 방식을 테스트 세트 점수가 아니라 검증 결과로 선택합니다. 더 좋은 수치가 나왔을 때에는 오분류 사례가 실제로 줄었는지도 확인합니다.

평가와 운영: 새로운 데이터에서 같은 전처리 규칙을 적용해야 합니다. 서비스에서는 표준화기와 분류기를 함께 저장하거나 Pipeline으로 묶어 누락을 방지합니다. 훈련 데이터가 커지면 예측 지연과 저장 비용을 측정하고, 더 빠른 이웃 탐색법이나 다른 모델과 비교합니다. 특히 차원이 매우 높은 데이터에서는 가까운 이웃과 먼 이웃의 거리 차이가 작아져 이웃의 의미가 약해질 수 있습니다.

입력 특성이 바뀌면 저장된 이웃의 좌표도 함께 바뀌어야 합니다. 운영 중에 길이를 cm에서 mm로 바꾸거나 새 특성을 추가했다면 새 입력만 바꾸어서는 안 됩니다. 훈련 데이터와 새 데이터에 같은 순서·단위·전처리를 적용해 모델을 다시 준비합니다. 이 점은 KNN이 단순한 알고리즘이어도 데이터 파이프라인 관리는 단순하지 않다는 뜻입니다.

정리

KNN은 “내 근처의 정답을 참고한다”는 알고리즘입니다. 그렇다고 가까운 점이 언제나 같은 클래스라는 보장은 없습니다. 경계 부근, 희귀한 클래스, 훈련에 없던 유형에서는 실수할 수 있으므로 독립 평가가 필요합니다.

같은 카테고리의 글