728x90

그냥 근접 이웃 알고리즘으로 했을 때 잘못된 분류로 넘어가는 경우가 있다.

이럴 경우 어떻게 보정을 해줄 것인가?

 

# data 전처리
# Numpy로 data 준비

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

np.column_stack(([1,2,3],[4,5,6]))
fish_data = np.column_stack((fish_length, fish_weight))
print(fish_data[:5])

# 5개를 1로 채운다
# np.zeros()는 0으로 채운다
print(np.ones(5))

# np.column_stack()은 2차 배열 방식으로 연결
# np.concatenate()은 1차 배열 방식으로 연결
# 1이 35개, 0이 14개 있는 data 생성
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.]

 

## scikit-learn으로 training set, test set 나누기
# train_test_split() : training/test set를 fish_target에 맞게 나눠준다
# 기본적으로 25%를 test set으로 만든다
from sklearn.model_selection import train_test_split
train_input, test_input, train_target, test_target = train_test_split(
    fish_data, fish_target, stratify=fish_target, random_state=42)

# shape : size를 표시
print(train_input.shape, test_input.shape)
print(train_target.shape, test_target.shape)
print(test_target)

(36, 2) (13, 2)

(36,) (13,)

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

 

# 수상한 도미 한마리
# 이번 장의 핵심 주제 : 엉뚱한 결과로 들어가는 data를 어떻게 보정할 것인가?

# 기존 알고리즘 : 그냥 근접 이웃을 표시
from sklearn.neighbors import KNeighborsClassifier

kn = KNeighborsClassifier()
kn.fit(train_input, train_target)
kn.score(test_input, test_target)

print(kn.predict([[25, 150]]))

1.0

[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()

distances, indexes = kn.kneighbors([[25, 150]])


plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(25, 150, marker='^')
##인접 data를 마름모로 표시
plt.scatter(train_input[indexes,0], train_input[indexes,1], marker='D')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

print(train_input[indexes])

[[[ 25.4 242. ] [ 15. 19.9] [ 14.3 19.7] [ 13. 12.2] [ 12.2 12.2]]]

print(train_target[indexes])

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

print(distances)

[[ 92.00086956 130.48375378 130.73859415 138.32150953 138.39320793]]

 

기준을 맞춰라

분류의 기준을 조정

 

가장 가까운 샘플까지의 거리는 92, 그외는 130,138 정도 된다.

X축은 범위가 좁고, Y축은 범위가 넓어 서로 scale이 맞지 않다.

scale을 맞추면 아래와 같이 된다.(X축의 거의 의미가 없어짐)

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')
plt.xlim((0, 1000))
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

표준점수(standard score) : scale을 보정하는 가장 일반적인 방법

   - 평균에서 표준편차의 몇 배만큼 떨어져 있는지는 나타냄, 원점에서 몇 표준편차만큼 떨어져 있는지

   - 표준편차 : 분산의 제곱근

   - 분산 : data에서 평균을 뺀 값을 모두 제곱한 다음 평균을 냄

mean = np.mean(train_input, axis=0)
std = np.std(train_input, axis=0)
print(mean, std)
train_scaled = (train_input - mean) / std

[ 27.29722222 454.09722222] [ 9.98244253 323.29893931]

전처리 데이터로 모델 훈련하기

plt.scatter(train_scaled[:,0], train_scaled[:,1])
plt.scatter(25, 150, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

표준점수로 변환한 train 자료에 의심스러운 도미를 넣으면 혼자만 동떨어지게 된다.

다른 자료들은 표준점수인데 비해 새로운 자료는 data 자체이기 때문에 맞지 않다.

 

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()

동일 기준을 적용하면 처음 보았던 산점도와 비슷하게 나온다.

scale을 맞춘 자료를 이용하여 다시 근접이웃 알고리즘을 돌려보자.

 

kn.fit(train_scaled, train_target)
test_scaled = (test_input - mean) / std
kn.score(test_scaled, test_target)
print(kn.predict([new]))


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()

최종적으로 눈에 보이는 가까운 이웃들이 선택되었다.

 

728x90

+ Recent posts