1. 훈련 세트와 테스트 세트
- 지도 학습 알고리즘은 훈련하기 위한 데이터와 정답이 필요
- 지도 학습에서는 데이터와 정답을 입력(input)과 타깃(target)이라고 하고, 이 둘을 합쳐 훈련 데이터(training data)라고 부름
- 머신러닝 알고리즘의 성능을 제대로 평가하려면 훈련 데이터와 평가에 사용할 데이터가 각각 달라야 함
- 테스트 세트(test set) : 평가에 사용하는 데이터
- 훈련 세트(train set) : 훈련에 사용되는 데이터
- 생선 길이와 무게를 위한 리스트 준비
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개를 테스트 세트로 사용
- 사이킷런의 KNeighborsClassifier 클래스를 임포트하고 모델 객체를 만듬
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier()
- 슬라이싱 연산으로 인덱스 0~34까지 처음 35개 샘플을 훈련 세트로 선택했고, 인덱스 35~48까지 나머지 14개 샘플을 테스트 세트로 선택
#훈련 세트로 입력값 중 0부터 34번째 인덱스까지 사용
train_input = fish_data[:35]
#훈련 세트로 입력값 중 0부터 34번째 인덱스까지 사용
train_target = fish_target[:35]
#테스트 세트로 입력값 중 35번째부터 마지막 인덱스까지 사용
test_input = fish_data[35:]
#테스트 세트로 입력값 중 35번째부터 마지막 인덱스까지 사용
test_target = fish_target[35:]
- 훈련 세트로 fit() 메서드를 호출해 모델을 훈련하고, 테스트 세트로 score() 메서드를 호출해 평가
kn.fit(train_input, train_target)
kn.score(test_input, test_target)
1) 샘플링 편향
- 훈련하는 데이터와 테스트하는 데이터에는 도미와 빙어가 골고루 섞여 있어야 함
- 훈련 세트와 테스트 세트에 샘플이 골고루 섞여 있지 않으면 샘플링이 한쪽으로 치우졌다는 의미로 샘플링 편향(sampling bias)이라고 부름
2) 넘파이
- 파이썬의 대표적인 배열(array) 라이브러리
- 파이썬의 리스트로는 2차원 리스트를 표현할 수 있지만 고차원 리스트를 표현하려면 매우 번거로움
- 생선 데이터를 2차원 넘파이 배열로 변환
- 넘파이 array() 함수에 파이썬 리스트를 전달
import numpy as np
input_arr = np.array(fish_data)
target_arr = np.array(fish_target)
print(input_arr)
- 넘파이는 배열의 차원을 구분하기 쉽도록 행과 열을 가지런히 출력
- 2개의 열(특성), 49개의 행(샘플)
- 넘파이 배열 객체는 배열의 크기를 알려주는 shape 속성을 제공
print(input_arr.shape) #이 명령을 사용하면 (샘플 수, 특성 수 )출력
- 이 배열에서 랜덤하게 샘플을 선택해 훈련 세트와 테스트 세트로 만들기
- 무작위로 샘플을 고르는 방법을 사용
- 주의점은 input_arr와 target_arr에서 같은 위치는 함께 선택되어야 함
- 인덱스를 섞은 다음 input_arr와 target_arr에서 샘플을 선택하면 무작위로 훈련 세트를 나누는 셈
- 넘파이 arange() 함수를 사용하면 0에서부터 48까지 1씩 증가하는 인덱스를 간단히 만들 수 있음
- 넘파이 arange() 함수에 정수 N을 전달하면 0에서부터 N-1까지 1씩 증가하는 배열을 만듬
- shuffle() 함수는 주어진 배열을 무작위로 섞음
np.random.seed(42)
index = np.arange(49)
np.random.shuffle(index)
print(index)
- 넘파이는 슬라이싱 외에 배열 인덱싱(array indexing)이라는 기능 제공
- 배열 인덱싱은 1개의 인덱스가 아닌 여러 개의 인덱스로 한 번에 여러 개의 원소를 선택할 수 있음
print(input_arr[[1,3]])
- 비슷한 방식으로 리스트 대신 넘파이 배열을 인덱스로 전달 가능
- index 배열의 처음 35개를 input_arr와 target_arr에 전달하여 랜덤하게 35개의 샘플을 훈련 세트로 만들기
train_input = input_arr[index[:35]]
train_target = target_arr[index[:35]]
- 나머지 14개를 테스트 세트로 만듬
test_input = input_arr[index[35:]]
test_target = target_arr[index[35:]]
- 훈련 세트와 테스트 세트에 도미와 빙어가 잘 섞여 있는지 산점도로 그리기
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()
- 파란색이 훈련 세트이고 주황색이 테스트 세트
3) 두 번째 머신러닝 프로그램
- 훈련 세트와 테스트 세트로 K-최근접 이웃 모델 훈련
kn.fit(train_input, train_target)
kn.score(test_input, test_target)
- predict() 메서드로 테스트 세트의 예측 결과와 실제 타깃을 확인
kn.predict(test_input)
- 테스트 세트에 대한 예측 결과가 정답과 일치
test_target
2. 데이터 전처리
1) 넘파이로 데이터 준비
- 넘파이의 column_stack() 함수는 전달받은 리스트를 일려로 세운 다음 차례대로 나란히 연결
- 연결할 리스트는 파이썬 튜플(tuple)로 전달
- fish_length와 fish_weight 합치기
import numpy as np
fish_data = np.column_stack((fish_length, fish_weight))
print(fish_data[:5])
- 타깃 데이터 만들기
- np.ones()와 np.zeros() 함수는 각각 원하는 개수의 1과 0을 채운 배열을 만들어 줌
- 이 두 함수를 사용해 1이 35개인 배열과 0이 14개인 배열을 만들 수 있음
- 그다음 두 배열을 그대로 연결하면 됨
- 첫 번째 차원을 따라 배열을 연결하는 np.concatenate() 함수 사용
- 연결할 리스트나 배열을 튜플로 전달해야 함
fish_target = np.concatenate((np.ones(35), np.zeros(14)))
print(fish_target)
2) 사이킷런으로 훈련 세트와 테스트 세트 나누기
- train_test_split() 함수는 전달되는 리스트나 배열을 비율에 맞게 훈련 세트와 테스트 세트로 나누어 줌
- train_test_split() 함수는 사이킷런의 model_selection 모듈 아래에 있음
- 자체적으로 랜덤 시드를 지정할 수 있는 random_state 매개변수가 있음
- 나누고 싶은 리스트나 배열을 원하는 만큼 전달
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)
- fish_data와 fish_target 2개의 배열을 전달했으므로 2개씩 나뉘어 총 4개의 배열이 반환
- 처음 2개는 입력 데이터(train_input, test_input), 나머지 2개는 타깃 데이터(train_target, test_target)
- 랜덤 시드는 42로 지정
- 이 함수는 기본적으로 25%를 테스트 세트로 때어 냄
- 훈련 데이터와 테스트 데이터를 각각 36개와 13개로 나눔
- 입력 데이터는 2개의 열이 있는 2차원 배열
print(train_input.shape, test_input.shape)
- 타깃 데이터는 1차원 배열
print(train_target.shape, test_target.shape)
- 도미와 빙어의 개수가 35개와 14개이므로 두 생선의 비율은 2.5:1
- 하지만 이 테스트 세트의 도미와 빙어의 비율은 3.3:1
- 샘플링 편향이 나타남
print(test_target)
- 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)
3) 수상한 도미 한마리
- 길이 25, 무게 150이면 도미(1)인데 빙어(0)라고 예측하는 문제
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier()
kn.fit(train_input, train_target)
kn.score(test_input, test_target)
print(kn.predict([[25,150]]))
- KNeighborsClassifier 클래스는 주어진 샘플에서 가장 가까운 이웃을 찾아주는 kneighbors() 메서드 제공
- 이 메서드는 이웃까지의 거리와 이웃 샘플의 인덱스를 반환
- KNeighborsClassifier 클래스의 이웃 개수인 n_neighbors의 기본값은 5이므로 5개의 이웃이 반환
- indexs 배열을 사용해 훈련 데이터 중 이웃 샘플을 따로 구분해서 그려보기
distances,indexes=kn.kneighbors([[25,150]])
import matplotlib.pyplot as plt
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_target[indexes])
4) 기준을 맞춰라
print(distances)
- 산점도에서 x축은 범위가 좁고(10~40), y축은 범위가 넓음(0~1000)
- 따라서 y축으로 조금만 멀어져도 거리가 아주 큰 값으로 계산됨
-> 이때문에 오른쪽 위의 도미 샘플이 이웃으로 선택되지 못함
- x축의 동일하게 0~1000으로 맞추어 봄
- 맷플롯립에서 x축 범위를 지정하려면 xlim() 함수 사용
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축)만 고려 대상이 됨
- 두 특성(길이와 무게)의 값이 놓인 범위가 매우 다름
- 이를 두 특성의 스케일(scale)이 다르다고 말함
- 데이터를 표현하는 기준이 다르면 알고리즘이 올바르게 예측할 수 없음
데이터 전처리(data preprocessing)
- 머신러닝 모델에 훈련 데이터를 주입하기 전에 가공하는 단계
- 가장 널리 사용하는 전처리 방법 중 하나는 표준 점수(standard score)
- 표준점수는 각 특성값이 평균에서 표준편차의 몇 배만큼 떨어져 있는지를 나타냄
- np.mean() 함수는 평균을 계산하고, np.std() 함수는 표준편차를 계산
- 특성마다 값의 스케일이 다르므로 평균과 표준편차는 각 특성별로 계산
-> 이를 위해 axis=0으로 지정 ( 행을 따라 각 열의 통계값을 계산)
mean = np.mean(train_input, axis=0)
std = np.std(train_input, axis=0)
print(mean,std)
- 원본 데이터에서 평균을 빼고 표준 편차로 나누어 표준 점수로 변환
train_scaled = (train_input - mean)/std
브로드캐스팅(broadcasting)
- 크기가 다른 넘파이 배열에서 자동으로 사칙 연산을 모든 행이나 열로 확장하여 수행하는 기능
- train_input의 모든 행에서 mean에 있는 두 평균값을 빼줌
- 그다음 std에 있는 두 표준편차를 다시 모든 행에 적용
5) 전처리 데이터로 모델 훈련하기
- 훈련 세트를 mean으로 빼고 std로 나누어 주었기 때문에 값의 범위가 크게 달라짐
- 샘플 [25,150]도 동일한 비율로 변환해야 함
- 반드시 훈련 세트의 평균과 표준편차로 테스트 세트를 바꿔야 함
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)로 예측
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()
'데이터 > 머신러닝' 카테고리의 다른 글
[혼공] ch 6. 비지도 학습 (0) | 2024.06.28 |
---|---|
[혼공] ch 5. 트리 알고리즘 (0) | 2024.06.27 |
[혼공] ch 4. 다양한 분류 알고리즘 (0) | 2024.06.26 |
[혼공] ch 3. 회귀 알고리즘과 모델 규제 (1) | 2024.06.26 |
[혼공] ch 1. 나의 첫 머신러닝 (1) | 2023.12.23 |