[기계학습][4주차] KNN 개념 - mingoori0512/minggori GitHub Wiki

KNN(K-Nearest Neighbors)

한글: K-최근접이웃

분류와 회귀

지도 학습의 대표적인 머신 러닝 방법

분류(classification), 회귀(regression)

분류

-분류는 미리 정의된, 가능성 잇는 여러 클래스 레이블(class label) 중 하나를 예측하는 것

-두 개로만 나누는 이진 분류(Binary classification)와 셋 이상의 클래스로 분류하는 다중 분류(multiclass classification)로 나뉨

-분류 예시: 얼굴 인식, 숫자 판별(MNIST) 등 handwritten dataset...

회귀

-연속적인 숫자 또는 부동소수점수(실수)를 예측하는 것

-회귀 예시: 주식 가격을 예측하여 수익을 내는 알고리즘 등

KNN의 개념

KNN이란?

-주변 k개의 자료의 클래스 중 가장 많은 클래스로 특정 자료를 분류하는 방식

-새로운 자료?를 가장 가까운 자료 5개의 자료(k=5)를 이용하여 투표하여 가장 많은 클래스로 할당

-Training-data 자체가 모형일 뿐 어떠한 추정 방법도 모형도 없음 = 즉, 데이터의 분포를 표현하기 위한 파라미터를 추정하지 않음

-매우 간단한 방법이지만 performance는 떨어지지 않음

-게으른 학습(lazy learner) 또는 사례중심학습(instance-based learning)*model-based learning과 반대

*게으른 학습이란: 알고리즘은 훈련 데이터에서 판별 함수(discriminative function)를 학습하는 대신 훈련 데이터 셋을 메모리에 저장하기 위한 방법

-데이터의 차원이 증가하면 차원의 저주(curse of dimension) 문제가 발생함

즉, KNN은 차원이 증가할수록 성능 저하가 심함

  1. 데이터의 차원(dimensionality)이 증가할수록 해당 공간의 크기(부피)가 기하급수적으로 증가하여 동일한 개수의 데이터의 밀도는 차원이 증가할수록 급속도로 희박(sparse)해짐
  2. 차원이 증가할수록 데이터의 분포 분석에 필요한 샘플 데이터의 개수가 기하급수적으로 증가하게 되는데 이러한 어려움을 표현한 용어가 차원의 저주임

-i번째 관측치와 j번째 관측치의 거리로 Minkowski 거리를 이용

KNN의 하이퍼파라미터

탐색할 이웃 수(k)와 거리 측정 방법

-k가 작을 경우 데이터의 지역적 특성을 지나치게 반영하여 과적합(overfitting) 발생

-반대로 매우 클 경우 모델이 과하게 정규화(underfitting) 발생

KNN의 k가 가지는 의미

-새로운 자료에 대해 근접치 k의 개수에 따라 Group이 달리 분류됨

->다수결의 방식(Major voting): 이웃 범주 가운데 빈도 기준 제일 많은 범주로 새 데이터의 범주를 예측하는 것

->가중 합 방식(Weighted voting): 가까운 이웃의 정보에 더 가중치를 부여

KNN의 장단점 요약

장점

-학습데이터 내에 끼어있는 노이즈의 영향을 크게 받지 않음

-학습 데이터의 수가 많다면 꽤 효과적인 알고리즘

-마할라노비스 거리와 같이 데이터의 분산을 고려할 경우 매우 강건(robust)한 방법론

->마할노비스 거리(Mahalnobis distance)는 평균과의 거리가 표준편차의 몇 배인지를 나타내는 값 ->즉, 어떤 값이 얼마나 일어나기 힘든 값인지, 또는 얼마나 이상한 값인지를 수치화하는 한 방법

단점

-최적 이웃의 수(k)와 어떤 거리 척도(distance metric)가 분석에 적합한지 불분명해 데이터 각각의 특성에 맞게 연구자가 임의로 선정해야 함

->best K는 데이터마다 다르기 때문에 탐욕적인 방식(Grid Search)으로 탐색

-새로운 관측치와 각각의 학습 데이터 사이의 거리를 전부 측정해야 하므로 계산 시간이 오래 걸리는 한계

-KNN의 계산 복잡성을 줄이려는 시도들

->Locality Sensitive Hashing, Network based Indexer, Optimized product quantization

KNN의 적용

기계 학습의 일반적인 실습 순서

-데이터셋 불러오기

->seaborn 라이브러리 사용, 유명한 데이터 셋 대부분 지원(예: Iris)

-데이터셋 카테고리의 실수화

-> setosa, versicolor, virginica -> "0", "1", "2"

-데이터 분할

->학습데이터와 테스트 데이터로 나누기

-(옵션) 입력데이터의 표준화

-모형 추정 혹은 사례중심학습

-결과 분석

->confusion matrix로 확인

Iris 데이터셋이란?

-데이터명: IRIS(아이리스, 붓꽃 데이터)

-레코드수: 150개(N)

-필드개수: 5개(D, Dimension)

-데이터설명: 아이리스(붓꽃) 데이터에 대한 데이터. 꽃잎의 각 부분의 너비와 길이 등을 측정한 데이터이며 150개의 레코드로 구성되어 있음.

-필드의 이해: 총 6개의 데이터로 구성되어 있음.caseno는 단지 순서를 표시하므로 분석에서 제외. 2번째부터 5번째의 4개의 필드는 입력 변수(X)로 사용되고, 맨 아래의 Species 속성이 목표, 종속 변수(Y)로 사용된다.

Iris 데이터셋 불러오기

import seaborn as sns
iris = sns.load_dataset('iris') #iris라는 변수명으로 Iris data를 download함.
print(iris.head())
print(iris.shape)

X = iris.drop('species', axis=1) # 'species'열을 drop하고 input X를 정의함.
print(X.shape)

y = iris['species']

카테고리의 실수화

sklearn.preprocessing.LabelEncoder

-Encode target labels with value between 0 and n_classes-1.

-This transformer should be used to encode target values, i.e. y, and not the input X.

from sklearn import preprocessing # LabelEncoder() method를 불러옴
le = preprocessing.LabelEncoder() # 인스턴스 생성
import numpy as np
y = le.fit_transform(iris['species'].values) # species 열의 문자열을 categorical 값으로 전환
print("species labels: ", np.unique(y)) # 중복되는 y 값을 하나로 정리하여 print

-[주의] DictVectorize 클래스 vs LabelEncoder 클래스

-> One-hot encoding vs 범주형 라벨

데이터 분할

-데이터 분할이란?

학습 데이터(train)와 시험 데이터(test)가 서로 겹치지 않도록 나누는 것

-데이터 분할의 목적

학습데이터로 자료를 학습시키고 학습에 전혀 사용하지 않은 시험데이터에 적용하여 학습 결과의 일반화(generalization)가 가능한지 알아보기 위함

-모델이 과적합되었다면, validation셋으로 검증시 예측율이나 오차율이 떨어지는 현상을 확인할 수 있으며, 이런 현상이 나타나면 학습을 종료

from sklearn.model_selection import train_test_split # Scikit-Learn의 model_selection library를 train_test_split로 명명
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)

-train_test_split() 함수의 인자 설명

옵션값 설명 -test_size: 테스트 셋 구성의 비율을 나타냅니다. train_size의 옵션과 반대 관계에 있는 옵션 값이며, 주로 test_size를 지정해 줍니다. 0.2는 전체 데이터 셋의 20%를 test(validation)셋으로 지정하겠다는 의미입니다. default 값은 0.25입니다.

-shuffle: defalut=True입니다. split을 해주기 이전에 섞을건지 여부입니다. 보통은 default 값으로 놔둡니다.

-stratify: default=None입니다. classification을 다룰 때 매우 중요한 옵션값입니다. stratify 값을 target으로 지정해주면 각각의 class 비율(ration)를 train/validation에 유지해 줍니다. (한 쪽으로 쏠려서 분배되는 것을 방지합니다.) 만약 이 옵션을 지정해 주지 않고 classification 문제를 다룬다면, 성능의 차이가 많이 날 수 있습니다.

-random_state: 세트를 섞을 때 해당 int 값을 보고 섞으며, 하이퍼파라미터를 튜닝시 이 값을 고정해두고 튜닝해야 매번 데이터셋이 변경되는 것을 방지할 수 있습니다.

모형 추정 및 사례중심 학습

# KNN의 적용
from sklearn.neighbors import KNeighborsClassifier # KNN 불러오기
knn = KNeighborsClassifier(n_neighbors=5, p=2) # 5개의 인접한 이웃, 거리측정기준: 유클리드
# knn.fit(X_train_std, y_train) 
knn.fit(X_train, y_train) # 모델 fitting 과정

결과 분석

# y_train_pred.predict(X_train_std) # train data의 y값 예측치
y_train_pred = knn.predict(X_train) # train data의 y값 예측치
# y_test_pred = knn.predict(X_test_std) # 모델을 적용한 test data의 y값 예측치
y_test_pred = knn.predict(X_test)
print('Misclassified training sampels: %d' %(y_train!=y_train_pred).sum()) # 오분류 데이터 갯수 확인
print('Misclassified test samples: %d' %(y_test!=y_test_pred).sum()) # 오분류 데이터 갯수 확인
from sklearn.metrics import accuracy_score # 정확도 계산을 위한 모듈 import
print(accuracy_score(y_test, y_test_pred)) # 45개 test sample 중 42개가 정확하게 분류됨.

-성능 평가 -> 분류 문제는 회귀 분석과 달리 다양한 성능 평가 기준(metric)이 필요함

->평가 방법마다 장단점이 존재함

-싸이킷런에서 제공하는 분류 성능 평가 방법

-confusion_matrix(y_true, y_pred)

-accuracy_score(y_true, y_pred)

-혼합 행렬(confusion matrix): 타겟의 원래 클래스와 모형이 예측한 클래스와 일치하는지는 갯수로 센 결과를 표로 나타낸 것

from sklearn.metrics import confusion_matrix # 오분류표 작성을 위한 모듈 import
conf = confusion_matrix(y_test, y_test_pred) # 대각원소가 각각 setosa, versicolor, virginica를 정확하게 분류한 갯수
print(conf)

##(옵션) 입력데이터의 표준화 -표준화

->특정 자료의 측정 단위(Scaling)에 의해 영향 받지 않도록 하는 과정

->싸이킷런의 StandardScaler 클래스를 호출하여 사용

->시험 데이터(test data)의 표준화는 학습 데이터(train data)에서 구한 특성 변수의 평균과 표준편차를 이용함

->표준화로 인해 데이터의 분포인 통계적 특성이 깨지면 머신러닝의 학습 저하를 가져옴

from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
sc.fit(X_train)
X_train_std = sc.transform(X_train) # training data의 표준화
X_test_std = sc.transform(X_test) # test data의 표준화

# 표준화된 data의 확인
print(X_train.head())
X_train_std[1:5,]
# KNN의 적용
from sklearn.neighbors import KNeighborsClassifier # KNN 불러오기
knn = KNeighborsClassifier(n_neighbors=5, p=2) # 5개의 인접한 이웃, 거리측정기준: 유클리드
knn.fit(X_train_std, y_train) 
# knn.fit(X_train, y_train) # 모델 fitting 과정
# y_train_pred = knn.predict(X_train) # train data의 y값 예측치
y_test_pred = knn.predict(X_test_std) # 모델을 적용한 test data의 y값 예측치
# y_test_pred = knn.predict(X_test)
print('Misclassified training sampels: %d' %(y_train!=y_train_pred).sum()) # 오분류 데이터 갯수 확인
print('Misclassified test samples: %d' %(y_test!=y_test_pred).sum()) # 오분류 데이터 갯수 확인
from sklearn.metrics import accuracy_score #정확도 계산을 위한 모듈 import
print(accuracy_score(y_test, y_test_pred))

-표준화로 인해 정확도가 떨어진 사례