데이터 전처리

Jaeseok Han·2024년 4월 14일

머신러닝&딥러닝

목록 보기
4/22

데이터 전처리란?

머신러닝 알고리즘이 이해하기 쉽고, 효과적으로 작동할 수 있도록 데이터를 준비하는 과정을 말한다. 이 과정에서는 데이터를 정제하고, 스케일링하고, 변환하는 등의 작업을 수행한다.

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]

import numpy as np
fish_data = np.column_stack((fish_length, fish_weight))
fish_target = np.concatenate((np.ones(35), np.zeros(14)))

### 사이킷런으로 훈련 세트와 테스트 세트 나누기
from sklearn.model_selection import train_test_split
#train_input, test_input, train_target, test_target = train_test_split(fish_data, fish_target, random_state=42) # 1의 비율이 높음(3.3 : 1)
train_input, test_input, train_target, test_target = train_test_split(fish_data, fish_target, stratify=fish_target, random_state=42) # 비율이 적절함(2.25:1)

# 훈련 테스트 데이터 확인
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.]

train_test_split의 stratify매개변수에 타깃 데이터를 전달하면 클래스의 비율에 맞게 데이터를 나눠준다.

2. 특이한 데이터

## 수상한 도미 한 마리
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]]))

# 산점도 확인
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()


k-최근접 이웃은 주변의 샘플에서 다수인 클래스를 예측하는데, 육안으로 확인해 봤을때는 도미(1)에 가까우나 빙어(0)로 예측된다.

1) kneighbor() 메서드 사용

주어진 샘플중 가장 가까운 이웃을 찾아주는 메서드

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') # 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]]]
#[[1. 0. 0. 0. 0.]]
#[[ 92.00086956 130.48375378 130.73859415 138.32150953 138.39320793]]


1개의 도미(1)와 4개의 빙어(0)가 가장 근접한 이웃으로 표시된다.

3. 기준 맞추기

육안으로 확인해 봤을때 도미가 더 근접하게 보이지만, 빙어에 근접한 이웃이 더 많은 이유는 x축 범위와 y축 범위가 동일하지 않기 때문이다.
y 축의 범위에서 조금만 멀어져도 큰 값으로 작용하게 된다.

1) x, y축 범위 동일하게 수정

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') # D 마름모
plt.xlim((0, 1000))
plt.xlabel("length")
plt.ylabel("weight")
plt.show()


특성의 스케일이 다르기(두 값의 놓인 범위가 다름) 때문에 해당 산점도가 수직으로 올라가 있는 형태를 띄게된다.

2) 일정한 기준으로 맞춤

샘플 간의 거리에 영향을 많이 받으므로 특성값을 일정한 기준으로 맞춰야 한다. (모든 알고리즘이 거리를 기반으로 하지는 않음)
이러한 작업을 데이터 전처리라고한다.

3) 표준점수와 표준편차

가장 많이 사용하는 전처리는 표준점수로, 표준 점수는 각 특성값이 평균에서 표준편차의 몇 배만큼 떨어져 있는지를 나타낸다.
표준점수와 표준편차 자세히보기

넘파이에서는 표준점수와 표준편차를 구하는 함수를 모두 제공한다.

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]

# 표준점수
train_scaled = (train_input - mean) / std

train_scaled에서 수식은 train_input 의 모든 행에서 mean에 있는 두 평균값을 빼준 후 std에 있는 두 표준편차를 나눠준다. 이런 넘파이 기능을 브로드캐스팅이라고 한다.

4. 전처리 데이터로 모델 훈련

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()
# 기존 변환전 산점도와 거의 동일하게 보여짐
# x, y 범위가 -1.5~1.5로 바뀜

# k-최근접 이웃 모델 다시 훈련
kn.fit(train_scaled, train_target)

# 테스트 훈련도 동일한 비율로 설정필요
test_scaled = (test_input - mean) / std
kn.score(test_scaled, test_target)
# 1.0 예측 성공

print(kn.predict([new]))
# 도미(1)로 예측 성공

# k-최근접 이웃 알고리즘 올바르게 거리 측정했는지 확인
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') # 최근접 5개 표시
plt.xlabel("length")
plt.ylabel("weight")
plt.show()

전처리된 데이터를 사용하여 모델을 다시 훈련시켰고, 테스트 데이터에 대해 모델을 평가한 결과 모델이 올바르게 예측하는 것을 확인할 수 있다.

데이터 전처리를 통해 모델의 성능을 향상시키고, 더 정확한 예측을 할 수 있게 되었다.

0개의 댓글