728x90
print(lr.coef_, lr.intercept_)

이번 장에서는 k-최근접이웃 알고리즘의 한계에 대해 이야기한다.

기존에 참고하는 data가 충분하지 않고 새로운 영역에 대한 data가 들어왔을 때 기존에 있는 자료만 가지고 비슷한 이웃을 찾아 결과를 알려준다면 그 결과가 원하는 내용이 되지 않는다는 것이다.

 

import numpy as np

perch_length = np.array(
    [8.4, 13.7, 15.0, 16.2, 17.4, 18.0, 18.7, 19.0, 19.6, 20.0, 
     21.0, 21.0, 21.0, 21.3, 22.0, 22.0, 22.0, 22.0, 22.0, 22.5, 
     22.5, 22.7, 23.0, 23.5, 24.0, 24.0, 24.6, 25.0, 25.6, 26.5, 
     27.3, 27.5, 27.5, 27.5, 28.0, 28.7, 30.0, 32.8, 34.5, 35.0, 
     36.5, 36.0, 37.0, 37.0, 39.0, 39.0, 39.0, 40.0, 40.0, 40.0, 
     40.0, 42.0, 43.0, 43.0, 43.5, 44.0]
     )
perch_weight = np.array(
    [5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0, 
     110.0, 115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0, 
     130.0, 150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0, 
     197.0, 218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0, 
     514.0, 556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0, 
     820.0, 850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0, 
     1000.0, 1000.0]
     )

from sklearn.model_selection import train_test_split

# 훈련 세트와 테스트 세트로 나눕니다
train_input, test_input, train_target, test_target = train_test_split(
    perch_length, perch_weight, random_state=42)
# 훈련 세트와 테스트 세트를 2차원 배열로 바꿉니다
train_input = train_input.reshape(-1, 1)
test_input = test_input.reshape(-1, 1)

from sklearn.neighbors import KNeighborsRegressor

knr = KNeighborsRegressor(n_neighbors=3)
# k-최근접 이웃 회귀 모델을 훈련합니다
knr.fit(train_input, train_target)

print(knr.predict([[50]]))

[1033.33333333]

 

import matplotlib.pyplot as plt

# 50cm 농어의 이웃을 구합니다
distances, indexes = knr.kneighbors([[50]])

# 훈련 세트의 산점도를 그립니다
plt.scatter(train_input, train_target)
# 훈련 세트 중에서 이웃 샘플만 다시 그립니다
plt.scatter(train_input[indexes], train_target[indexes], marker='D')
# 50cm 농어 데이터
plt.scatter(50, 1033, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

 

농어의 길이가 50이나 되는데 최근접 이웃인 45보다 작은 농어의 평균 값을 50cm짜리 농어의 무게로 예측한다.

print(np.mean(train_target[indexes]))

1033.3333333333333

print(knr.predict([[100]]))

[1033.33333333]

# 100cm 농어의 이웃을 구합니다
distances, indexes = knr.kneighbors([[100]])

# 훈련 세트의 산점도를 그립니다
plt.scatter(train_input, train_target)
# 훈련 세트 중에서 이웃 샘플만 다시 그립니다
plt.scatter(train_input[indexes], train_target[indexes], marker='D')
# 100cm 농어 데이터
plt.scatter(100, 1033, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

선형회귀 : 직선으로 결과값 예측

from sklearn.linear_model import LinearRegression

lr = LinearRegression()
# 선형 회귀 모델 훈련
lr.fit(train_input, train_target)

# 50cm 농어에 대한 예측
print(lr.predict([[50]]))

[1241.83860323]

 

print(lr.coef_, lr.intercept_)

[39.01714496] -709.0186449535477

 

from sklearn.linear_model import LinearRegression

lr = LinearRegression()
# 선형 회귀 모델 훈련
lr.fit(train_input, train_target)

# 50cm 농어에 대한 예측
print(lr.predict([[50]]))

[1241.83860323]

print(lr.coef_, lr.intercept_)

[39.01714496] -709.0186449535477

# 훈련 세트의 산점도를 그립니다
plt.scatter(train_input, train_target)
# 15에서 50까지 1차 방정식 그래프를 그립니다
plt.plot([15, 50], [15*lr.coef_+lr.intercept_, 50*lr.coef_+lr.intercept_])
# 50cm 농어 데이터
plt.scatter(50, 1241.8, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

print(lr.score(train_input, train_target))
print(lr.score(test_input, test_target))

0.939846333997604

0.8247503123313558

# 직선으로 추세선을 만들어 기울기에 맞는 값을 예측한다.

 

다항회귀 : 곡선으로 결과값 예측

train_poly = np.column_stack((train_input ** 2, train_input))
test_poly = np.column_stack((test_input ** 2, test_input))

print(train_poly.shape, test_poly.shape)

(42, 2) (14, 2)

lr = LinearRegression()
lr.fit(train_poly, train_target)

print(lr.predict([[50**2, 50]]))

[1573.98423528]

print(lr.coef_, lr.intercept_)

[ 1.01433211 -21.55792498] 116.05021078278276

 

무게 = 1.01 * 길이^2 - 21.6 * 길이 + 116.05

 

# 구간별 직선을 그리기 위해 15에서 49까지 정수 배열을 만듭니다
point = np.arange(15, 50)
# 훈련 세트의 산점도를 그립니다
plt.scatter(train_input, train_target)
# 15에서 49까지 2차 방정식 그래프를 그립니다
plt.plot(point, 1.01*point**2 - 21.6*point + 116.05)
# 50cm 농어 데이터
plt.scatter([50], [1574], marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

다항회귀

print(lr.score(train_poly, train_target))
print(lr.score(test_poly, test_target))

0.9706807451768623

0.9775935108325121

728x90
728x90

From : 혼자공부하는 머신러닝+딥러닝 (한빛미디어)

지도학습 : Supervised Learning

      - data와 target로 이뤄진 훈련 데이터가 필요하며 새로운 데이터를 예측하는데 활용

 

비지도학습 : Unsupervised Learning

      - Target data없이 input data만 있을 때 사용 -> 정답을 사용하지 않기 때문에 무엇가를 맞힐 수는 없고 data를 잘 파악하거나 변형하는데 도움을 줌

 

훈련데이터 : Training data

      - 지도학습의 경우 필요한 입력(data)과 정답(target)을 합쳐놓은 것

 

훈련세트/테스트세트 : Train set / Test set

      - test set 전체 data의 20~30%로 사용

 

샘플링 편향 : Sampling bias

      - 훈련세트와 테스트세트에서 샘플이 고르게 섞여있지 않을 때 나타남

 

데이터 전처리 : data preprocessing

      - 머신러닝 모델에 훈련 데이터를 주입하기 전 가공하는 단계. 특성값을 일정한 기준으로 맞추어 주는 작업.

      - data를 표현하는 기준이 다르면 알고리즘을 올바르게 예측할 수 없음

728x90
728x90

Chapter 03에서는 '회귀 알고리즘과 모델 규제'에 대해 나온다.

소제목으로 '농어의 무게를 예측하라!' 라고 되어 있다.

 

드디어 분류가 아니라 예측을 향해 나아간다.

이 책에서는 농어의 data가 있는 상황에서 새로운 농어에 대해 길이 값만 가지고 있다.

기존 농어의 길이/무게 data들을 가지고 있을 때 새로운 농어의 무게를 예측해보자.

 

우선 기존 data의 산점도를 보자

##데이터 준비

import numpy as np

perch_length = np.array(
    [8.4, 13.7, 15.0, 16.2, 17.4, 18.0, 18.7, 19.0, 19.6, 20.0, 
     21.0, 21.0, 21.0, 21.3, 22.0, 22.0, 22.0, 22.0, 22.0, 22.5, 
     22.5, 22.7, 23.0, 23.5, 24.0, 24.0, 24.6, 25.0, 25.6, 26.5, 
     27.3, 27.5, 27.5, 27.5, 28.0, 28.7, 30.0, 32.8, 34.5, 35.0, 
     36.5, 36.0, 37.0, 37.0, 39.0, 39.0, 39.0, 40.0, 40.0, 40.0, 
     40.0, 42.0, 43.0, 43.0, 43.5, 44.0]
     )
perch_weight = np.array(
    [5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0, 
     110.0, 115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0, 
     130.0, 150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0, 
     197.0, 218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0, 
     514.0, 556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0, 
     820.0, 850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0, 
     1000.0, 1000.0]
     )

import matplotlib.pyplot as plt
plt.scatter(perch_length, perch_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

from sklearn.model_selection import train_test_split

##train_test_split : train/test set 생성
train_input, test_input, train_target, test_target = train_test_split(
    perch_length, perch_weight, random_state=42)
print(train_input.shape, test_input.shape)
test_array = np.array([1,2,3,4])
print(test_array.shape)
test_array = test_array.reshape(2, 2)
print(test_array.shape)

train_input = train_input.reshape(-1, 1)
test_input = test_input.reshape(-1, 1)
print(train_input.shape, test_input.shape)

(42,) (14,)

(4,) (2, 2)

(42, 1) (14, 1)

결정 계수 (R^2)

분류를 할 때는 얼마나 정확하게 분류한 지 점수로 평가가 가능

예측 모델에서는 결정계수를 사용

 

R^2 = 1 - (sum((타깃-예측)^2)) / (sum((타깃-평균)^2))

 

from sklearn.neighbors import KNeighborsRegressor
knr = KNeighborsRegressor()
# k-최근접 이웃 회귀 모델을 훈련합니다
knr.fit(train_input, train_target)

knr.score(test_input, test_target)

0.992809406101064

 

mean_absolute_error : 타깃과 예측의 절댓값 오차의 평균

from sklearn.metrics import mean_absolute_error
# 테스트 세트에 대한 예측을 만듭니다
test_prediction = knr.predict(test_input)
# 테스트 세트에 대한 평균 절댓값 오차를 계산합니다
mae = mean_absolute_error(test_target, test_prediction)
print(mae)

19.157142857142862

평균적으로 무게에 대한 예측 값과 실제 값이 19g정도 차이 난다는 결과

과대적합 vs 과소적합

과대적합(overfitting) : 훈련 세트만 점수가 좋고 테스트 세트에서 점수가 낮음

과소적합(underfitting) : 훈련세트보다 테스트세트의 점수가 더 높거나, 둘다 낮은 경우

 

print(knr.score(train_input, train_target))

0.9698823289099254

 

knr.score(test_input, test_target) : 0.99
knr.score(train_input, train_target) : 0.96

훈련세트보다 테스트세트의 점수가 높으니 과소적합

 

# 이웃의 갯수를 3으로 설정
knr.n_neighbors = 3
# 모델을 다시 훈련
knr.fit(train_input, train_target)
print(knr.score(train_input, train_target))

0.9804899950518966

점수가 조금 더 올라 갔다

 

이웃 수를 변경하면서 결과를 확인해보자

# k-최근접 이웃 회귀 객체를 만듭니다
knr = KNeighborsRegressor()
# 5에서 45까지 x 좌표를 만듭니다
x = np.arange(5, 45).reshape(-1, 1)

# n = 1, 5, 10일 때 예측 결과를 그래프로 그립니다.
for n in [1, 5, 10]:
    # 모델 훈련
    knr.n_neighbors = n
    knr.fit(train_input, train_target)
    # 지정한 범위 x에 대한 예측 구하기 
    prediction = knr.predict(x)
    # 훈련 세트와 예측 결과 그래프 그리기
    plt.scatter(train_input, train_target)
    plt.plot(x, prediction)
    plt.title('n_neighbors = {}'.format(n))    
    plt.xlabel('length')
    plt.ylabel('weight')
    plt.show()
    print(knr.score(train_input, train_target))

 

 

 

728x90
728x90

그냥 근접 이웃 알고리즘으로 했을 때 잘못된 분류로 넘어가는 경우가 있다.

이럴 경우 어떻게 보정을 해줄 것인가?

 

# data 전처리
# Numpy로 data 준비

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])

# 5개를 1로 채운다
# np.zeros()는 0으로 채운다
print(np.ones(5))

# np.column_stack()은 2차 배열 방식으로 연결
# np.concatenate()은 1차 배열 방식으로 연결
# 1이 35개, 0이 14개 있는 data 생성
fish_target = np.concatenate((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.]

 

## scikit-learn으로 training set, test set 나누기
# train_test_split() : training/test set를 fish_target에 맞게 나눠준다
# 기본적으로 25%를 test set으로 만든다
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)

# shape : size를 표시
print(train_input.shape, test_input.shape)
print(train_target.shape, test_target.shape)
print(test_target)

(36, 2) (13, 2)

(36,) (13,)

[0. 0. 1. 0. 1. 0. 1. 1. 1. 1. 1. 1. 1.]

 

# 수상한 도미 한마리
# 이번 장의 핵심 주제 : 엉뚱한 결과로 들어가는 data를 어떻게 보정할 것인가?

# 기존 알고리즘 : 그냥 근접 이웃을 표시
from sklearn.neighbors import KNeighborsClassifier

kn = KNeighborsClassifier()
kn.fit(train_input, train_target)
kn.score(test_input, test_target)

print(kn.predict([[25, 150]]))

1.0

[0.]

 

import matplotlib.pyplot as plt

plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(25, 150, 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='^')
##인접 data를 마름모로 표시
plt.scatter(train_input[indexes,0], train_input[indexes,1], marker='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]]]

print(train_target[indexes])

[[1. 0. 0. 0. 0.]]

print(distances)

[[ 92.00086956 130.48375378 130.73859415 138.32150953 138.39320793]]

 

기준을 맞춰라

분류의 기준을 조정

 

가장 가까운 샘플까지의 거리는 92, 그외는 130,138 정도 된다.

X축은 범위가 좁고, Y축은 범위가 넓어 서로 scale이 맞지 않다.

scale을 맞추면 아래와 같이 된다.(X축의 거의 의미가 없어짐)

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()

표준점수(standard score) : scale을 보정하는 가장 일반적인 방법

   - 평균에서 표준편차의 몇 배만큼 떨어져 있는지는 나타냄, 원점에서 몇 표준편차만큼 떨어져 있는지

   - 표준편차 : 분산의 제곱근

   - 분산 : data에서 평균을 뺀 값을 모두 제곱한 다음 평균을 냄

mean = np.mean(train_input, axis=0)
std = np.std(train_input, axis=0)
print(mean, std)
train_scaled = (train_input - mean) / std

[ 27.29722222 454.09722222] [ 9.98244253 323.29893931]

전처리 데이터로 모델 훈련하기

plt.scatter(train_scaled[:,0], train_scaled[:,1])
plt.scatter(25, 150, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

표준점수로 변환한 train 자료에 의심스러운 도미를 넣으면 혼자만 동떨어지게 된다.

다른 자료들은 표준점수인데 비해 새로운 자료는 data 자체이기 때문에 맞지 않다.

 

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()

동일 기준을 적용하면 처음 보았던 산점도와 비슷하게 나온다.

scale을 맞춘 자료를 이용하여 다시 근접이웃 알고리즘을 돌려보자.

 

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()

최종적으로 눈에 보이는 가까운 이웃들이 선택되었다.

 

728x90

+ Recent posts