본문 바로가기
Programming/Python

[ML] KN 분류 알고리즘 (실전편)

기본편 게시물과 전혀 다른 방법으로, numpy로 세련되게 데이터를 정리해보자

 

 

column_stack(리스트1, 리스트2.. .)   <- 리스트 첫째 값을 1행, 둘째 값을 2행.. 등 으로 묶는 함수

import numpy as np

fish_data = np.column_stack((fish_length, fish_weight))
# 길이와 무게 data를 이렇게 한번에 묶는다.
print(fish_data[:5])

[[ 25.4 242. ]
 [ 26.3 290. ]
 [ 26.5 340. ]
 [ 29.  363. ]
 [ 29.  430. ]]

 

 

 

ones(개수), zeros(개수)  : 개수만큼 1(0)을 리스트로 반환

concentrate(리스트1, 리스트2...)  : 리스트들을 그대로 가로로 이어붙이기

fish_target = np.concatenate((np.ones(35), np.zeros(14)))
print(fish_target)

  [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0.]

 

(!) numpy array 사용 이유: 데이터 클수록 파이썬 리스트는 비효율적이므로 array(배열; matrix) 사용 권장.

 

 

 

 

 

train, test set 한 번에 나누기

from sklearn.model_selection import train_test_split

# Test 데이터를 2.5:1로(test size 미입력 default) 분할
train_input, test_input, train_target, test_target = train_test_split(fish_data, fish_target, 
                                                      stratify=fish_target, random_state=42)

stratify = target data로 지정하여 sampling bias를 방지

 

 

 

 

KN으로 다시 훈련.

 

from sklearn.neighbors import KNeighborsClassifier

kn = KNeighborsClassifier()
kn.fit(train_input, train_target)  #훈련
kn.score(test_input, test_target)  #테스트

1.0   <- 100%

 

 

 

print(kn.predict([[25, 150]]))  # 도미데이터 예측 시도

 [0.]            <- 1이 나와야하는데 0이 나온다. 그래프로 보자

 

 

import matplotlib.pyplot as plt

plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(25, 150, marker='^')  # 마커로 표시하기.
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

 

이유는 KN default 개수가 5개인데 가까운 5개 주변값중 4개가 빙어이기 때문이다. 

distances, indexes = kn.kneighbors([[25, 150]]) # 특정 데이터 주변의 5개 거리, 인덱스 반환
print('distances = ', distances) # 5개의 이웃 데이터의 거리
print('indexes = ', indexes) # 5개의 이웃 인덱스 번호

distances =  [[ 92.00086956 130.48375378 130.73859415 138.32150953 138.39320793]]
indexes =  [[21 33 19 30  1]]

 

 

plt.scatter(train_input[:,0], train_input[:,1]) # 전체 데이터를 스케터 플랏에 표시
plt.scatter(25, 150, marker='^')
plt.scatter(train_input[indexes,0], train_input[indexes,1], marker='D') 
#위 index 5개 값을 Diamond로 표시
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

print(train_target[indexes]) # 위 인덱스의 training Target data(1:도미, 0:빙어)

[[1. 0. 0. 0. 0.]]   <- 이러니 다수결로 0이 출력됨

 

 

 

 

더 큰 문제는 x축과 y축 단위가 g와 cm으로 다르다는 점이 문제다. 즉, 두 값의 scale이 다르다. 이 둘의 측정 단위가 다른데 KN으로 근사 5개 값을 찾는게 정확하다고 보기 힘들다.

 

 

 

이에 따라 두 데이터를 표준점수로 만들어서 측정해야한다. 가장 무난하고 대중적인 scale 정리 방법이다.

표준점수 = (데이터 - 평균) / 표준편차

mean = np.mean(train_input, axis=0) # axis=0:행을 고정, 열의 평균을 구함
std = np.std(train_input, axis=0)

train_scaled = (train_input - mean) / std # 표준점수로 변환된 데이터
new = ([25, 150] - mean) / std # 샘플 25, 150 데이터를 동일한 비율로 변환한 것.


plt.scatter(train_scaled[:,0], train_scaled[:,1]) # 동일한 비율로 변환된 train data 표시
plt.scatter(new[0], new[1], marker='^') # 샘플 25, 150을 동일한 비율로 변환된 data 표시
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

weight, length 단위가 같음에 주목한다.

test_scaled = (test_input - mean) / std 
# train set 기준으로 변환된 테스트 데이터 값. 까먹지 말고 변환!

kn.fit(train_scaled, train_target) # 동일한 비율로 변환된 데이터 값 학습
kn.score(test_scaled, test_target)

1.0   <- 100%

 

print(kn.predict([new])) # 샘플데이터 빙어로 예측

[1.] 

 

 

K 근방 5 이웃이 어떻게 잡혔는지 그래프로 보자

distances, indexes = kn.kneighbors([new]) # KN의 거리, 인덱스 default 5개 출력


plt.scatter(train_scaled[:,0], train_scaled[:,1])
plt.scatter(new[0], new[1], marker='^') # 측정하고자하는 값
plt.scatter(train_scaled[indexes,0], train_scaled[indexes,1], marker='D') #주변 5개
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

 

 

제대로 분류하였다.

728x90
반응형