본문 바로가기

파이썬/머신러닝

K-최근접 이웃 (K-NN) 분류기, 가장 간단한 머신러닝 알고리즘

K-최근접 이웃은 가장 간단한 머신러닝 알고리즘이다. 일반적으로 머신러닝 모델을 만들면 clf.fit(X_train, y_train)으로 훈련시키고 테스트해서 성능을 평가하는데 k-nn은 따로 훈련이 필요없다. 훈련 데이터셋을 그냥 저장만 하면 되기 때문에 lazy model이라고도 한다.

 

 

출처: 위키피디아

 

k-nn은 가장 가까운 훈련 데이터 포인트 k개를 최근접 이웃으로 찾아 예측에 사용한다. k가 1이라면 가장 가까운 포인트 1개를 예측에 사용하는 것이고, k가 3이라면 3개를 예측에 사용하는 것이다.

 

 

위 그림을 예로 들어보자. 초록색 원이 예측하고자 하는 데이터이다. 만약 k=3이라면 가장 가까운 거리에 있는 3개의 데이터는 빨간색 삼각형 2개와 파란색 사각형 1개이므로 아마 해당 데이터를 빨간색 삼각형으로 예측할 것이다.

 

 

k=5라면 5개의 범위 내에서 가깝게 있는 데이터가 빨간색 삼각형 2개와 파란색 사각형 3개이므로 이때는 데이터를 파란색 사각형으로 예측할 것이다.

 

 

즉, k를 몇으로 설정하냐에 따라서 예측값이 달라진다.

 

 

k가 너무 작으면 오버피팅이 발생하기 쉽다. 아주 근처에 있는 점 하나에 민감하게 반응하기 때문에 분류 정확도가 낮아진다. 반면 k가 너무 크면 너무 많은 데이터가 예측에 사용되기 때문에 언더피팅 될 확률이 높다.

 

 

추가로 knn 분류기를 사용할 때는 모든 특성을 고르게 반영하기 위해 정규화가 반드시 필요하다. 

 

데이터 스케일링과 fit, transform 차이

 


사이킷런의 k-nn classifier를 이용해서 실습해보자.

 

 

우선 파라미터부터 살펴보자. K-NN의 k를 결정하는 n_neighbors는 기본값이 5이다. 짝수일 경우 동점이 되어서 하나의 결과를 도출하기 어려운 상황이 발생할 수 있기 때문에 일반적으로 홀수값을 사용한다.

 

 

weights는 이웃을 설정할 때 가중치를 주는 방법이다. 'uniform'은 모든 포인트가 동등하게 가중치를 받도록 하고, 'distance'는 거리가 가까운 이웃의 영향을 더 많이 받도록 설정한다. 

 

 

사이킷런의 load_breast_cancer 데이터로 코드 실습을 해보았다.

 

from sklearn.datasets import load_breast_cancer
import pandas as pd

data = load_breast_cancer()
df_data = pd.DataFrame(data.data)
df_labels = pd.DataFrame(data.target)
df_data.head()

 

X값을 보면 컬럼마다 데이터 값이 중구난방이다. 보다 더 정확한 통계 수치를 확인하기 위해 df_data.describe() 으로 확인해보면 아래와 같다.

 

보면 0번 컬럼은 평균값이 14인데 3번 컬럼은 평균이 654이다. 각 컬럼마다 데이터 값의 크기가 들쭉날쭉하기 때문에 전처리가 반드시 필요하다.

 

from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(df_data, df_labels, test_size=0.4, random_state=42)
X_val, X_test, y_val, y_test = train_test_split(X_test, y_test, test_size=0.5, random_state=42)

scaler = StandardScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_val_scaled = scaler.transform(X_val)
X_test_scaled = scaler.transform(X_test)
X_train.describe()

 

정규화 후에 describe함수를 써서 다시 확인해보면 이제 모든 컬럼의 값이 다 동일한 스케일을 갖게 되었다.

 

from sklearn.neighbors import KNeighborsClassifier

clf = KNeighborsClassifier(n_neighbors = 5)

clf = KNeighborsClassifier(n_neighbors=5)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_val)

print(classification_report(y_pred, y_val))
print(roc_auc_score(y_pred, y_val))

 

AUC는 0.95, 정밀도 0.95, 재현율 0.93로 성능이 아주 좋다.