본문 바로가기
[파이썬]/머신러닝

[머신러닝] K최근접이웃모델(KNN) - 회귀분석의 기초(+ 예제)

by sung min_Kim 2023. 12. 22.

 본 글에서는 "K최근접이웃모델 - 회귀분석의 기초"에 대한 내용을 다루고자 한다. 'K최근접이웃 모델(KNN)'과 '머신러닝'의 기본적인 내용은 아래를 참조하시라.
2023.12.20 - [[파이썬]/머신러닝] - [머신러닝] K최근접이웃모델(KNN) - 분류분석의 기초
2023.12.20 - [[파이썬]/머신러닝] - [머신러닝] 머신러닝이란? (Machine Learning, 기계학습)


차례와 사용 툴 및 라이브러리는 아래와 같다.

 [차례]

 예제: 생선구분하기 - K최근접이웃모델(회귀)

 [순서]

  1. 데이터 불러오기
  2. 데이터 전처리
    - 원본 데이터를 numpy 배열로 변환
    - 원본 데이터를 훈련 및 테스트 데이터 세트로 분리
    - 훈련 및 테스트 데이터의 독립변수를 2차원 배열로 변환
  3. KNN 회귀모델 구축
  4. 모델 훈련(학습)
  5. 모델 성능 평가
    - 훈련 및 테스트 데이터 세트의 결정계수 확인
  6. 모델 예측 성능 평가
    - 테스트 데이터의 독립변수 전달을 통한 종속변수 예측
    - 실제 값과 예측 값 사이의 절대평균오차(MAE)를 계산하여 모델의 예측 성능 평가
  7.  하이퍼파라메터 튜닝
    - 과소적합 해소를 위한 이웃의 갯수 조정
  8. KNN 회귀모델의 한계
    - 임의 값 입력을 통한 예측
    - (산점도 그래프를 그리기 위한) 이웃의 인덱스 추출
    - 산점도 그래프 생성
    - 다른 임의 값 입력을 통한 예측 및 산점도 그래프 생성
    - 위의 산점도 그래프와 비교를 통한 KNN 회귀모델의 한계 도출 


 [사용 툴]
- Jupyter notebook(웹 기반 대화형 코딩 환경)
 
 [사용 라이브러리 및 모듈]

  • 연산 라이브러리: numpy
  • 시각화 라이브러리 및 모듈: matplotlib의 pyplot 모듈
  • 머신러닝 라이브러리 및 모델: scikit-learn
    - sklearn.neighbors.KNeighborsRegressor: KNN 회귀모델 생성
    - sklearn.model_selection.train_test_split: 훈련 및 테스트 데이터 세트 분리
    - sklearn.metrics.mean_absolute_error: 평균절대오차(MAE) 측정

 


· 예제: 생선구분하기 - K최근접이웃모델(회귀)

 

1. 데이터 불러오기
# 농어 길이
perch_length = [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 = [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. 데이터 전처리
2.1 원본 데이터를 numpy 배열로 변환


 numpy 라이브러리의 array 함수를 사용하여 원본 데이터를 numpy 배열로 변환한다. 이를 통해, 통계 관련 함수, 선형 함수 등 수학적 연산을 위한 다양한 함수를 제공하고, 다차원의 데이터를 쉽게 처리할 수 있게 된다.

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.2 훈련 및 테스트 데이터 세트로 분리하기 


 'sklearn' 라이브러리의 'model_selection' 모듈에 있는 'train_test_split' 함수를 사용하여 원본 데이터를 훈련 데이터와 테스트 데이터로 분리한다. 해당 함수에는 독립변수와, 종속변수, 훈련 및 테스트 데이터의 비율(75:25), 그리고 랜덤 규칙'을 인자로 전달한다. 이때 비율은 '75 : 25'로 설정할 것이다. 이를 통해 분리된 훈련 데이터는 모델(클래스) 학습(훈련)에 사용되고, 테스트 데이터는 학습된 모델의 성능을 평가하는 데 사용된다.

 함수 내에서 'test_size=0.25' 옵션을 통해 전체 데이터의 25%가 테스트 데이터로, 나머지 75%가 훈련 데이터로 분리된다. 또한, 'randon_state=42' 옵션은 일반적으로 사용되는 랜덤 규칙이다. 이 숫자는 특별한 의미를 가진 것은 아니며, 단지 데이터를 섞는 과정에서 사용되는 랜덤 시드값이다. 이 값을 설정하면, 같은 시드값을 사용하여 함수를 여러 번 호출하더라도 동일한 결과를 얻을 수 있다. 이는 재현성을 보장하는 데 중요한 기능이다.

from sklearn.model_selection import train_test_split

# 'random_state=42' : 일반적인 규칙
train_input, test_input, train_target, test_target = train_test_split(perch_length,
                                                                      perch_weight,
                                                                      test_size=0.25,
                                                                      random_state=42)

print(f"{train_input.shape} : {train_target.shape}")
print(f"{test_input.shape} : {test_target.shape}")

훈련 및 테스트 데이터 분리

 

2.3 훈련 및 테스트 데이터의 독립변수를 2차원 배열로 변환


 reshape() 함수는 배열의 차원을 재구성하는 데 사용된다. 여기서 첫 번째 인자 '-1'은 전체 행을, 두 번째 인자 '1'은 1열을 의미한다. 이렇게 두 개의 인자를 사용하여 훈련 데이터와 테스트 데이터의 독립변수를 2차원 배열로 변환한다. 결과적으로, 훈련 데이터와 테스트 데이터의 각각의 행들이 한 열로 구성된 2차원 배열로 재구성된다.

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

print(f"{train_input.shape} / {test_input.shape}")

훈련 및 테스트 데이터의 독립변수를 2차원 데이터로 변환

 

3. 모델 구축 
3.1 라이브러리 호출하기


 'sklearn.neighbors' 패키지의 'KNeighborsRegressor' 클래스를 사용하여 KNN 회귀모델을 생성한다.

from sklearn.neighbors import KNeighborsRegressor

 

3.2 KNN 회귀모델 생성하기


 KNeighborsRegressor 함수를 사용하여 KNN 회귀모델을 생성한다.

knr = KNeighborsRegressor()
knr

KNN 회귀모델(클래스) 생성

 

4. 모델 훈련(학습)


 KNN 모델은 '학습'이라는 개념보다는 '기억'하는 방식에 더 가깝다. fit 함수를 호출할 때, KNN 모델은 주어진 독립변수(특성 데이터)와 종속변수(레이블 또는 타겟 데이터)를 메모리에 저장한다. 이는 모델이 훈련 데이터를 '기억'하는 과정이다. 그리고 새로운 데이터 포인트가 주어졌을 때, 모델은 기억해둔 훈련 데이터 중에서 독립변수(특성) 사이의 거리를 기반으로 가장 가까운 K개의 데이터 포인트(이웃)을 찾는다. 이렇게 찾아진 이웃들의 종속변수(레이블 또는 타겟)을 바탕으로 새로운 데이터 포인트의 종속변수를 예측한다.

 회귀 문제에서는 이웃들의 종속변수의 평균값을 예측값으로 사용한다. 따라서, KNN 모델은 독립변수와 종속변수를 모두 '기억'하며, 이를 바탕으로 새로운 데이터에 대한 예측을 수행한다.

knr.fit(train_input, train_target)

KNN 회귀모델 학습



5. 모델 성능 평가
5.1 훈련 및 테스트 데이터 세트의 결정계수 확인


 훈련 데이터와 테스트 데이터에 score 함수를 적용하여 KNN 회귀모델(knr)에 대한 결정계수를 학인하는 작업을 수행한다.

train_r2 = knr.score(train_input, train_target)
test_r2 = knr.score(test_input, test_target)

print(f"훈련={train_r2} / 테스트={test_r2}")

KNN 회귀모델의 훈련 및 테스트 데이터의 결정계수

 

5.1.1 해석

 

  • 훈련 및 테스트 모델의 결정계수를 보아, "성능 좋은 모델"로 판단됨

  • 그러나, 훈련 결정계수가 테스트 결정계수보다 낮게 나타난 것으로 보아 '과소적합'이 발생하고 있는 것으로 판단됨

  • 이는 데이터의 개수가 작거나, 튜닝이 필요한 경우로 판단됨

 

5.1.2 정확도에 대한 용어 정의


 '분류'에서는 '정확도'라고 칭하며, 모든 샘플 중에서 정확하게 분류된 샘플의 수의 비율을 의미한다.
 '회귀'에서는 '결정계수(R^2)"라고 칭하며, 결정계수는 회귀 모델이 데이터의 변동성을 얼마나 잘 설명하는지를 나타내는 값으로, '1'에 가까울수록 모델이 데이터를 잘 설명하고 있다는 것을 의미한다.


6. 모델 예측 성능 평가


 학습된 모델을 통해 임의의 입력 값에 대한 출력 값을 예측하고, 이를 기존의 훈련 데이터와 함께 산점도 그래프에 표시함으로써, 모델이 어떻게 데이터를 해석하고 예측하는지를 직접적으로 확인할 수 있다. 이는 모델의 성능을 평가하고, 필요한 경우 모델을 수정하거나 개선하는 데 도움이 된다.

6.1 독립변수 데이터로 예측하기


 predict 함수에 테스트 데이터의 독립변수 값을 전달하여, KNN 회귀모델이 학습한 관계를 기반으로 해당 독립변수에 대한 종속변수의 예측값을 출력한다. 해당 출력값은 이웃의 갯수를 평균한 값이다. 말 그대로 '평균'이므로 정확한 값이 아닐 수 있음을 유의해야 한다.

test_perd = knr.predict(test_input)
test_perd

독립변수를 기반으로 예측한 값

 

6.2 예측 결과의 성능을 평가하기


 'sklearn.metrics' 패키지의 'mean_absolute_error' 함수는 '평균절대오차(MAE)'를 계산해 주는 함수이다. MAE는 예측값과 실제 값의 차이를 절대값으로 변환한 후 평균을 계산하는 방법으로, 회귀 모델의 성능을 평가하는 하나의 지표이다.

 MAE를 계산할 때 함수의 첫 번째 인자로는 실제 값(테스트 데이터의 종속변수)을, 두 번째 인자로는 모델에 의해 예측된 값(테스트 데이터의 독립변수를 예측한 값) 입력하여, 둘 사이의 절대적인 차이를 평균한다. 따라서,
MAE 값이 작을수록 모델의 성능이 좋다는 것을 의미한다.

from sklearn.metrics import mean_absolute_error

mae = mean_absolute_error(test_target, test_perd)
mae

평균절대오차(MAE)

 

6.2.1 해석

 

  • 이 모델을 사용하여 예측을 진행할 경우, 평균적으로 '19.157g' 정도의 오차가 발생하는 것으로 보임

  • 즉, 이 모델로 예측한 값은 실제 값과 '19.157g' 정도의 차이가 날 수 있다는 것을 의미함


7. 하이퍼파라메터 튜닝
7.1 (과소적합 해소를 위한) 하이퍼파라메터(이웃의 갯수) 조정


 n_neighbors 함수를 사용하여 KNN 모델의 '이웃의 개수(하이퍼파라메터)'를 조정한 후, 모델에 재학습시켜서 검증하도록 한다.

# 이웃의 갯수
knr.n_neighbors = 3

knr.fit(train_input, train_target)

train_r2 = knr.score(train_input, train_target)
test_r2 = knr.score(test_input, test_target)

print(f"훈련={train_r2} / 테스트={test_r2}")

하이퍼파라메터 조정: 과소적합 해소(훈련 > 테스트)

 

7.1.1 해석

 

  • 과소적합 해소를 위해 이웃의 갯수를 조정하여 하이퍼파라메터 튜닝을 진행한 결과 과소적합은 해소할 수 있었음

  • 또한, 훈련 결정계수와 테스트 결정계수의 차이가 크지 않기에 과대적합도 발생하지 않았음

  • 다만, 테스트 결정계수는 다소 낮아진 반면, 훈련 결정계수가 높아졌음

  • 이 모델은 과적합이 발생하지 않은 일반화된 모델로 사용가능

 

7.2 모델의 성능이 가장 좋은 시점 확인하기
knr = KNeighborsRegressor()
knr.fit(train_input, train_target)

# 결정계수가 가장 높을 때, 이웃 값을 담을 변수
nCnt = 0
# 결정계수가 가장 높을 때, 결정계수 값을 담을 변수
nScore = 0

for n in range(3, len(train_input), 2) :
    knr.n_neighbors = n
    score = knr.score(train_input, train_target)
    print(f"{n} / {score}")

    if score < 1 :
        if nScore < score :
            nScore = score
            nCnt = n
            
print(f"nCnt = {nCnt} / nSnScore = {nScore}")

모델의 이웃 갯수&결정계수를 통한 성능 확인

 

7.2.1 해석

 

  • 모델의 성능이 가장 좋은 시점의 이웃의 개수를 추출하기 위한 하이퍼파라메터 튜닝결과, 이웃의 개수를 '3'으로 지정하였을 때, 가장 좋은 성능을 발휘하는 것으로 확인됨

 

8. KNN의 한계
8.1 임의 데이터로 예측하기

  
 predict 함수에 임의의 독립변수 값을 전달하여, 모델이 학습한 관계를 기반으로 해당 독립변수에 대한 종속변수의 예측값을 출력한다.

# 길이 50으로 무게 예측하기
pred = knr.predict([[50]])
pred

임의의 값에 대한 예측 값

 

8.2 (산점도 그래프를 그리기 위한) 이웃의 인덱스 추출하기


 'kneighbors' 함수는 주어진 샘플에 가장 가까운 이웃을 찾는 데 사용된다. 이 메서드는 두 개의 배열을 반환하는데, 하나는 이웃들까지의 거리를 담은 배열(dist)이고, 다른 하나는 이웃들의 인덱스를 담은 배열(indexes)이다. 인덱스 배열(indexes)은 예측하려는 샘플과 가장 가까운 이웃들이 어떤 샘플이지 알려주는 역할을 한다.

dist, indexes = knr.kneighbors([[50]])
indexes

예측 값 '50'과 가장 가까운 이웃 인덱스

 

8.3 산점도 그래프 생성
import matplotlib.pyplot as plt

plt.scatter(train_input, train_target, c="blue", label="train")
plt.scatter(50, pred[0], marker="^", c="green", label="pred")
plt.scatter(train_input[indexes], train_target[indexes], c="red", label="nei")
plt.xlabel("length")
plt.ylabel("weight")
plt.show()

예측값이 '50'일 때의 산점도 그래프

 

8.4 위와 비교를 위한 또 다른 임의 데이터로 예측하기
# 길이 100으로 무게 예측하기
pred = knr.predict([[100]])
pred

50일 때와 동일한 예측값

 

8.5 사용된 이웃의 인덱스 확인
dist, indexes = knr.kneighbors([[100]])
indexes

50일 때와 동일한 인덱스

 

8.6 산점도 그래프 생성
plt.scatter(train_input, train_target, c="blue", label="train")
plt.scatter(100, pred[0], marker="^", c="green", label="pred")
plt.scatter(train_input[indexes], train_target[indexes], c="red", label="nei")
plt.xlabel("length")
plt.ylabel("weight")
plt.show()

예측값이 '100'일 때의 산점도 그래프

 

8.7 KNN의 한계

 

  • KNN 모델은 가장 가까운 이웃을 이용해서 예측을 수행하는 모델이기에, 예측하고자 하는 독립변수의 값이 기존 훈련 데이터의 독립변수가 가지고 있는 범위를 벗어나는 경우, 값이 항상 동일하게 나오는 한계점을 보임

  • 즉, 가까운 거리의 이웃이 항상 동일해짐으로써 데이터의 범위 밖에서는 새로운 예측을 제공하지 못하며, 그 결과 예측값이 항상 일정하게 나오게 됨

  • 따라서 다른 회귀모델을 사용해야 함

 


 이번 글에서는 KNN 회귀모델의 특성, 사용 방법, 그리고 한계점에 대해 살펴보았다. 다음 글에서는 이 한계점을 해결하기 위한 방안으로 선형회귀모델을 적용해 볼 것이며, 또한 선형회귀모델에서의 한계점을 해결하기 위한 방안으로 다항회귀모델을 적용해 볼 것이다.
2023.12.23 - [[파이썬]/머신러닝] - [머신러닝] 선형회귀와 다항회귀 모델링의 이론과 실제 적용 방법(+예제)

 

[머신러닝] 선형회귀와 다항회귀 모델링의 이론과 실제 적용 방법(+예제)

HTML 삽입 미리보기할 수 없는 소스 회귀모델은 다양한 종류가 있지만, 이번 글에서는 "선형회귀모델(LR; Liner Regression Model)"과 "다항회귀모델(PR; Polynomial Regression Model)"에 대해 다루어보려 한다. 선

sungmin93.tistory.com