NAN/머신 러닝
KNN 간단 예제_3
onddd
2021. 7. 13. 08:57
728x90
데이터 전처리
# 데이터 준비
fish_length = [25.4, 26.3, 26.5, 29.0, 29.0, 29.7, 29.7, 30.0, 30.0, 30.7, 31.0, 31.0,
31.5, 32.0, 32.0, 32.0, 33.0, 33.0, 33.5, 33.5, 34.0, 34.0, 34.5, 35.0,
35.0, 35.0, 35.0, 36.0, 36.0, 37.0, 38.5, 38.5, 39.5, 41.0, 41.0, 9.8,
10.5, 10.6, 11.0, 11.2, 11.3, 11.8, 11.8, 12.0, 12.2, 12.4, 13.0, 14.3, 15.0]
fish_weight = [242.0, 290.0, 340.0, 363.0, 430.0, 450.0, 500.0, 390.0, 450.0, 500.0, 475.0, 500.0,
500.0, 340.0, 600.0, 600.0, 700.0, 700.0, 610.0, 650.0, 575.0, 685.0, 620.0, 680.0,
700.0, 725.0, 720.0, 714.0, 850.0, 1000.0, 920.0, 955.0, 925.0, 975.0, 950.0, 6.7,
7.5, 7.0, 9.7, 9.8, 8.7, 10.0, 9.9, 9.8, 12.2, 13.4, 12.2, 19.7, 19.9]
import numpy as np
# (열방향)column_stack 옵션 두개 이상의 데이터를 같은 인덱스 번호끼리 튜플 형태로 묶어준다.
# (행방향)row_stack
np.column_stack(([1,2,3],[4,5,6]))
array([[1, 4],
[2, 5],
[3, 6]])
# 확인
fish_data = np.column_stack((fish_length,fish_weight))
print(fish_data[:5])
[[ 25.4 242. ]
[ 26.3 290. ]
[ 26.5 340. ]
[ 29. 363. ]
[ 29. 430. ]]
# np.concatenate() 함수와 np.ones, np.zeros를 이용해 target data를 간편하게 만들 수 있다.
# stack와 다르게 1차원 배열을 붙여준다는 차이가 있다.
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.]
지난 포스트에서 데이터를 input data와 target data를 준비할 때 리스트를 이용해 묶었다면,
이번에는 numpy 를 활용하여 보다 간결하게 데이터를 묶었다. 이렇게 라이브러리를 잘 활용한다면 효율이 증가한다.
from sklearn.model_selection import train_test_split
# train_test_split() : 입력 데이터, 타겟 데이터를 전달해 나눠주는 함수
# 2개의 배열을 입력하면 4개가 반환되고, 3개는 6개가 반환된다.
# stratify= : 비율에 맞춰 데이터를 섞어 준다. (주로 target data에 사용)
train_input, test_input, train_target, test_target =
train_test_split(fish_data, fish_target, stratify = fish_target, random_state=42)
print(train_input.shape, test_input.shape)
(36, 2) (13, 2)
print(train_target.shape,test_target.shape)
(36,) (13,)
print(test_target)
[0. 0. 1. 0. 1. 0. 1. 1. 1. 1. 1. 1. 1.]
지금까지의 과정을 통해 지난 시간과 같은 데이터를 준비했다.
분명 잘 만든 이진 분류 프로그램이라 생각했지만 여기에는 우리가 간과하고 넘어간 부분이 존재한다.
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier()
kn.fit(train_input, train_target)
kn.score(test_input,test_target)
1.0
print(kn.predict([[25,150]]))
[0.]
데이터를 훈련 시킨 값은 1.0으로 정확하게 일치하지만 새로운 데이터를 넣어 분류를 하니 우리가 기대했던 것과 달리
상반된 결과를 얻게되었다. 시각화를 통해 자세히 알아보자
# 시각화
import matplotlib.pyplot as plt
# kn.kneighbores('data')를 통해 특정 데이터를 지정하면 이웃의 거리와 인덱스를 알 수 있다.
distances, indexes = kn.kneighbors([[25, 150]])
# 전체 데이터
plt.scatter(train_input[:,0], train_input[:,1])
# 지정 데이터 별도 표시
plt.scatter(25, 150, marker = '^')
# kn.kneighbores('data')를 통해 구한 데이터로 인덱스를 구했기 때문에 배열 인덱싱을 사용했다.
plt.scatter(train_input[indexes,0],train_input[indexes,1], marker = 'D')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
그래프를 천천히 살펴보게되면 확실하게 새로운 데이터와 근접하다 측정된 기존 데이터가 나눠진 것을 볼 수 있는데
여기서 약간의 의구심이 생긴다. 육안으로 보아도 신규 데이터와 더 가까운 데이터는 우측에 존재하는 데이터인데
어째서 이런 오류가 생긴걸까? 그 이유는 x축과 y축의 단위가 차이가 극심하게 나기 때문에다.
# kn.kneighbores('data')로 구한 인덱스를 통해 이웃 데이터 확인
print(train_input[indexes])
[[[ 25.4 242. ]
[ 15. 19.9]
[ 14.3 19.7]
[ 13. 12.2]
[ 12.2 12.2]]]
# target data 확인
print(train_target[indexes])
[[1. 0. 0. 0. 0.]]
# 거리 확인
print(distances)
[[ 92.00086956 130.48375378 130.73859415 138.32150953 138.39320793]]
distances, indexes = kn.kneighbors([[25, 150]])
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')
# xlim() : 축의 범위를 수동으로 지정 할 수 있는 메서드
plt.xlim((0, 1000))
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
x축과 y축의 범위를 맞춰주니 그래프가 y축으로 편향되었다.
이는 데이터를 나누는 기준이 길이와 무게가 아닌, 무게에만 집중되었다는 의미이므로
데이터 처리가 잘 되지않았다는 증거이다.
# 표준 점수로 바꾸기
# (특성 - 평균) / 표준 편차
mean = np.mean(train_input, axis=0)
std = np.std(train_input, axis=0)
print(mean,std)
[ 27.29722222 454.09722222] [ 9.98244253 323.29893931]
# numpy는 브로드 캐스팅이 적용되기 때문에 아래와 같이 식을 적용해도
# 모든 배열에 동일하게 식이 적용되어 값을 만들어준다.
train_scaled = (train_input - mean) / std
# 위 과정을 거치고 그대로 데이터를 시각화 하면
# 샘플 데이터만 지나치게 큰 값을 가지게 되는데 이런것을 막기 위해 샘플 데이터도
# 기존 데이터와 같은 과정을 거쳐 정제해줘야한다.
plt.scatter(train_scaled[:,0], train_scaled[:,1])
plt.scatter(25, 150, marker = '^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
# 샘플 데이터 정제
new = ([25,150] - mean) / std
# 전체 데이터
plt.scatter(train_scaled[:,0], train_scaled[:,1])
# 샘플 데이터
plt.scatter(new[0], new[1], marker = '^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
kn.fit(train_scaled, train_target)
#KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
# metric_params=None, n_jobs=None, n_neighbors=5, p=2,
# weights='uniform')
# 테스트 데이터도 마찬가지로 정제한다. test_ target은 x
test_scaled = (test_input - mean) / std
kn.score(test_scaled, test_target)
1.0
print(kn.predict([new]))
[1.]
# 새로운 데이터 추가 확인하기
distances, indexes = kn.kneighbors([new])
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')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()