지도 학습 (supervised learning)
- 훈련하기 위한 데이터와 정답이 필요함 / 훈련데이터(데이터(입력)+타깃(정답)) 지도 학습은 정답이 있으니 알고리즘이 정답을 맞히는 것을 학습. 그러나 비지도 학습은 타깃 없이 입력 데이터만 사용하므로 무언가를 맞히는 것이 불가능. 대신 데이터를 잘 파악하거나 변형하는데 도움을 줄 수 있음.
훈련 세트와 테스트 세트
- 도미와 빙어의 데이터와 타깃을 주고 훈련한 다음, 같은 데이터로 테스트한다면 모두 맞히는 것이 당연함. 머신러닝 알고리즘의 성능을 제대로 평가하려면 훈련 데이터와 평가에 사용할 데이터가 각각 달라야함.
샘플링 편향 (sampling bias)
- 훈련 세트와 테스트 세트에 샘플이 골고루 섞여 있지 않으면 샘플링이 한쪽으로 치우쳤다는 의미로 샘플링 편향(sampling bias)라고 부른다. 따라서 데이터를 섞든지 아니면 골고루 샘플을 뽑을 수 있도록 훈련 세트와 테스트 세트를 만들어야 한다.
훈련 세트와 테스트 세트를 나누어 머신러닝 모델 구축하기
# 도미와 빙어의 데이터를 합쳐 하나의 파이썬 리스트로 준비하기
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차원 리스트 생성
fish_data = [[l,w] for l,w in zip(fish_length, fish_weight)]
fish_target = [1]*35 + [0]*14
# 이 데이터의 처음 35개를 훈련 세트로, 나머지 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_input)
# 정확도가 0이 나온다?
샘플링 편향을 방지하기 위해 넘파이 라이브러리 사용하기
# 넘파이는 파이썬의 대표적인 배열 라이브러리로, 고차원의 배열을 손쉽게 만들고 조작할 수 있는 간편한 도구들을 제공한다.
# array 함수를 사용해 파이썬 리스트를 넘파이 배열로 변환하기
input_arr = np.array(fish_data)
target_arr = np.array(fish_target)
print(input_arr.shape) #(샘플 수, 특성 수) 출력
# 무작위로 샘플 고르기 (이때, 입력과 타깃은 함께 선택되어야 한다)
# 아예 인덱스 번호를 섞은 다음, input_arr와 target_arr 에서 샘플을 선택하는 방식 사용
# 넘파이의 arange 함수 사용해서 0에서부터 48까지 1씩 증가하는 인덱스 만들기
np.random.seed(42)
index = np.arange(49)
np.random.shuffle(index) #0부터 48까지의 정수가 잘 섞였다
# 섞인 인덱스 배열에서 처음 35개를 전달하여 훈련 랜덤 샘플 만들기
train_input = input_arr[index[:35]]
train_target = target_arr[index[:35]]
# 마찬가지로 테스트 랜덤 샘플 만들기
test_input = input_arr[index[35:]]
test_target = target_arr[index[35:]]
두 번째 머신러닝 프로그램 만들기
kn.fit(train_input, train_target)
kn.score(test_input, test_target)
# 정확도 1.0 달성!
kn.predict(test_input)
test_target
# 두 코드의 결과가 완전히 일치하는 것을 확인할 수 있다
넘파이로 데이터 준비하기
# 이번에는 파이썬 함수 말고 넘파이를 이용해서 2차원 리스트를 생성한다(입력 데이터)
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))
#column_stack 함수는 전달받은 리스트를 일렬로 세운 다음 차례대로 나란히 연결, 파이썬 튜플로 전달함
# 넘파이를 이용해 정답 데이터도 생성한다(1 35개, 0 14개)
fish_target = np.concatenate((np.ones(35), np.zeros(14)))
사이킷런으로 데이터 분리하기
# 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, random_state=42)
# 기본적으로 25%를 테스트 세트로 떼어 낸다
print(train_input.shape, test_input.shape) #shape 속성: 크기
# 클래스 비율에 맞게 데이터를 분리하고 싶다면, stratify 속성에 타깃 데이터를 전달하면 된다
train_input, test_input, train_target, test_target = train_test_split(fish_data, fish_target, stratify = fish_target, random_state=42)
수상한 도미 한 마리
# 넘파이, 사이킷런으로 준비한 데이터로 1장과 똑같이 모델을 훈련, 평가한다
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier()
kn.fit(train_input, train_target)
kn.score(test_input, test_target) #정확도 1.0
# 도미 데이터를 넣고 결과를 확인해본다
kn.predict([[25,150]]) # 빙어로 예측. 왜 그럴까?
# 어떤 문제가 있는 것인지 훈련 데이터를 산점도로 나타내본다
import matplotlib.pyplot as plt
plt.scatter(train_input[:,0], train_input[:,1]) # 모든 행에서 각각 0과 1 선택
plt.scatter(25,150,marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show() # 산점도로 보았을 때도 역시 빙어보다는 도미에 더 가까워보임
# 이번에는 수치상으로 가까운 5개의 이웃을 직접 계산해보자, kneighbors 메소드 사용
distances, indexes = kn.neighbors([[25,150]])
# 그리고 이것을 포함해서 산점도를 다시 그려보자(이웃은 마름모로)
import matplotlib.pyplot as plt
plt.scatter(train_input[:,0], train_input[:,1]) # 모든 행에서 각각 0과 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() #빙어가 4개, 도미가 1개
# 4개의 샘플이 빙어인 것을 확인
train_input[indexes] #훈련데이터 중 이웃 인덱스 반환
train_target[indexes] #타깃데이터는 더 명확하다
# 직관적으로 보기에는 도미에 더 가까워보이는데, 왜 빙어일까?
스케일과 데이터 전처리
- 두 특성의 값이 놓인 범위가 매우 다른 것을 스케일이 다르다고도 표현함. 데이터를 표현하는 기준이 다르면 알고리즘이 올바르게 예측할 수 없음. (특히 알고리즘이 거리 기반일 경우) 따라서 특성값을 일정한 기준으로 맞춰주어야 하는데, 이를 데이터 전처리라고 함.