개요
이전 k-최근접 이웃 회귀에서는 가장 가까운 k개의 이웃 샘플을 찾고 이 샘플들의 타깃값을 평균으로 하여 예측으로 삼는다.
실습
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)
train_input = train_input.reshape(-1,1) # 1차원 배열을 2차원배열로 다시 만들어준다.
test_input =test_input.reshape(-1,1)
from sklearn.neighbors import KNeighborsRegressor
knr = KNeighborsRegressor(n_neighbors=3) #이웃의 개수를 3으로 지정하여 학습
knr.fit(train_input, train_target)
print(knr.predict([[50]])) # [1033.33333333]
import matplotlib.pyplot as plt
distances, indexes = knr.kneighbors([[50]]) # 50cm 농어의 이웃을 구한다.
plt.scatter(train_input, train_target)
plt.scatter(train_input[indexes], train_target[indexes], marker='D') # 훈련세트 중에서 이웃샘플만 다시 그린다.
plt.scatter(50, 1033, marker='^') # 50cm 농어 데이터
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
print(np.mean(train_target[indexes])) #훈련세트의 이웃들의 타깃
print(knr.predict([[100]])) # 예측한 길이가 100일 때의 타깃, [1033.33333333]
산점도를 보면 길이가 커질 수록 농어의 무게가 증가하는 경향이 있지만, K최근접 이웃 알고리즘은 50cm 농어에서 가장 가까운 것이 45cm 근방이기 때문에 이 샘플들의 무게를 평균한다.
print(knr.predict([[100]]))
[1033.33333333]
따라서 100cm여도 이웃의 평균이 1033이기 때문에 이렇게 나온다.
그러므로 테스트세트를 평균값을 따라가게 하는 것이 아닌 특성이 하나인 경우 어떤 직선을 학습하는 알고리즘인 선형회귀를 사용한다.
선형회귀
실습
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(train_input, train_target)
print(lr.predict([[50]])) # [1241.83860323]
print(lr.coef_, lr.intercept_) # [39.01714496] -709.0186449535474
하나의 직선을 그리려면 기울기와 절편이 있어야 한다.
y = ax+b일 때, a와 b를 구해야하는 데, LinearRegression 클래스에서 찾은 것은 lr.coef_와 lr.intercept_에 저장되어 있다.(a == lr.coef_, b == lr.intercept_)
plt.scatter(train_input, train_target) # 훈련세트의 산점도를 그린다.
plt.plot([15,50],[15*lr.coef_+lr.intercept_, 50*lr.coef_+lr.intercept_]) # 15에서 50까지 1차 방정식 그래프를 그린다.
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.9398463339976041 0.824750312331356
훈련세트의 점수보다 테스트세트의 점수가 더 적어서 과대적합이 의심스럽지만, 훈련세트도 높지 않으므로 과소적합이라 볼 수 있다.
하지만 위의 그래프를 봤을 때 농의의 무게가 0이하로 내려갈 수 없기 때문에, 우리는 다항회귀를 사용해야한다.
다항회귀
이번 실습에서 사용하는 다항회귀의 공식은 다음과 같다.
$$
무게 = a길이^2 + b길이 + c
$$
그리고 농어의 길이를 제곱한것이 배열에 들어가야 하므로 넘파이를 써서 원래의 배열 데이터 앞에 붙이겠다.
실습
train_poly = np.column_stack((train_input ** 2, train_input))
test_poly = np.column_stack((test_input ** 2, test_input))
print(train_poly, test_poly)
print(train_poly.shape, test_poly.shape)
# train_poly # 제곱데이터 원래 데이터 [[ 384.16 19.6 ] . . . [1190.25 34.5 ]] # test_poly [[ 70.56 8.4 ] . . . [ 745.29 27.3 ]] # train_poly.shape, test_poly.shape (42, 2) (14, 2)
column_stack()은 전달받은 리스트를 일렬로 세운다음 나란히 연결한다.
따라서 train_input을 제곱한 값과 train_input을 나란히 붙여서 42개의 행(샘플)과 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.0502107827827
point = np.arange(15,50) # 구간별 직선을 그리기 위해 15부터 49까지 정수배열을 만듦
plt.scatter(train_input,train_target)
plt.plot(point,1.01*point**2-21*point+116) # 15에서 49까지 2차원 방정식 그래프를 그림
plt.scatter(50,1573, marker = '^') # 50cm 농어데이터
plt.xlabel('length')
plt.ylabel('weight')
print(lr.score(train_poly, train_target)) # 0.9706807451768623
print(lr.score(test_poly, test_target)) # 0.9775935108325122
테스트 세트의 점수가 살짝 더 높은 것으로 보아 과소적합이 있는 것으로 보인다.
'STUDY' 카테고리의 다른 글
vim 명령어 정리 (2) | 2024.11.14 |
---|---|
[AI] 특성공학과 규제 (0) | 2022.11.06 |
[AI] k-최근접 이웃 회귀 (1) | 2022.11.06 |
[AI] 데이터 전처리 (0) | 2022.11.06 |
[AI] 훈련 세트와 테스트 세트 (0) | 2022.10.19 |