오늘의 다짐: 큰일날 뻔했다 .. 스터디 1주차 진도범위를 착각했다. 책으로 먼저 정리하고, 인프런 강의를 통해 복습하는 형식으로 하자!
*** 해당 글은 [한빛미디어 출판 / 박해선 지음] "혼자 공부하는 머신러닝+딥러닝" 책과, 인프런에서 무료로 제공하고 있는 혼자 공부하는 머신러닝+딥러닝 강의를 기반으로 작성함 ***
02 데이터 다루기
[ 02-1 훈련 세트와 테스트와 세트 ]
지도 학습과 비지도 학습
머신러닝 알고리즘은 크게 [지도학습, 비지도학습]으로 나눌 수 있으며, 이외에 강화 학습(reinforement learning)이라는 종류도 존재한다.
이는 타깃(정답)이 아니라, 알고리즘이 행동한 결과로 얻은 '보상'을 사용하여 학습한다_책에서는 다루지 않음*
지도학습
: 지도 학습 알고리즘은 훈련하기 위한 '데이터'와 '정답'이 필요하다, 정답을 맞히는 것을 학습할 수 있다
+ 1장에서 다뤘던 k-최근접 이웃 알고리즘은, 지도학습에 해당한다
비지도학습
: 정답(타깃) 없이 입력 데이터만 사용하여, 데이터를 잘 파악하거나 변형하는데 도움을 준다
🔎용어정리
데이터 > 입력 input
정답 > 타깃 target
데이터 + 정답 > 훈련 데이터 training data
입력으로 사용된 특정 성질 (ex.물고기의 길이와 무게) > 특성 feature
훈련 세트와 테스트 세트
1장에서 도미인지 아닌지 판단하기 위해 훈련했던 것의 문제점은,
훈련시켰던 것과 적절하게 훈련했는지 확인하는 데이터가 동일했다는 것이다.
마치, 숙제로 내준 '연습문제'와 '중간/기말고사'의 문제가 동일해서 100점을 당연하게 맞는 상황처럼 ...
➡️ 성능을 제대로 평가하기 위해서는 '훈련 데이터'와 '평가'에 사용할 데이터가 다르면 되는 것!
이를 위해 [ 또 다른 데이터 준비 ] or [ 이미 준비된 데이터 중 일부를 떼어 활용하기 ]의 방식이 있는데
후자의 경우가 더욱 빈번하다
훈련 세트 train set
: 훈련에 사용하는 데이터
+ 보통은 훈련세트가 클수록 좋으며 테스트 세트를 제외한 모든 데이터를 사용한다
테스트 세트 test set
: 평가에 사용하는 데이터
+ 전체 데이터에서 20~30%를 사용하는 경우가 많으며, 전체 데이터가 매우 클 경우 1%만 덜어내도 충분할 수 있다
샘플 sample
: 하나의 데이터
미니 실습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]
fish_data = [[l,w] for l,w in zip(fish_length, fish_weight)]
fish_target = [1]*35 + [0]*14
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier()
#슬라이싱해서 샘플 선택해보기
train_input = fish_data[:35]
train_target = fish_target[:35]
test_input = fish_data[35:]
test_target = fish_target[35:]
kn.fit(train_input, train_target)
kn.score(test_input, test_target)
1장에서 배웠던 것처럼, 2차원 리스트 형태로 data를 저장해준 후,
슬라이싱 문법을 활용하여 훈련 세트와 테스트 세트를 나누어 활용하였다
➡️ 그런데 kn.score()의 결과가 0.0이 나왔다 !!!!!!!!!!
샘플링 편향
샘플링 편향 sampling bias
: 훈련 세트와 테스트 세트에 샘플이 골고루 섞여 있지 않아 한쪽으로 치우쳐 있는 것
➡️ 이러한 경우 지도 학습 모델을 제대로 생성할 수 없다, 위의 결과가 0.0이 나온 이유도 마찬가지다
넘파이
넘파이 numpy
: 파이썬의 대표적인 배열array 라이브러리며, 고차원 리스트를 편리하게 조작할 수 있도록 도와준다
( 코랩에는 사이킷런뿐만 아니라, 맷플롯립, 넘파이 등의 데이터과학 라이브러리가 포함되어 있다 )
import numpy as np
input_arr = np.array(fish_data)
target_arr = np.array(fish_target)
print(input_arr.shape)
shape()는 샘플 개수와 특성 개수를 보여주는데,
위의 코드를 실행할 경우 (49,2)의 값을 확인할 수 있다! (49개 샘플과 2개의 특성을 설정했으므로)
np.random.seed(42)
index = np.arange(49)
np.random.shuffle(index)
#print(index)
train_input = input_arr[index[:35]]
train_target = target_arr[index[:35]]
#print(input_arr[13], train_input[0])
test_input = input_arr[index[35:]]
test_target = target_arr[index[35:]]
🔎[코드정보] - random seed
무작위 결과를 생성해주는 함수들은 실행할 때마다 다른 결과를 만들기 때문에, 일정한 결과를 얻기 위해 초기에 시드를 지정할 수 있다
🔎[코드정보] - arange()
인수로 정수 N을 전달할 경우, 0~(N-1)까지 1씩 증가하는 배열을 생성한다
🔎[코드정보] - random 패키지의 shuffle()
인수로 전달한 배열을 무작위로 섞어준다
🔎[코드정보] - 배열 인덱싱
1개의 인덱스가 아닌, 여러 인덱스로 한 번에 여러 개의 원소를 선택할 수 있다
[총 해설]
49개의 원소를 가진 Index 배열을 만들어줘서 0~48까지의 값을 갖게 해준 뒤에
shuffle함수를 통해 무작위로 섞어주고
이 무작위로 섞어준 인덱스를 기준으로 input_arr와 target_arr를 슬라이싱하여
훈련 세트와 타겟 세트를 만들어주었다.
이러한 방식은 '짝을 맞추기 위함'인데 각각에 input에 맞는 target값이 제대로 짝 지어진 상태로 섞여야 하기 때문이다!
(아래의 그림처럼 이해했다)
1강에서 배웠던 플롯을 통해서, 훈련 세트와 테스트 세트가 잘 섞여있는지 시각적으로 확인해보면, 아래와 같이 잘 나온당 ><
import matplotlib.pyplot as plt
plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(test_input[:,0], test_input[:,1])
plt.xlabel('length')
plt.ylabel('weight')
plt.show
두 번째 머신러닝 프로그램
위에서 섞어서 만든 훈련 세트와 테스트 세트로 k-최근접 이웃 모델을 훈련시켜보기!
kn.fit(train_input, train_target)
kn.score(test_input, test_target)
kn.predict(test_input)
test_target
score값이 1.0의 정확도로 나온다 (성공)
🔎[추가개념] - 반환값에 대하여
사실 predict메서드가 반환하는 값은 '파이썬 리스트'가 아닌 '넘파이 배열'에 해당한다
+ 사이킷런 모델의 모든 입력과 출력은 넘파이 배열이다
[ 02-2 데이터 전처리 ]
넘파이로 데이터 준비하기
데이터를 세련된 방식으로 만들어보아용
1절에서 데이터를 리스트를 순회하며 2차원 리스트를 생성했던 것을
조금 더 세련된 방식으로 바꾸기!
import numpy as np
np.column_stack(([1,2,3],[4,5,6]))
🔎[코드정보] - column_stack
전달받은 리스트를 일렬로 세운 후
차례대로 나란히 연결!
위의 코드결과로 array([[1,4], [2,5], [3,6]])이 생성된다
위와 같은 방식으로, fish_length 와 fish_weight 데이터를 합쳐보면,
fish_data = np.column_stack((fish_length, fish_weight))
print(fish_data[:5])
제대로 나오고 있다!
🔎[참고] - 왜 튜플tuple로 넘겨주는가?
파이썬에서 튜플tuple이란, 리스트 example = [1,2,3]과 다르게, example =(1,2,3) 요소에 접근하여 값을 수정할 수 없다. 따라서 함수로 전달한 값이 바뀌지 않음을 믿을 수 있으므로 매개변수 값으로 많이 사용된다
타깃 데이터도 세련된 방식으로 만들어보아용
fish_target = np.concatenate((np.ones(35), np.zeros(14)))
print(fish_target)
🔎[코드정보] - concatenate()
리스트를 합쳐주는 역할을 한다. 예를 들어 [0,0,0]과 [1,2,3]이 인수로 들어왔을 때
[0,0,0,1,2,3]으로 연결해준다
🔎[코드정보] - np.ones() np.zeros()
전달한 숫자에 해당하는 개수로 1이나 0을 생성해준다. 예를 들어 np.ones(5)의 경우 출력해보면
[1,1,1,1,1]을 확인할 수 수 있다
사이킷런으로 훈련 세트와 테스트 세트 나누기
앞에서는 넘파이 배열의 '인덱스'를 직접 섞어서 나누는 방식을 활용했지만, 사이킷런 model_selection 모듈의 train_test_split()함수를 사용하면 전달되는 리스트나 배열을 비율에 맞게 알아서 나누어 준다. 사용법을 살펴보자
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)
print(test_target)
🔎[코드정리]
헷갈리지 않게 정리해보면
train_input 훈련할 때 입력해줄 데이터
train_target 훈련할 때 넣어준 데이터에 해당하는 '정답'들
test_input 테스트할 때 입력해줄 데이터
test_target 테스트할 때 넣어준 데이터에 해당하는 '정답'들
🔎[코드정보] - random_state란?
위에서 seed()로 같은 정수에 대해서는 일정한 무작위 값이 생성되도록 만들었던 것과 유사한 역할을 한다
🔎[코드정보] - stratify란?
무작위로 데이터를 나누었을 때 샘플이 골고루 섞이지 않는 문제가 발생할 수 있다 (샘플 개수가 작을수록 위험성이 더 크다)
stratify 매개변수에 타깃데이터를 전달하면 클래스 비율에 맞게 데이터를 자동으로 나눠준다
수상한 도미 한 마리
length 25와 weight 150의 물고기를 판단하면 어떻게 될까? 산점도를 확인하여 직관적으로 생각해보면, '도미'인 걸 알겠지?라고 생각하지만, 막상 predict로 확인해보면 0으로 판단해버린다.
plt.scatter(train_input[:, 0], train_input[:,1])
plt.scatter(25, 150, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
marker값을 통해 수상한 도미만 모양을 달리하여 표시해보니, 위의 산점도를 얻었다. 분명히 오른쪽 윗부분의 도미 데이터와 가까워 보이는데, 왼쪽 아래의 빙어 데이터에 가깝다고 판단한 것일까?
➡️ KNeighborsClassifier의 기본 이웃 개수는 5라는 걸 기억하자!
➡️ kneighbors()메서드를 통해, 주어진 샘플에서 '가장 가까운 이웃과의 거리'와 '이웃 샘플의 인덱스'를 반환하여 확인해보자!
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')
kneighbors로 확인한 가까운 이웃들은 marker값을 따로 지정하여 다이아몬드로 표시하였다.
이렇게 보면, 수상한 생선의 가까운 이웃은 "빙어 4개, 도미 1개"로 구성되어있으므로, 빙어라고 판단해버린 것이다 !
기준을 맞춰라
가까운 이웃의 '거리'도 고려했을텐데 어떻게 된 것일까?
그 이유는 x축에서의 한 칸 크기는 5, y축에서의 한 칸 크기는 200을 차지하므로 각 축에서 다른 기준으로 영향을 미치고 있었기 때문이다
(좋은 비유: 방의 넓이를 재는데, 세로는 cm 가로는 inch로 쟀을 경우, 정사각형인 방도 직사강형처럼 측정될 것)
➡️ 이를 동일하게 맞추기 위해서는 xlim()과 ylim()함수를 통해 범위를 지정할 수 있다. plt.xlim((0,1000)을 통해 x축도 1000까지 범위를 설정해주면 다음과 같은 산점도를 얻을 수 있다
이렇게 확인해보니, 생선의 종류를 판단하는 데 있어서 length의 영향은 거의 없고 오로지 weight가 고려대상이 된다.
데이터 전처리
: 특성값을 일정한 기준으로 맞춰주는 작업
+ 대표적으로 쓰이느 전처리 방법 중 하나는 '표준점수 standard score(z점수)'이며 각 값들에서 평균을 빼고 표준편차로 나누어 계산할 수 있다
다음의 코드로 작업해주자
mean = np.mean(train_input, axis=0)
std = np.std(train_input, axis=0)
train_scaled = (train_input - mean) / std
넘파이에서는 위와 같이 평균과 표준편차를 구하는 함수를 모두 제공해준다 np.mean과 np.std
(axis=0을 지정한 이유는, 평균과 표준편차를 '각 특성별'로 계산해야 하므로, 행을 따라 각 열의 통계 값을 계산하도록 만든 것이다)
또한, 넘파이 배열 사이에서 연산을 하는 경우, 모든 행에 접근하여 연산을 저절로 해주는데 책에 정말 쉽게 그림으로 표현되어있다.
이를 브로드캐스팅이라고 한다.
전처리 데이터로 모델 훈련하기
위에서 표준점수로 작업해준 데이터를 가지고 산점도를 확인해보면, 다음과 같이 수상한 도미가 동떨어져 있다
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()
다시 적잘한 산점도를 얻었다 ><
위의 산점도를 바탕으로 다시 '수상한 생선'의 가까운 이웃을 확인해보면다면,
new = ([25, 150] - mean)/ std
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')
위에서 했던 것과 다르게, 거리를 알맞게 고려하여 가까운 이웃들이 초록색 다이아몬드로 제대로 표시되고 있다.
'스터디&프로젝트 > 데이터사이언스' 카테고리의 다른 글
[혼자 공부하는 머신러닝+딥러닝] 01나의 첫 머신러닝 (4) | 2023.03.22 |
---|