[공부] 03-1 K-최근접 이웃 회귀

정은·2023년 7월 17일

Chapter 3. 회귀 알고리즘과 모델 규제

03-1 k-최근접 이웃 회귀


지도 학습 알고리즘은 크게 분류회귀로 나눠진다.

  • 분류는 샘플을 몇 개의 클래스 중 하나로 분류하는 문제이다.
  • 회귀는 클래스 중 하나로 임의의 어떤 숫자를 예측하는 문제이다.

✅ 용어 설명

  • 회귀 : 임의의 수치를 예측하는 문제이다. 따라서, 타깃값도 임의의 수치가 된다.

k-최근접 이웃 회귀

2장에서 배웠던 k-최근접 이웃 알고리즘이 분류뿐만 아니라 회귀에서도 적용된다.

  • K-최근접 이웃 분류 알고리즘은 예측하려는 샘플에 가장 가까운 샘플 k개를 선택한다. 그 다음 이 샘플들의 클래스를 확인하여 다수 클래스를 새로운 샘플의 클래스로 예측된다.

  • K-최근접 이웃 회귀 알고리즘은 분류와 똑같이 예측하려는 샘플에 가장 가까운 샘플 k개를 선택한다. 하지만 회귀이므로 이웃한 샘플의 타깃은 어떤 클래스가 아니라 임의의 수치이다. 이웃 샘플의 수치들의 평균을 구해 예측 타깃값을 구한다.

✅ 용어 설명

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

2장에서 했듯이 데이터를 훈련 세트테스트 세트로 나누어 보자.

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차원 배열이어야 하므로 1차원 배열을 reshape 한다.

test_array = np.array([1,2,3,4])
print(test_array.shape)

test_array = test_array.reshape(2,2)
print(test_array.shape)

reshape() 메서드는 바꾸려하는 배열의 크기를 지정할 수 있다. 바꾸고자 하는 배열의 크기를 매개변수로 전달하며, 바꾸기 전후의 배열 원소 개수는 동일해야 한다.

train_input = train_input.reshape(-1, 1) # 크기에 -1을로 지정하면 나머지 원소 개수로 모두 채움
test_input = test_input.reshape(-1, 1)
print(train_input.shape, test_input.shape)

>>> (42, 1) (14, 1)

결정계수(R²)

분류의 경우에는 우리가 모델을 훈련하고 테스트 세트의 점수를 알아보기 위해 평가를 할때 나오는 점수는 정확도였다. 정확도는 테스트 세트에 있는 샘플을 정확하게 분류한 개수의 비율을 말한다.

그럼, 회귀의 경우에는 어떤 평가지수로 판단할까?

바로, 결정계수이다. 회귀에서는 정확한 숫자를 맞힌다는 것이 거의 불가능하므로 샘플의 타깃과 예측 값의 차이를 보는 결정 계수 값을 사용한다.

위와 같은 식으로 결정계수가 계산되는데, 각 샘플의 타깃과 예측한 값의 차이를 제곱하여 더한다.
그 다음 타깃과 타깃 평균의 차이를 제곱하여 더한 값으로 나눈다.

만약 타깃의 평균 정도를 예측하는 수준이라면 R²는 0에 가까워지고(즉 분자와 분모가 비슷해져) 예측이 타깃에 아주 가까워지면 1에 가까워지는 값(분자가 0에 가까워지기 때문에)을 가진다.

✅ 용어 설명

  • 결정계수(R²) : 대표적인 회귀 문제의 성능 측정 도구이다. 1에 가까울수록 좋고, 0에 가깝다면 성능이 나쁜 모델이다.
from sklearn.neighbors import KNeighborsRegressor

knr = KNeighborsRegressor()

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

print(knr.score(test_input, test_target))

>>> 0.992809406101064

0.99의 값은 1에 가깝기에 좋은 모델이라고 할 수 있다.

다만, 결정 계수는 직감적으로 얼마나 좋은지 이해하기 어렵기 때문에 사이킷런에서는 여러 가지 측정 도구를 제공한다. ex) mse, mae

mean_absolute_error는 타깃과 예측의 절댓값 오차를 평균하여 반환한다. 첫 번째 매개변수는 타깃, 두 번째 매개변수는 예측값을 전달한다.

이와 비슷한 함수로는 평균 제곱 오차를 계산하는 mean_squared_error가 있다. 이 함수는 타깃과 예측 값을 뺀 값을 제곱한 다음 전체 샘플에 대해 평균한 값을 반환한다.

from sklearn.metrics import mean_absolute_error

# 테스트 세트에 대한 예측을 만듭니다
test_prediction = knr.predict(test_input)

# 테스트 세트에 대한 평균 절댓값 오차를 계산한다.
mae = mean_absolute_error(test_target, test_prediction)
print(mae)

mae의 값은 적을수록 좋은 모델임을 알 수 있다.

과대 적합 vs 과소 적합

과대 적합은 훈련 세트에서는 점수가 굉장히 좋았는데 테스트 세트에서 점수가 굉장히 나쁜 모델의 현상이 나는 것을 말한다. 즉, 훈련 세트에만 잘 맞는 모델이라 테스트 세트와 나중에 실전에 투입하여 새로운 샘플에 대한 예측을 만들 때 잘 동작하지 않을 것이다.

과소 적합은 반대로 훈련 세트보다 테스트 세트의 점수가 높거나 두 점수가 모두 너무 낮은 경우이다.

왜 과소 적합이 일어날까 ?

  • 이런 현상의 또 다른 원인은 훈련 세트와 테스트 세트의 크기가 매우 작기 때문이다.

해당 문제를 해결하는 방법은 아래와 같다.

과소 적합을 해결 하는 방법은 K-이웃의 개수를 늘리는 방법이며, 과대 적합을 해결 하는 방법은 K-이웃의 개수를 줄이는 방법이다.

이에 따라, 원래 KNeighborsRegressorn_neighbors 매개변수의 기본 값은 5이므로 3으로 줄여 다시 학습해본다.

# 이웃의 갯수를 3으로 설정합니다
knr.n_neighbors = 3

# 모델을 다시 훈련합니다
knr.fit(train_input, train_target)
print(knr.score(train_input, train_target))

✅ 용어 설명

  • 과대 적함 : 모델의 훈련 세트 성능이 테스트 세트 성능보다 훨씬 높을 때 일어난다. 모델이 훈련 세트에 너무 집착해서 데이터에 내재된 거시적인 패턴을 감지하지 못한다.
  • 과소 적합 : 훈련 세트와 테스트 세트 성능이 모두 동일하게 낮거나 테스트 세트 성능이 오히려 더 높을 때 일어난다. 이런 경우 더 복잡한 모델을 사용해 훈련 세트에 잘 맞는 모델을 만들어야 한다.

확인 문제 🖥️

2. k-최근접 이웃 회귀 모델의 k 값을 1, 5, 10으로 바꿔가며 훈련해 보세요. 그 다음 농어의 길이를 5에서 45까지 바꿔가며 예측을 만들어 그래프로 나타내보세요.

소스코드


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

전체 소스 코드

JeongeunBae의 Github에서 전체 코드를 확인하실 수 있습니다. 👻
https://github.com/JeongEunBae/TIL/blob/main/Basic_ML_DL%20(%ED%98%BC%EA%B3%B5%EB%A8%B8%EC%8B%A0)/3_1_K_%EC%B5%9C%EA%B7%BC%EC%A0%91_%EC%9D%B4%EC%9B%83_%ED%9A%8C%EA%B7%80.ipynb

profile
정니의 이런거 저런거 기록 일지 😛

1개의 댓글

comment-user-thumbnail
2023년 7월 18일

너무 좋은 글이네요. 공유해주셔서 감사합니다.

답글 달기