혼자 공부하는 머신러닝 + 딥러닝

[혼공머신] 3. 데이터 전처리

Seon_ 2021. 12. 17. 17:13
3.fish_classification3
In [1]:
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]
In [2]:
import numpy as np
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. ]]

numpy의 column_stack은 전달받은 리스트를 일렬로 세운 후 차례대로 나란히 연결해준다.

이때 연결할 리스트는 파이썬 튜플로 전달한다.

In [6]:
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의 zeros, ones는 원하는 개수만큼 1과 0을 채워주는 함수이다.
  • numpy의 concatenate는 두 개의 행렬을 일차원적으로 이어붙이는 함수이다.
  • 데이터 크기가 큰 경우 파이썬 리스트를 사용하는 것은 비효율적이다.
  • 넘파이 배열은 핵심 부분이 C, C++과 같은 저수준 언어로 개발되어 빠르고, 데이터 과학 분야에 알맞게 최적화되어있다.
In [7]:
from sklearn.model_selection import train_test_split

사이킷런의 model_selection 모듈 내의 train_test_split 함수의 기능

  • 전달되는 리스트나 배열을 비율에 맞게 훈련 세트와 테스트 세트로 나눠준다.
  • 나누기 전에 셔플도 해준다 (!)
  • 셔플할 때 random seed 값도 설정할 수 있다.
In [8]:
train_input, test_input, train_target, test_target = train_test_split(fish_data, fish_target, random_state=42)

등호를 기준으로 왼쪽은 함수를 적용한 후 나눌 데이터가 들어갈 공간이고 오른쪽은 원본 데이터가 저장된 리스트나 배열을 뜻한다.

In [9]:
print(train_input.shape, test_input.shape)
(36, 2) (13, 2)

train_test_split 함수가 훈련 세트와 테스트 세트를 각각 36개, 13개로 설정했음을 알 수 있다.

In [10]:
print(test_target)
[1. 0. 0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1.]

테스트 세트의 결과값이 잘 shuffle된 것을 볼 수 있다.

  • 13개의 테스트 세트 중에 10개가 도미(1)이고, 3개가 빙어(0)이다.
  • 잘 섞인 것 같지만 빙어의 비율이 조금 모자란다. 원래 도미와 빙어의 개수가 35개와 14개이므로 두 생선의 비율은 2.5:1이다.
  • 하지만 이 테스트 세트의 도미와 빙어의 비율을 3.3:1이다. 샘플링 편향이 일어난 셈이다.
    • 이를 해결하기 위해서는 stratify 매개변수에 타깃 데이터를 전달하면 클래스 비율에 맞게 데이터를 나눠준다.
In [11]:
train_input, test_input, train_target, test_target = train_test_split(fish_data, fish_target, stratify=fish_target, random_state=42)
In [12]:
print(test_target)
[0. 0. 1. 0. 1. 0. 1. 1. 1. 1. 1. 1. 1.]
  • 빙어가 하나 늘어 테스트 세트의 비율이 2.25:1이 된 것을 확인할 수 있다.
In [13]:
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier()
kn.fit(train_input, train_target)
kn.score(test_input, test_target)
Out[13]:
1.0
In [15]:
print(kn.predict([[25,150]]))
[0.]
In [16]:
import matplotlib.pyplot as plt
plt.scatter(train_input[:,0],train_input[:,1])
plt.scatter(25, 150, marker='^') #marker 매개변수는 모양을 지정한다.
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
In [18]:
distances, indexes= kn.kneighbors([[25,150]])
In [20]:
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') # marker D는 마름모
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

주어진 세모 점으로부터 가까운 k개의 neighbor들을 표시해본 결과

도미보다는 빙어쪽에 가깝기 때문에 빙어로 판단하는 오류가 발생한 것이다.

In [21]:
print(distances)
[[ 92.00086956 130.48375378 130.73859415 138.32150953 138.39320793]]

도미쪽 데이터와 훨씬 가까워 보이는데왜 빙어쪽 데이터와 차이가 크지 않을까?

  • x축 데이터 간격과 y축 데이터 간격이 다르기 때문 !
In [23]:
mean = np.mean(train_input, axis=0) #axis=0은 행을 따라 열의 통계값을 계산하겠다는 뜻
std = np.std(train_input, axis=0)
print(mean, std)
[ 27.29722222 454.09722222] [  9.98244253 323.29893931]
In [24]:
train_scaled = (train_input - mean) / std
In [25]:
plt.scatter(train_scaled[:,0],train_scaled[:,1])
plt.scatter(25, 150, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
  • 훈련 세트도 scaling 해야 함을 알 수 있다.
In [26]:
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()
  • 이제 x축과 y축의 범위가 동일해졌음을 알 수 있다.
In [28]:
kn.fit(train_scaled, train_target)
Out[28]:
KNeighborsClassifier()
In [29]:
test_scaled = (test_input - mean)/std

테스트 세트의 스케일 역시 훈련 세트의 통계값을 사용하여 처리해야 한다!

In [30]:
kn.score(test_scaled, test_target)
Out[30]:
1.0
In [31]:
print(kn.predict([new]))
[1.]

이제서야 새로운 데이터가 도미임을 알맞게 찾아내고 있다.

In [1]:
from IPython.core.display import display, HTML

display(HTML("<style>.container { width:90% !important; }</style>"))