[PYTHON]-(08) 데이터 마이닝[k-NN]
k-NN(k Nearest Neighbors)
kNN은 범주형 결과를 위한 분류 문제나 수치형 결과를 위한 예측문제에 모두 적용 가능한 방법이다.
새로운 데이터와 거리가 가장 가까운 k개 데이터의 정보를 이용해 분류와 예측 문제를 해결하는 방식이다.
1. 개념
kNN의 아이디어는 학습 데이터로부터 분류하고자 하는 새로운 데이터와 유사한 k개의 데이터를 식별하는 것이다.
kNN은 종속변수와 예측변수들 간의 관계에 대한 가정을 만들지 않고, 비모수적 방법으로서 함수 형태에 대한 모수 추정 또한 포함하지 않는다.
1.1 이웃 결정하기
kNN은 데이터 셋의 레코드들 간의 유사성으로부터 정보를 얻는다.
그 방법으로 예측변수의 값에 기반한 레코드들 간의 거리로서 유사성을 측정한다.
가장 보편적인 거리 척도는 유클리드 거리를 사용한다.
이 외 거리 종류에는 코사인, 맨해튼, 해밍, 마할라노비스 거리 등이 있다.
1.2 분류 규칙
기존 레코드와 새로운 레코드 사이의 거리를 계산한 후, 레코드를 한 클래스로 배정할 수 있는 규칙이 필요하다.
이 때 가장 단순한 경우는 k=1인 경우이며, 가장 유사한 레코드가 속한 클래스에 새로운 데이터를 배정하는 것이다.
k>1 이상인 경우에는 k개의 이웃을 찾고 다수결 결정 규칙을 통해 클래스에 배정한다.
1.3 k값 선택
k>1 이상일 때 장점으로는, k값이 커질수록 학습데이터에 존재하는 잡음으로 인해 과적합화의 위험을 줄여주는 평활효과를 얻는다는 것이다.
하지만 k가 너무 큰 경우에도 데이터의 지역적 구조를 파악할 수 없게 되므로 적절한 k를 선택해야한다.
이에 대한 방법으로는 데이터 분할을 통해 가장 분류 성능이 좋은 k를 선택하는 것이다.
1.4 주요 이슈 및 장단점
이슈
- kNN은 학습용 데이터에 대한 의존도가 절대적이므로, 정확하고 적확한 요건정의를 통해 대표성이 있는 학습용 데이터를 확보해야 한다.
- 대표성이 떨어지는 관측치가 존재하면 제거하는 것이 옳다.
- 거리 지표를 사용하기 때문에 척도(scale)에 영향을 많이 받으므로, 표준화 작업이 선행되어야 한다.
- 설명변수가 결측치를 갖는 경우 계산은 불가능하기 때문에 결측치에 대한 처리가 반드시 필요하다.(대체, 변수 제거, 행 제거, 범주화 등)
장점
- 단순하고 파라미터에 대한 가정이 거의 없다.
- 충분히 많은 학습데이터와 분류 기준이 여러가지일 때 성능이 좋다.
- 활용도가 높고, 결측치 처리나 상품 추천 등에서 다양하게 활용된다.
단점
- 학습 데이터로부터 파라미터를 추정하는 데 걸리는 시간이 엄청나게 소요될 수 있다.
- 설명 변수 수 증가에 따라 학습 데이터로 필요한 관측치 수가 기하급수적으로 증가한다.(= 차원의 저주)
- 예측변수의 개수를 줄이거나, 주성분 분석이나 특이값 분해, 요인분석으로 예측변수를 결합시켜 줄이는 방법을 사용
2. 예제 : Titanic
로지스틱 회귀분석에서 사용했던 변수 사용
import numpy as np
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from matplotlib import pyplot as plt
from matplotlib.colors import ListedColormap
from pylab import rcParams
%matplotlib inline
import warnings
warnings.filterwarnings('ignore')
df_knn = pd.read_csv('C:/Users/opqrs/Desktop/블로그용 쥬피터/Python_ML/titanic.csv').drop('Unnamed: 0', axis=1)
df_knn.head(5)
| survived | age | sibsp | parch | fare | pclass_2 | pclass_3 | sex_1 | |
|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 22.0 | 1 | 0 | 2.110213 | 0.0 | 1.0 | 1.0 |
| 1 | 1 | 38.0 | 1 | 0 | 4.280593 | 0.0 | 0.0 | 0.0 |
| 2 | 1 | 26.0 | 0 | 0 | 2.188856 | 0.0 | 1.0 | 0.0 |
| 3 | 1 | 35.0 | 1 | 0 | 3.990834 | 0.0 | 0.0 | 0.0 |
| 4 | 0 | 35.0 | 0 | 0 | 2.202765 | 0.0 | 1.0 | 1.0 |
knn 적용
k의 수를 3, 가중치는 uniform(가중치 x)으로 fit()을 해보자
X = np.array(df_knn[['fare','age']])
y = np.array(df_knn['survived'])
clf = KNeighborsClassifier(3, weights='uniform')
clf.fit(X,y)
KNeighborsClassifier(n_neighbors=3)
def graph_knn(neighbors_num, weights_char, xlabel_char, ylabel_char):
h = 0.02
cmap_light = ListedColormap(['#FFAAAA','#AAAAFF'])
cmap_bold = ListedColormap(['#FF0000','#0000FF'])
x_min, x_max = X[:,0].min()-1, X[:,0].max()+1
y_min, y_max = X[:,1].min()-1, X[:,1].max()+1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
pred_ = np.c_[xx.ravel(), yy.ravel()]
z = clf.predict(pred_)
z = z.reshape(xx.shape)
plt.figure
plt.pcolormesh(xx, yy, z, cmap=cmap_light)
plt.scatter(X[:,0], X[:,1], c=y, cmap=cmap_bold,
edgecolors='k', s=20)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.title("KNN (k = %i, weights = '%s')"%(neighbors_num, weights_char))
plt.xlabel(xlabel_char)
plt.ylabel(ylabel_char)
plt.show()
graph_knn(clf.n_neighbors, clf.weights, 'Fare', 'Age')
/output_7_0.png)
붉은색은 0(dead) 파란색은 1(alive)를 의미한다.
그리드서치를 통해 최적화된 파라미터를 찾은 후 다시 fit해보자
knn = KNeighborsClassifier()
k_range = list(range(1,30))
weight_options = ['uniform','distance']
k_grid = dict(n_neighbors=k_range, weights=weight_options)
clf = GridSearchCV(knn, k_grid, cv=10, scoring='accuracy')
clf.fit(X,y)
GridSearchCV(cv=10, estimator=KNeighborsClassifier(),
param_grid={'n_neighbors': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29],
'weights': ['uniform', 'distance']},
scoring='accuracy')
df_clf = pd.DataFrame(clf.cv_results_)
df_clf[['param_n_neighbors','param_weights','params','mean_test_score','rank_test_score']].sort_values(['rank_test_score']).head()
| param_n_neighbors | param_weights | params | mean_test_score | rank_test_score | |
|---|---|---|---|---|---|
| 24 | 13 | uniform | {'n_neighbors': 13, 'weights': 'uniform'} | 0.689906 | 1 |
| 20 | 11 | uniform | {'n_neighbors': 11, 'weights': 'uniform'} | 0.689906 | 2 |
| 12 | 7 | uniform | {'n_neighbors': 7, 'weights': 'uniform'} | 0.688400 | 3 |
| 10 | 6 | uniform | {'n_neighbors': 6, 'weights': 'uniform'} | 0.684116 | 4 |
| 28 | 15 | uniform | {'n_neighbors': 15, 'weights': 'uniform'} | 0.681436 | 5 |
k가 13일 때 0.6899로 가장 높은 스코어를 보인다.
graph_knn(clf.best_params_['n_neighbors'], clf.best_params_['weights'], 'Fare', 'Age')
/output_12_0.png)
데이터 분할 및 정규화
feature_columns = df_knn.columns.difference(['survived'])
X = df_knn[feature_columns]
y = df_knn['survived']
# 정규화
from sklearn import preprocessing
X = preprocessing.StandardScaler().fit(X).transform(X)
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.3, random_state=7)
모형 학습 및 검증
# k = 13
knn = KNeighborsClassifier(13, weights='uniform')
# 학습
knn.fit(X_train, y_train)
# 예측
y_hat = knn.predict(X_test)
성능확인
from sklearn import metrics
knn_report = metrics.classification_report(y_test, y_hat)
print(knn_report)
precision recall f1-score support
0 0.79 0.90 0.84 129
1 0.81 0.64 0.71 85
accuracy 0.79 214
macro avg 0.80 0.77 0.78 214
weighted avg 0.80 0.79 0.79 214
f1-score를 보면 dead(0)의 예측 정확도가 0.84 alive(1)의 예측 정확도가 0.71이다.
Leave a comment