티스토리 뷰
주어진 데이터를 가지고 학습하여 테스트 데이터를 "분류"하는 k-최근접 이웃 알고리즘에 대하여 직전 포스팅에서 다뤘다. 물론 k-최근접 이웃 알고리즘으로도 특정 값을 예측하는 "회귀" 모델을 만들 수 있지만, 그 한계가 명확하다.
아래 예제로 한번 k-최근접 이웃 알고리즘의 회귀 모델이 갖는 한계점을 확인해보자.
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])
위 데이터는 농어의 길이와 무게 데이터이다. 이 데이터를 가지고 k-최근접 이웃 알고리즘에 대입하여 농어의 길이에 따른 무게를 예측하는 모델을 만들려고 하면, 우선 길이, 무게 데이터가 1차 배열이므로 reshape하여 크기가 3, 1인 1차원 배열로 만들어 준 뒤 아래와 같이 코드를 짜면 된다.
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsRegressor
import numpy as np
import matplotlib.pyplot as plt
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])
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)
test_input = test_input.reshape(-1, 1)
# k-최근접 이웃 회귀
knr = KNeighborsRegressor()
knr.n_neighbors = 3
knr.fit(train_input, train_target)
print(knr.predict([[50]]))
위 코드의 결과 값은 [1033.33333333] 이라는 값이 나온다.
44cm 인 농어가 1000g 이라는 데이터가 있는데, 6cm나 차이나는 농어의 무게가 거의 같다는 것이 말이 안된다.
데이터와 길이가 예측 값, 이웃 값을 좌표평면에 찍어보면 바로 원인을 찾을 수 있다.
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsRegressor
from sklearn.linear_model import LinearRegression
import numpy as np
import matplotlib.pyplot as plt
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])
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)
test_input = test_input.reshape(-1, 1)
knr = KNeighborsRegressor()
knr.n_neighbors = 3
knr.fit(train_input, train_target)
distances, indexes = knr.kneighbors([[50]])
plt.scatter(train_input, train_target)
plt.scatter(train_input[indexes], train_target[indexes], marker='D')
plt.scatter(50, 1033, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
초록색 데이터가 길이가 50cm 인 농어의 예측 무게 데이터, 주황색 세모가 최근접 3개의 이웃이다.
농어의 길이가 아무리 길어져도 해당 데이터의 최근접 데이터 셋에는 변화가 없기 때문에, 길이가 1000cm인 농어의 데이터를 입력해도 그 결과는 1000g에서 큰 차이가 없는 것이다.
즉, k-최근접 이웃 알고리즘은 학습한 데이터 범위가 벗어나버리면 그 예측값의 정확도가 크게 떨어진다는 것이다.
따라서, 우리는 선형 회귀를 사용해야 한다.
선형회귀는 그 이름에서 짐작 가능하듯, 특성이 하나인 경우에 데이터의 패턴대로 직선의 방정식을 도출하고, 그 직선을 토대로 예측 값을 구하는 알고리즘이다.
바로 확인해보자.
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsRegressor
from sklearn.linear_model import LinearRegression
import numpy as np
import matplotlib.pyplot as plt
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])
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)
test_input = test_input.reshape(-1, 1)
# 선형 회귀
lr = LinearRegression()
# 선형 회귀 모델을 학습
lr.fit(train_input, train_target)
# 50cm 농어에 대한 예측값
print(lr.predict([[50]]))
# 데이터 모델로 부터 도출한 직선의 기울기(가중치)와 절편
print(lr.coef_, lr.intercept_)
# 훈련 데이터의 산점도
plt.scatter(train_input, train_target)
# 15 ~ 50 까지의 1차 방정식 그래프
plt.plot([15, 50], [15*lr.coef_ + lr.intercept_, 50*lr.coef_ + lr.intercept_])
plt.scatter(50, lr.predict([[50]]), marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
선형 회귀 모델의 50cm 길이의 농어의 무게가 1241.83860323 정도라고 예측한다. 이는 k-최근접 이웃 알고리즘보다 훨씬 신빙성 있는 예측값이다.
하지만, 해당 선형 회귀 모델에 대한 정확도 Score를 확인해보면,
훈련 데이터 >> 0.939846333997604
테스트실 데이터 >> 0.824750312331356
정도로 전체적으로 선형 회귀 모델의 대한 R제곱 Score가 낮다는 것을 확인할 수 있다.
이는 선형 그래프와 데이터 산점도를 확인해보면 금방 눈치챌 수 있다.
보면 애초에 모델이 되는 데이터가 선형적인 형태와 맞지 않다. 현실에서는 길이가 0, 무게가 0인 농어는 존재하지 않기 때문이다. 데이터를 잘 살펴보면, 일직선이라기 보단, 왼쪽 위로 조금 구부러진 곡선에 가깝다.
곡선인 2차 방정식의 그래프를 그리려면 길이를 제곱한 항이 훈련 데이터 세트에 추가되어야 한다. 1차원 벡터인 train, test input 데이터에 각 항을 제곱한 데이터를 아래와 같이 붙여주자.
train_poly = np.column_stack((train_input**2, train_input))
test_poly = np.column_stack((test_input**2, test_input))
동일하게 해당 데이터로 학습을 시켜준 뒤, 예측 값을 확인해보면 [1573.98423528] 로 직선을 사용하여 도출했을 때 보다 더 큰 값을 예측했다.
해당 모델의 가중치와 절편은 [ 1.01433211 -21.55792498] 116.0502107827827 로 확인되기 때문에, 아래와 같이 2차 방정식 그래프와 함께 산점도를 확인하면
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsRegressor
from sklearn.linear_model import LinearRegression
import numpy as np
import matplotlib.pyplot as plt
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])
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)
test_input = test_input.reshape(-1, 1)
# 선형 회귀
lr = LinearRegression()
train_poly = np.column_stack((train_input**2, train_input))
test_poly = np.column_stack((test_input**2, test_input))
lr.fit(train_poly, train_target)
print(lr.predict([[50**2, 50]]))
print(lr.coef_, lr.intercept_)
point = np.arange(15, 50)
plt.scatter(train_input, train_target)
plt.plot(point, 1.01*point**2 - 21.6*point + 116.05)
plt.scatter(50, 1574, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
위와 같이 이전과 다르게 그래프와 산점도가 매우 높은 수준으로 맞춰진다.
이런 식으로 단항, 다항 선형 회귀를 사용하여 훈련 데이터 셋에 없는 값을 예측하는 모델을 만들 수 있다.
끝!
'[Python] > Machine learning' 카테고리의 다른 글
[ML] 지도 학습 5: 확률적 경사 하강법 (0) | 2023.02.24 |
---|---|
[ML] 지도 학습 4: 로지스틱 회귀 (0) | 2023.02.23 |
[ML] 지도 학습 3: 다중 회귀(특성 공학과 규제) (0) | 2023.02.21 |
[ML] 지도 학습 1: k-nearest neighbors 알고리즘 (0) | 2023.02.20 |
[ML] 기초: 지도 학습과 비지도 학습 (0) | 2023.02.20 |
- javascript
- 이탈리안 레스토랑
- await
- redux-thunk
- Promise
- 파니노구스토
- 인천 구월동 맛집
- react-native
- Async
- redux
- 맛집
- 정보보안기사 #실기 #정리
- 인천 구월동 이탈리안 맛집
- react
- AsyncStorage
- Total
- Today
- Yesterday