STUDY

[AI] 선형회귀

Dalseung 2022. 11. 6. 14:26

개요

이전 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]

image

산점도를 보면 길이가 커질 수록 농어의 무게가 증가하는 경향이 있지만, 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()

image

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