02-2. 데이터 전처리
넘파이로 데이터 준비하기
코드 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]
코드 2 - 넘파이 임포트 하기
import numpy as np
코드 3 - 넘파이 colmn_stack() 함수 사용
np.column_stack(([1,2,3], [4,5,6])) // array([[1, 4], [2, 5], [3, 6]])
- 넘파이 colmn_stack() 함수는 전달받은 리스트를 일렬로 세운 다음 차례대로 연결함
- 연결할 리스트는 튜플로 전달
- [1,2,3] 과 [4,5,6] 두 리스트를 일렬로 세운 다음 나란히 옆으로 붙음
- 만들어진 배열은 (3,2) 크기의 배열임
중요!!
튜플(tuple) 이란?
파이썬의 리스트와 비슷하며, 리스트처럼 원소에 순서가 있지만 한 번 만들어진 튜플은 수정이 안됨!!
튜플을 사용하면 함수로 전달한 값이 바뀌지 않는다는 것을 믿을 수 있기에 매개변수 값으로 많이 사용함
코드 4 - fish_length와 fish_weight를 합침
fish_data = np.column_stack((fish_length, fish_weight))
코드 5 - 두 리스트가 잘 연결되었는지 5개의 데이터를 확인
print(fish_data[:5])
# [[ 25.4 242. ]
[ 26.3 290. ]
[ 26.5 340. ]
[ 29. 363. ]
[ 29. 430. ]]
- 넘파이 배열을 출력하면 리스트처럼 한 줄로 길게 출력되지 않고 행과 열을 맞추어 가지런히 정리된 모습이 보임
- 결과만 봐도 5개의 행을 출력했고 행마다 2개의 열(생선의 길이와 무게)이 있다는 것을 알수 있음
코드 6 - np.ones()와 np.zeros() 함수를 이용한 타깃 데이터
print(np.ones(5)) # [1. 1. 1. 1. 1.]
- 이 두 함수를 사용해 1이 35개인 배열과 0이 14개인 배열을 만들 수 있음
- 그다음 두 배열을 그대로 사용하면 됨
코드 7 - np.concatenate() 함수를 사용한 타깃데이터
fish_target = np.hstack((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.]
사이킷런으로 훈련 세트와 데이터 세트 나누기
- 사이킷런은 머신러닝 모델을 위한 알고리즘 뿐만 아니라 다양한 유틸리티 도구도 제공함
- 대표적인 도구가 train_test_split() 함수임
- 이 함수는 전달되는 리스트나 배열을 비율에 맞게 훈련 세트와 데이터 세트로 나누어 줌
코드 8 - 사이킷런의 model_selection import 하기
from sklearn.model_selection import train_test_split
- 사용버은 나누고 싶은 리스트나 배열을 원하는 만큼 전달하면 됨
- 여기서는 fish_data와 fish_target으로 나눔
- train_test_split() 함수는 친절하게도 자체적으로 랜덤시드를 지정할 수 있는 random_state 매개변수가 있음
코드 9 - 훈련세트와 데이터 세트로 나누기
train_input, test_input, train_target, test_target = train_test_split(fish_data, fish_target, random_state=42)
- fish_data와 fish_target 2개의 배열을 전달했으므로 2개씩 나뉘어 총 4개의 배열이 반환됨
- 차례대로 처음 2개는 입력 데이터(train_input, test_input)
- 나머지는 타깃 데이터(train_target, test_target)임
- 랜덤 시드(random_state)는 42로 저장함
코드 10 - 넘파이 배열의 shape 속성으로 입력 데이터 출력하기
print(train_input.shape, test_input.shape) # (36, 2) (13, 2)
print(train_target.shape, test_target.shape) # (36,) (13,)
- 훈련 데이터와 입력 데이터는 각각 36개와 13개로 나눔
- 입력 데이터는 2개의 열이 있는 2차원 배열이고 타깃 데이터는 1차원 배열임
코드 11 - 테스트 데이터 출력하기
print(test_target) # [1. 0. 0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
- 13개의 테스트 세트 중에 10개가 도미(1)이고, 3개가 방어(0) 임
- 잘 섞인것 같지만 빙어의 비율이 조금 모자람
- 이 테스트 세트의 도미와 빙어의 비율은 3.3:1임
- 이렇게 되면 샘플링 편향도 나타남
코드 12 - stratify 매개변수에 타깃 데이터 전달하기
train_input, test_input, train_target, test_target = train_test_split(fish_data, fish_target, stratify=fish_target, random_state=42)
print(test_target) # [0. 0. 1. 0. 1. 0. 1. 1. 1. 1. 1. 1. 1.]
- 빙어가 하나 더 늘어 테스트 세트 비율이 2.25:1이 됨
수상한 도미 한마리
코드 13 - k- 최근접 이웃 알고리즘 훈련
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier()
kn.fit(train_input, train_target)
kn.score(test_input, test_target)
# 1.0
- 완벽한 결과가 나왔음
- 테스트 세트의 도미와 빙어를 올바르게 분류함
코드 14 - 예측 데이터 확인하기
print(kn.predict([[25, 150]])) # [0.]
- 이 데이터가 올바른지 의심됨
- 그래서 산점도를 이용한 데이터를 그려봄
코드 14 - 산점도를 이용한 데이터 그리기
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()

- 새로운 샘플은 maker 매개변수를 ^로 지정하여 삼각형으로 나타냄
- 이렇게 하면 구분하기 더 쉬워짐
- k- 최근접 이웃은 주변의 샘플 중에서 다수의 클래스를 예측으로 사용함
- KNeighborsClassifier 클래스는 주어진 샘플에서 가장 가까운 이웃을 찾아주는 Kneighbors() 매서드 제공
- 이 메서드는 이웃까지의 거리와 이웃 샘플의 인덱스를 반환함
- KNeighborsClassifier 클래스의 이웃 개수인 n_neighbors의 기본값은 5이므로 5개의 이웃이 반환됨
distances, indexes = kn.kneighbors([[25, 150]])
코드 15 - indexes 배열을 사용해 훈련 데이터 중에서 이웃 샘플 그리기
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.xlabel('length')
plt.ylabel('weight')
plt.show()

- maker='D'로 지정하면 산점도를 마름모로 그림
- 삼각형 샘플에 가까운 5개의 샘플이 초록 다이아몬드에 표시됨
- 예측 결과와 마찬가지로 가장 가까운 아웃에 도미가 하나밖에 포함 됨
- 나머지 4개의 샘플은 모두 빙어
코드 16 - 샘플 빙어 확인하기(1)
print(train_input[indexes])
# [[[ 25.4 242. ]
[ 15. 19.9]
[ 14.3 19.7]
[ 13. 12.2]
[ 12.2 12.2]]]
코드 17 - 샘플 빙어 확인하기(2)
print(train_target[indexes]) # [[1. 0. 0. 0. 0.]]
- 길이가 25cm, 무게가 150g인 생선에 가장 가까운 이웃에는 빙어가 압도적으로 많음
- 따라서 이 샘플의 클래스를 빙어로 예측하는 것은 무리가 아님
코드 18 - 샘플 빙어 확인하기(3)
print(distances)
# [[ 92.00086956 130.48375378 130.73859415 138.32150953 138.39320793]]
기준을 맞춰라
코드 19 - 확률에 맞는 기준 맞추기
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()

- 산점도가 거의 일직선으로 나타내는 x축과 y축의 범위를 동일하게 맞춤
- 모든 데이터가 수직으로 늘어선 형태가 됨
- 이런 데이터는 생선의 길이(x축)은 가장 가까운 이웃을 찾는 데 크게 영향을 미치지 못함
- 오로지 생선의 무게(y축)만 고려 대상이 됨
- 두 특성(길이와 무게)의 값이 놓인 범위가 매우 다름
- 두 특성의 스케일이 다르다고 말함
데이터 전처리
알고리즘이 샘플 간의 거리에 영향을 받은데, 제대로 사용하려면 특성값을 일정한 기준으로 맞춰야 함
표준점수란?
각 특성값이 0에서 표준편차의 몇 배만큼 떨어져 있는지를 나타냄
이를 통해서 실제 크기와 상관없이 동일한 조건으로 비교가능
코드 20 - 표준점수와 표준편차 계산(1)
mean = np.mean(train_input, axis=0) // 평균을 계산
std = np.std(train_input, axis=0) // 표준편차 계산
- 특성마다 값의 스케일이 다르므로 평균과 표준편차는 각 특성별로 계산해야 함
- 이를 위해 axis = 0으로 지정함
코드 21 - 표준점수와 표준편차 계산(2)
print(mean, std)
# [ 27.29722222 454.09722222] [ 9.98244253 323.29893931]
train_scaled = (train_input - mean) / std
- 각 특성마다 평균과 표준편차가 구해짐
- 넘파이는 똑똑하게도 train_input에 있는 모든 행에서 mean에 있는 평균값을 빼줌
- 그다음 std에 있는 두 표준편차를 다시 모든 행에 적용함
- 이런 넘파이 기능을 브로드캐스팅 이라고 함
전처리 데이터로 모델 훈련하기
코드 22 - 앞써 데이터의 샘플을 산점도로 그리기
plt.scatter(train_scaled[:,0], train_scaled[:,1])
plt.scatter(25, 150, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

- 이 값에서 틀린 부분이 있음
- 오른쪽 맨 꼭대기에 수상한 샘플 하나만 덩그러니 떨어져 있음
- 이런 이유는 훈련 세트를 mean(평균) - std(표준편차)로 나누어 주었기 때문
- 샘플 [20, 150]을 동일한 비율로 변환하지 않았기에 이런 현상이 발생함
코드 23 - 훈련세트의 mean, std를 이용해서 산점도 그리기
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()

- 이그래프는 앞서 표준편차로 변환하기 전의 산점도와 거의 비슷함
- 크게 달라진 점은 축의 범위가 -1.5 ~ 1.5 사이로 바뀜
- 훈련 데이터의 두 특성이 비슷한 범위를 차지하고 있음
코드 24 - 위의 데이터셋으로 k-최근점 이웃 모델 훈련
kn.fit(train_scaled, train_target)
- 훈련을 마치고 테스트 세트로 평가할 때는 주의해야 함
- 테스트 세트도 훈련 세트의 평균과 표준 편차를 변환해야 함
- 그렇지 않으면 데이터의 스케일이 같아지지 않으므로 훈련한 모델이 쓸모없게 됨
코드 25 - 테스트 세트의 스케일 변환
test_scaled = (test_input - mean) / std
코드 26 - 모델 평가
kn.score(test_scaled, test_target) # 1.0
- 모든 테스트 세트의 샘플을 완벽하게 분류해냄
코드 27 - 최종 평가(1)
print(kn.predict([new])) # [1.]
- 도미(1)로 예측하게 됨
- 확실히 길이가 25cm이고, 무게가 150인 생선은 도미임
코드 28 - 최종 평가(2)
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()

- 샘플에서 가장 가까운 샘플은 모두 도미임
- 따라서 이 수상한 샘플을 도미로 예측하는 것은 당연한것
- 특성값의 스케일에 민감하지 않고 안정적인 예측을 할 수 있는 모델임
코드 29 - 총 정리
"""# 데이터 전처리"""
"""## 넘파이로 데이터 준비하기"""
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])
print(np.ones(5))
fish_target = np.hstack((np.ones(35), np.zeros(14)))
print(fish_target)
"""## 사이킷런으로 훈련 세트와 테스트 세트 나누기"""
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)
print(train_input.shape, test_input.shape)
print(train_target.shape, test_target.shape)
print(test_target)
train_input, test_input, train_target, test_target = train_test_split(fish_data, fish_target, stratify=fish_target, random_state=42)
print(test_target)
"""## 수상한 도미 한 마리"""
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier()
kn.fit(train_input, train_target)
kn.score(test_input, test_target)
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()
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')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
print(train_input[indexes])
print(train_target[indexes])
print(distances)
"""## 기준을 맞춰라"""
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()
mean = np.mean(train_input, axis=0)
std = np.std(train_input, axis=0)
print(mean, std)
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)
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()
'공부 기록일지' 카테고리의 다른 글
유성이의 공부일지(18-1) - 혼자공부하는 머신러닝 + 딥러닝 3장 (0) | 2024.10.11 |
---|---|
유성이의 공부일지(17-1) - 혼자공부하는 머신러닝 + 딥러닝 2장 (1) | 2024.09.07 |
유성이의 공부일지(16) - 혼자공부하는 머신러닝 + 딥러닝 1장 (3) | 2024.08.27 |
유성이의 공부일지(15) - 혼자공부하는 컴퓨터 구조 + 운영체제 15장 (0) | 2024.07.16 |
유성이의 공부일지(14) - 혼자공부하는 컴퓨터 구조 + 운영체제 14장 (0) | 2024.07.15 |