[기계학습][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은 차원이 증가할수록 성능 저하가 심함
- 데이터의 차원(dimensionality)이 증가할수록 해당 공간의 크기(부피)가 기하급수적으로 증가하여 동일한 개수의 데이터의 밀도는 차원이 증가할수록 급속도로 희박(sparse)해짐
- 차원이 증가할수록 데이터의 분포 분석에 필요한 샘플 데이터의 개수가 기하급수적으로 증가하게 되는데 이러한 어려움을 표현한 용어가 차원의 저주임
-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))
-표준화로 인해 정확도가 떨어진 사례