본문 바로가기

Machine Learning_모델설계_Python

kNN_4 회귀

안녕하세요 배도리 입니다. 오늘은 kNN 마지막 부분입니다. kNN은 분류 뿐만 아니라 회귀에서도 가능합니다. 회귀는연속적인 수치(실수)를 예측하는 것이기에 종속변수가 연속형인경우입니다. 그런데 사실 일반적으로 kNN은 분류에 더 많이 사용되는 경우가 많습니다. kNN은 주어진 샘플을 가장 가까운 이웃들과 비교하여 다수결이나 가중 평균 등을 통해 클래스를 할당하는 방식으로 작동합니다. 이는 kNN의 분류 알고리즘의 특징과 장점에 잘 부합하기 때문입니다. 그래도 배워봅시당 ㅎㅎ

 

kNN 회귀는 모델 학습 과정이 없으며, 입력 데이터와 훈련 데이터셋 간의 거리를 기반으로 예측을 수행합니다.


kNN 회귀는 데이터의 분포에 따라 이웃의 개수(k)를 조정함으로써 모델의 유연성을 조절할 수 있습니다. 작은 k 값은 모델의 복잡성을 증가시키고 노이즈에 민감하게 작동할 수 있으며, 큰 k 값은 모델의 부드러움을 증가시키고 지역적인 패턴을 더 잘 캡처할 수 있습니다.

kNN 회귀는 비선형 회귀 문제에 적용되며, 주로 작은 규모의 데이터셋에 적용됩니다. 특히 데이터가 충분하지 않거나 특성의 중요도가 고르지 않은 경우에 kNN 회귀를 활용할 수 있습니다.

데이터가 충분하지 않은 경우: kNN 회귀는 훈련 데이터셋의 근접 이웃들의 값을 활용하여 예측을 수행하기 때문에 데이터가 충분하지 않은 상황에서도 상대적으로 좋은 성능을 보일 수 있습니다. 작은 규모의 데이터셋에서는 KNN 회귀가 유용한 대안이 될 수 있습니다.

특성의 중요도가 고르지 않은 경우: kNN 회귀는 비선형 모델이기 때문에 특성 간의 상호작용과 비선형 관계를 모델링하는 데 용이합니다. 특성의 중요도가 고르지 않거나 불규칙한 형태를 가지는 경우에도 kNN 회귀를 적용할 수 있습니다.

그러나 kNN 회귀는 한계점이 있으며, 특히 데이터셋이 크거나, 고차원인 경우에는 성능이 저하될 수 있습니다. 또한, 이상치(일반적인 데이터 패턴에서 벗어나는 값)에 민감하게 작동할 수 있고 계산 비용이 크기 때문에 데이터셋의 크기와 차원, 이상치 등을 고려하여 적절히 사용해야 합니다.

마지막으로, KNN 회귀는 주변 이웃들의 값을 기반으로 예측을 수행하기 때문에 주변 이웃들의 분포가 중요합니다. 따라서 데이터가 밀집한 지역에는 좋은 예측을 할 수 있지만, 데이터가 희소하게 분포되어 있거나 경계 영역에 위치한 경우에는 예측이 불안정해질 수 있습니다. 

 

KNN의 회귀에는 단순 회귀와 가중 회귀가 있습니다.

kNN의 단순 회귀 (Simple kNN Regression)

kNN의 기본 회귀 알고리즘입니다. 주어진 입력 데이터의 k개의 최근접 이웃을 찾고, 이웃들의 출력 값의 평균 또는 중앙값을 사용하여 해당 입력에 대한 출력 값을 예측합니다. 이웃들의 값에 대해 가중치를 고려하지 않으며, 동일한 가중치를 적용합니다.

 

kNN의 가중 회귀 (Weighted kNN Regression)
가중 회귀는 kNN 회귀에 가중치를 도입하여 예측을 수행하는 방법입니다.주어진 입력 데이터의 k개의 최근접 이웃을 찾고, 이웃들의 출력 값에 가중치를 부여하여 가중 평균을 사용하여 해당 입력에 대한 출력 값을 예측합니다. 가중치는 일반적으로 거리에 반비례하여 할당됩니다. 가까운 이웃일수록 더 높은 가중치를 가지고, 먼 이웃일수록 더 낮은 가중치를 가집니다. 대표적인 가중치 함수로는 역거리 가중치(Inverse Distance Weighting, IDW) 등이 있습니다.

 

 

 

한번 코드를 통해 살펴볼까요 kNN 회귀를 이용한 영화 평점 예측하겠습니다. 영화평을 ‘긍정’과 ‘부정’으로 분류하는 것이 아닌, 실제 영화 등급(별점)을 예측합니다. 이 코드는 K-Nearest Neighbors 알고리즘의 회귀 버전인 KNeighborsRegressor를 사용하였고 가중회귀를 적용했습니다. 

from sklearn.neighbors import KNeighborsRegressor
#먼저, KNeighborsRegressor를 사용하기 위해 필요한 라이브러리를 불러옵니다.
import numpy as np

regressor = KNeighborsRegressor(n_neighbors=3, weights='distance') #.p=1 쓰면 맨허튼거리
#KNeighborsRegressor 객체를 생성하고 하이퍼파라미터를 설정합니다. (n_neighbors,weights)
#n_neighbors=3: 가장 가까운 3개의 이웃을 참조합니다.
#weights='distance': 이웃들의 거리에 따라 가중치를 부여합니다. 거리가 가까울수록 예측에 더 큰 영향을 미칩니다.
#기본값은 p=2이므로, 유클리디안 거리를 기본 거리 측정 기준으로 사용합니다 p=1 쓰면 맨허튼거리

X_train = [
           [0.5, 0.2, 0.1],
           [0.9, 0.7, 0.3],
           [0.4, 0.5, 0.7]
]
y_train = [5.0, 6.8, 9.0]
#학습 데이터를 정의합니다. X_train은 입력 특성을 포함하고 있으며, y_train은 해당 특성에 대한 목표 값(레이블)을 포함하고 있습니다.
regressor.fit(X_train, y_train)
#학습 데이터를 사용하여 KNeighborsRegressor 모델을 학습시킵니다. 
 
X_test = np.array([[0.2, 0.1, 0.7],[0.4, 0.7, 0.6],[0.5, 0.8, 0.1]])
#테스트 데이터는 모델의 성능을 평가하기 위해 사용됩니다.
 
y_pred = regressor.predict(X_test)
#학습된 모델을 사용하여 테스트 데이터에 대한 예측값을 계산합니다. predict() 함수는 테스트 데이터에 대한 예측값을 반환합니다.
 
y_pred
 
array([7.28143288, 7.76451922, 6.8457845 ])

 

 

단순 회귀 또한 살펴보겠습니다.

regressor = KNeighborsRegressor(n_neighbors=3) #Default: n_neighbors=5, weights='uniform'
#가중치(weights)는 기본값인 'uniform'으로 설정되어 있습니다. 'uniform'은 모든 이웃이 동일한 가중치!!!!! 를 가짐을 의미합니다.
X_train = [
           [0.5, 0.2, 0.1],
           [0.9, 0.7, 0.3],
           [0.4, 0.5, 0.7]
]
y_train = [5.0, 6.8, 9.0]
regressor.fit(X_train, y_train)

X_test = np.array([[0.2, 0.1, 0.7],[0.4, 0.7, 0.6],[0.5, 0.8, 0.1]])
y_pred = regressor.predict(X_test)
y_pred
 
array([6.93333333, 6.93333333, 6.93333333])

 

 

또 다른 실습 예제로 kNN 회귀를 이용한 농어의 무게 예측해볼까용? 농어의 길이와 무게 데이터 이용하겠습니다. 일단 농어의 길이와 무게를 입력해주겠습니다.

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

 

이걸 한번 산점도로 한번 살펴볼게요. matplotlib 라이브러리를 사용하여 데이터를 산점도(scatter plot)로 시각화하겠습ㄴ다.

import matplotlib.pyplot as plt

plt.scatter(perch_length, perch_weight, marker='o', s=50, edgecolor="k", linewidth=1)
plt.xlabel("Length")
plt.ylabel("Weight")
plt.show()

 

농어의 산점

 

sklearn의 train_test_split 함수를 사용하여 데이터를 학습용 데이터셋과 테스트용 데이터셋으로 나누겠습니다..

 
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(perch_length, perch_weight, random_state = 42)
#기본적으로 train_test_split 함수는 데이터를 75%의 학습 데이터와 25%의 테스트 데이터로 나눕니다.
 

 

Scikit-Learn에 사용할 훈련 셋은 2차원배열이어야 합니다. 각각의 배열은 샘플 수와 1개의 특성(열)을 가진 형태로 변경되어어야 는 scikit-learn 모델과 같은 머신러닝 모델에서 입력 데이터로 사용할 수 있습니다. 이 코드는 X_train과 X_test 배열을 scikit-learn 모델이 요구하는 2차원 형태로 변환하는 것입니다.

 
X_train = X_train.reshape(-1, 1) #reshape(-1, 1) 함수를 사용하면, 배열의 형태를 (샘플 수, 1)로 변경
X_test = X_test.reshape(-1, 1)
# 예를 들어변환 전 X_train의 형태가 (50,)인 1차원 배열이었다면, 변환 후의 X_train의 형태는 (50, 1)인 2차원 배열이 됩니다.
# 이는 50개의 샘플과 각 샘플에 대한 1개의 특성(여기서는 길이)을 포함하게 됩니다.

 

 

이제 K-최근접 이웃(kNN) 회귀 알고리즘을 사용하여 입력 데이터(X_train)에 대한 출력 데이터(y_train)를 학습하고, 테스트 데이터(X_test)에 대한 예측을 수행하겠습니다.

 
from sklearn.neighbors import KNeighborsRegressor
regressor = KNeighborsRegressor() #Default: n_neighbors=5, weights='uniform'
 
regressor.fit(X_train, y_train)
y_pred = regressor.predict(X_test)
y_pred
 
array([ 60. , 79.6, 248. , 122. , 136. , 847. , 311.4, 183.4, 847. , 113. , 1010. , 60. , 248. , 248. ])

 

 

산점도로 살펴볼까요 주황색이 X_test,y_pred 입니다.

plt.scatter(X_train, y_train, marker='o', s=50, edgecolor="k", linewidth=1)
plt.scatter(X_test, y_pred, marker='o', s=50, edgecolor="k", linewidth=1)
plt.xlabel("Length")
plt.ylabel("Weight")
plt.show()

test포함 산점도

 

 

 

오호호 이제는 학습된 K-최근접 이웃(KNN) 회귀 모델(regressor)을 사용하여 길이가 100인 새로운 데이터 포인트에 대한 결과를 예측해보겠습니다.

 
y_pred_1 = regressor.predict([[100]]) #수는 입력 데이터의 형태가 2차원 배열이어야 하므로, 입력 데이터를 [[100]]과 같이 2차원 배열로 표현합니다.
y_pred_1
 
array([1010.])
 

 

 

1010이 나왔네요 잘 와닿지 않으니 한번 시각화 또한 진행하겠습니다. x표시 길이가 100인 새로운데이터 입니다.

plt.scatter(X_train, y_train, marker='o', s=50, edgecolor="k", linewidth=1)
plt.scatter(X_test, y_pred, marker='o', s=50, edgecolor="k", linewidth=1)
plt.scatter([[100]], y_pred_1, marker='X', s=50, edgecolor="k", linewidth=1)
plt.xlabel("Length")
plt.ylabel("Weight")
plt.show()

 

흐음 역시 앞에 말씀드린것 처럼 kNN회귀는 학습데이터의 범위에서 많이 벗어나는 이상치는 정확한 예측이 어렵군요. 빠가!

 

다음 게시글에는 선형회귀에 대해 알려드리겠습니다!

'Machine Learning_모델설계_Python' 카테고리의 다른 글

선형회귀_2 단순선형회귀 실습  (0) 2023.05.16
선형회귀_1 단순선형회귀  (5) 2023.05.16
kNN_3 분류 실습(표준화, 결과분석)  (3) 2023.05.13
kNN_2 분류 실습  (3) 2023.05.10
kNN_1 분류  (2) 2023.05.09