Data Analysis & ML/Machine Learning

[Machine Learning][머신러닝][지도학습] K-최근접 이웃(KNN)

YSY^ 2020. 9. 1. 14:24

K-최근접 이웃 (K-Nearest Neighbors, K-NN)

  • 분류(Classification)와 회귀(Regression) 를 모두 지원한다.
  • 예측하려는 데이터와 input 데이터들 간의 거리를 측정해 가장 가까운 K개의 데이터셋의 레이블을 참조해 분류/예측한다.
  • 학습시 단순히 input 데이터들을 저장만 하며 예측 시점에 거리를 계산한다.
    • 학습은 빠르지만 예측시 시간이 많이 걸린다.

  • K-NN에서 K는 새로운 데이터포인트를 분류할때 확인할 데이터 포인트의 개수를 지정하는 하이퍼파라미터
  • K를 1로 하면 파란색, K를 3으로 하면 주황색 으로 분류한다.
  • K가 너무 작으면 과적합(Overfitting)이 일어나고 K가 너무 크면 성능이 나빠진다. (Underfitting).

 

주요 하이퍼 파라미터

  • 이웃 수
    • n_neighbors = K
    • K가 작을 수록 모델이 복잡해져 과적합이 일어나고 너무 크면 단순해져 성능이 나빠진다.
    • n_neighbors는 Feature수의 제곱근 정도를 지정할 때 성능이 좋은 것으로 알려져 있다.
  • 거리 재는 방법
    • p=2: 유클리디안 거리(Euclidean distance - 기본값)
    • p=1: 맨하탄 거리(Manhattan distance)

 

유클리디안 거리(Euclidean_distance)

 

맨하탄 거리 (Manhattan distance)

요약

  • K-NN은 이해하기 쉬운 모델이며 튜닝할 하이퍼파라미터의 수가 적어 빠르게 만들 수있다.
  • K-NN은 서비스할 모델을 구현할때 보다는 복잡한 알고리즘을 적용해 보기 전에 확인용 또는 base line을 잡기 위한 모델로 사용한다.
  • 훈련세트가 너무 큰 경우(Feature나 관측치의 개수가 많은 경우) 거리를 계산하는 양이 늘어나 예측이 느려진다.
  • Feature간의 값의 단위가 다르면 작은 단위의 Feature에 영향을 많이 받게 되므로 전처리로 Scaling작업이 필요하다.
  • Feature가 너무 많은 경우와 대부분의 값이 0으로 구성된(희소-sparse) 데이터셋에서 성능이 아주 나쁘다

 

위스콘신 유방암 데이터를 이용한 암환자분류

K값 변화에 따른 성능 평가

  • malignant: 악성
  • benign : 양성
import pandas as pd
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(cancer.data, 
                                                    cancer.target, 
                                                    stratify=cancer.target)

# feature scaling (각 feature(컬럼)간의 단위(범위)를 맞춰준다.)
# Train set으로 fit, 새로운데이터(X_test, new data)의 scaling을 train set에 맞춰서 한다.
from sklearn.preprocessing import StandardScaler#, MinMaxScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train) #fit(X_train), tranform(X_train)
X_test_scaled = scaler.transform(X_test)

#KNN
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score

k_param = range(1,11) #k값을 1 ~ 10 변경하면서 성능을 체크
train_acc_list = []
test_acc_list = []

for k in k_param: 
    knn = KNeighborsClassifier(n_neighbors=k) 
    # 모델 학습
    knn.fit(X_train_scaled, y_train)
    # 예측 및 검증
    pred_train = knn.predict(X_train_scaled)
    pred_test = knn.predict(X_test_scaled)
    # 성능점수를 list에 추가
    train_acc_list.append(accuracy_score(y_train, pred_train))
    test_acc_list.append(accuracy_score(y_test, pred_test))
dic = {
    'k':k_param,
    'Train 정확도':train_acc_list,
    'Test 정확도':test_acc_list
}
acc_df = pd.DataFrame(dic)
acc_df

 

import matplotlib.pyplot as plt
acc_df2 = acc_df.set_index('k')
acc_df2 = acc_df2.sort_index(ascending=False)

acc_df2.plot(figsize=(7,7))
# plt.plot(acc_df2.index, acc_df2['Train 정확도'])
# plt.plot(acc_df2.index, acc_df2['Test 정확도'])
plt.xlabel('K값-모델의 복잡도')
plt.show()

 

knn_clf = KNeighborsClassifier(n_neighbors=7)
knn_clf.fit(X_train_scaled, y_train)
p_train = knn_clf.predict(X_train_scaled)
p_test = knn_clf.predict(X_test_scaled)
accuracy_score(p_train, y_train), accuracy_score(p_test, y_test)
(0.9765258215962441, 0.9790209790209791)

파이프라인(scaling, knn), gridsearchcv (가장 성능좋은 k수 찾기.)

from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline

pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('knn', KNeighborsClassifier())
])

param_grid = {
    "knn__n_neighbors":range(1,11),
    "knn__p":[1,2]
}

gs = GridSearchCV(pipeline,
                 param_grid=param_grid,
                 cv=5)

 

# KNeighborsClassifier(n_neighbors=5, p=2)
#베스트 모델 조회
best_model = gs.best_estimator_
#학습
best_model.fit(X_train_scaled, y_train)
#예측 검증
pred_train = best_model.predict(X_train_scaled)
pred_test = best_model.predict(X_test_scaled)
accuracy_score(pred_train, y_train), accuracy_score(pred_test, y_test)
(0.9788732394366197, 0.972027972027972)

accuracy_score(y_train, gs.predict(X_train_scaled))
0.9788732394366197
728x90
반응형