Post

k-최근접 이웃 회귀와 머신러닝 데이터 준비 과정

k-최근접 이웃 회귀 알고리즘과 머신러닝 데이터 준비 과정을 다룹니다. 지도 학습의 기본 개념인 분류와 회귀의 차이점을 설명하고, k-최근접 이웃 알고리즘을 사용한 분류 및 회귀 예제를 제공합니다. 또한, 데이터 준비 과정에서 넘파이 배열 생성, 데이터 시각화, 데이터셋 분리, 차원 조정, 모델 훈련 및 평가 방법을 상세히 설명합니다.

k-최근접 이웃 회귀와 머신러닝 데이터 준비 과정

1. 지도 학습의 이해: 분류와 회귀

1.1 지도 학습이란?

지도 학습(Supervised Learning)은 머신러닝에서 입력 데이터(Input)와 이에 상응하는 정답(Label)을 사용해 모델을 학습시키는 방법입니다.
이 과정에서 모델은 주어진 입력 데이터와 정답 간의 관계를 파악하여, 새로운 데이터에 대해 올바른 예측을 수행할 수 있도록 학습합니다.

1.2 분류(Classification)

  • 정의: 주어진 입력 데이터를 미리 정의된 카테고리(클래스) 중 하나로 분류하는 문제입니다.
    예를 들어, 스팸 이메일 필터링에서는 이메일이 ‘스팸’ 또는 ‘정상’ 중 하나로 분류됩니다.

  • 특징:
    • 출력 값(타깃)은 이산적(Discrete)입니다.
    • 모델은 각 입력 데이터가 특정 클래스에 속할 확률을 계산하며, 확률이 가장 높은 클래스를 선택합니다.
  • 예시:
    1. 스팸 메일 분류 (스팸/정상)
    2. 손글씨 숫자 이미지 분류 (0~9)
    3. 질병 진단 (양성/음성)
코드 예제: 분류 문제 데이터의 구조
1
2
3
# 간단한 데이터 예시: 이메일 분류
emails = ["Win a free iPhone!", "Meeting at 10AM", "Buy cheap pills now!"]
labels = [1, 0, 1]  # 1: 스팸, 0: 정상

1.3 회귀(Regression)

  • 정의: 주어진 입력 데이터를 사용하여 연속적(Continuous)인 값을 예측하는 문제입니다.
    예를 들어, 집 값 예측에서는 특정 입력 데이터(예: 위치, 면적)에 기반해 예상 집 값을 예측합니다.

  • 특징:
    • 출력 값(타깃)은 연속적(Continuous)입니다.
    • 모델은 숫자 데이터의 패턴을 학습하여, 입력 데이터에 대한 실수형 결과를 반환합니다.
  • 예시:
    1. 내년도 경제 성장률 예측 (예: 3.2%)
    2. 배달 도착 시간 예측 (예: 25분)
    3. 특정 제품의 판매량 예측 (예: 150개)
코드 예제: 회귀 문제 데이터의 구조
1
2
3
# 간단한 데이터 예시: 배달 도착 시간 예측
features = [[2.5], [3.0], [4.2]]  # 배달 거리가 입력
labels = [15, 18, 25]  # 예측하려는 배달 시간(분)

1.4 분류와 회귀의 차이점

특징분류(Classification)회귀(Regression)
출력 값이산적(범주형) 값연속적(숫자형) 값
목표특정 클래스에 속하는지 예측입력 데이터로 임의의 수치 예측
예시스팸/정상 이메일 분류내일의 온도 예측
평가 지표정확도(Accuracy), F1 Score결정계수(\(R^2\)), 평균 절대 오차(MAE)
추가 팁:
  • 날씨 예측 문제:
    • 내일 “비가 올지 여부”를 예측한다면 → 분류 문제
    • 내일 “비가 올 확률”을 예측한다면 → 회귀 문제

1.5 추가 설명: 분류와 회귀를 혼동하기 쉬운 사례

  1. 문제 정의의 중요성:
    같은 데이터를 사용하더라도 문제의 정의에 따라 분류 문제 또는 회귀 문제가 될 수 있습니다.
    • 예를 들어, “학생 점수가 80점을 넘을 확률은?” → 회귀
    • “학생 점수가 80점을 넘을까?” → 분류
  2. 데이터 시각화의 도움:
    데이터를 시각화하면 문제의 본질을 더 쉽게 이해할 수 있습니다.
    • 군집처럼 보이는 데이터는 분류 문제일 가능성이 높음.
    • 연속된 패턴을 보이는 데이터는 회귀 문제일 가능성이 큼.
코드 예제: 데이터 시각화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import matplotlib.pyplot as plt
import numpy as np

# 분류 데이터 예시
x = np.random.randn(100)
y = np.random.choice([0, 1], size=100)  # 이산적 출력
plt.scatter(x, y)
plt.title("분류 데이터 예시")
plt.show()

# 회귀 데이터 예시
x = np.linspace(0, 10, 100)
y = x + np.random.normal(0, 0.5, 100)  # 연속적 출력
plt.scatter(x, y)
plt.title("회귀 데이터 예시")
plt.show()

분류 데이터 예시 회귀 데이터 예시

2. k-최근접 이웃 알고리즘

2.1 k-최근접 이웃 분류

k-최근접 이웃(k-Nearest Neighbors, KNN)은 가장 간단하면서도 직관적인 지도 학습 알고리즘 중 하나입니다.
분류 문제에서 KNN은 다음과 같은 방식으로 작동합니다:

  1. 예측하려는 데이터(샘플)가 주어졌을 때, 훈련 데이터에서 이 샘플과 가장 가까운 k개의 이웃 샘플을 선택합니다.
  2. 선택된 이웃 샘플들이 속한 클래스를 확인합니다.
  3. 다수결 투표를 통해 이웃 중 가장 많은 클래스를 예측 값으로 설정합니다.
예시: KNN을 이용한 분류
  • 입력 샘플의 좌표가 \((2, 3)\)이라고 가정.
  • \(k=3\)일 때, 이 샘플에 가장 가까운 3개의 훈련 샘플이 선택됩니다.
  • 이 3개의 샘플 중 2개가 클래스 A, 1개가 클래스 B에 속한다고 하면, 최종 예측 값은 클래스 A입니다.
코드 예제: KNN 분류
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
import numpy as np

# 데이터 준비
X = np.array([[1, 2], [2, 3], [3, 1], [6, 5], [7, 8], [8, 6]])
y = np.array([0, 0, 0, 1, 1, 1])  # 클래스 0 또는 1

# 훈련 세트와 테스트 세트 분리
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

# KNN 분류 모델 생성
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train, y_train)

# 예측
print("테스트 세트 예측:", knn.predict(X_test))
print("모델 정확도:", knn.score(X_test, y_test))
KNN 분류의 특징
  • 장점:
    • 이해하기 쉽고 구현이 간단합니다.
    • 데이터의 분포에 대한 사전 가정이 필요하지 않습니다.
  • 단점:
    • 데이터가 많아질수록 거리 계산에 시간이 오래 걸릴 수 있습니다.
    • \(k\) 값에 따라 모델의 성능이 크게 좌우됩니다.

2.2 k-최근접 이웃 회귀

KNN은 분류뿐 아니라 회귀 문제에도 적용할 수 있습니다.
회귀 문제에서 KNN은 다음과 같은 방식으로 작동합니다:

  1. 예측하려는 데이터(샘플)에 대해 훈련 데이터에서 가장 가까운 k개의 이웃 샘플을 선택합니다.
  2. 이웃 샘플의 타깃 값(연속형 값)의 평균을 계산하여 새로운 샘플의 예측 값으로 설정합니다.
예시: KNN을 이용한 회귀
  • 입력 샘플의 좌표가 \((3, 4)\)라고 가정.
  • \(k=3\)일 때, 이 샘플에 가장 가까운 3개의 훈련 샘플의 타깃 값이 각각 10, 20, 30이라면,
    예측 값은 \((10 + 20 + 30) / 3 = 20\)입니다.
코드 예제: KNN 회귀
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from sklearn.neighbors import KNeighborsRegressor
from sklearn.model_selection import train_test_split
import numpy as np

# 데이터 준비
X = np.array([[1], [2], [3], [4], [5]])
y = np.array([10, 20, 30, 40, 50])  # 연속형 타깃 값

# 훈련 세트와 테스트 세트 분리
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

# KNN 회귀 모델 생성
knr = KNeighborsRegressor(n_neighbors=2)
knr.fit(X_train, y_train)

# 예측
print("테스트 세트 예측:", knr.predict(X_test))
print("모델 결정계수 R^2:", knr.score(X_test, y_test))
KNN 회귀의 특징
  • 장점:
    • 간단하고 구현이 쉬우며, 데이터의 분포에 대한 가정이 필요 없습니다.
    • 다양한 비선형 데이터에도 적합합니다.
  • 단점:
    • \(k\) 값 선택에 따라 성능이 크게 달라질 수 있습니다.
    • 데이터가 많아지면 계산량이 증가하여 속도가 느려질 수 있습니다.

3. 데이터 준비 과정

3.1 넘파이 배열 생성

K-최근접 이웃(KNN) 알고리즘을 사용하려면 데이터를 준비해야 합니다. 이번 예제에서는 물고기의 길이와 무게 데이터를 활용합니다.
먼저, 데이터를 numpy 배열로 생성합니다.

코드 예제: 넘파이 배열 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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])

3.2 산점도를 이용한 데이터 시각화

데이터를 시각화하면 데이터의 분포와 패턴을 쉽게 이해할 수 있습니다.
예를 들어, 물고기 길이(perch_length)와 무게(perch_weight) 간의 관계를 산점도로 확인합니다.

코드 예제: 데이터 시각화
1
2
3
4
5
6
7
8
import matplotlib.pyplot as plt

# 산점도 그리기
plt.scatter(perch_length, perch_weight)
plt.xlabel('Length')  # X축 라벨
plt.ylabel('Weight')  # Y축 라벨
plt.title('Length vs Weight of Fish')  # 그래프 제목
plt.show()

산점도

그래프 해석:
  • 그래프를 통해 물고기 길이와 무게 간의 양의 상관관계를 확인할 수 있습니다.
    즉, 길이가 길어질수록 무게가 증가하는 경향이 있습니다.

3.3 데이터셋 분리: 훈련 세트와 테스트 세트

모델 학습에는 데이터를 훈련 세트테스트 세트로 나누는 것이 중요합니다.
훈련 세트는 모델을 학습하는 데 사용되고, 테스트 세트는 학습된 모델의 성능을 평가하는 데 사용됩니다.

코드 예제: 데이터 분리
1
2
3
4
5
6
7
8
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)

# 분리된 데이터 크기 출력
print("훈련 세트 크기:", train_input.shape, train_target.shape)
print("테스트 세트 크기:", test_input.shape, test_target.shape)

4. 데이터의 차원 조정

4.1 넘파이 reshape() 메서드의 이해와 활용

K-최근접 이웃(KNN) 알고리즘을 사용하려면 입력 데이터가 2차원 배열 형태여야 합니다.
현재 데이터는 1차원 배열로 되어 있으므로 이를 2차원 배열로 변환해야 합니다.

numpyreshape() 메서드를 사용하면 배열의 차원을 쉽게 변환할 수 있습니다.

코드 예제: reshape() 메서드 사용
1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np

# 예제 배열 생성
test_array = np.array([1, 2, 3, 4])
print("원본 배열:", test_array)
print("원본 배열의 크기:", test_array.shape)

# (2, 2) 크기로 변환
reshaped_array = test_array.reshape(2, 2)
print("변환된 배열:")
print(reshaped_array)
print("변환된 배열의 크기:", reshaped_array.shape)
출력 결과:
1
2
3
4
5
6
원본 배열: [1 2 3 4]
원본 배열의 크기: (4,)
변환된 배열:
[[1 2]
 [3 4]]
변환된 배열의 크기: (2, 2)
주의사항:
  1. reshape() 메서드로 배열의 크기를 바꿀 때, 지정한 크기의 원소 수가 원본 배열의 원소 수와 동일해야 합니다.
    • 예를 들어, 4개의 원소를 가진 배열을 (2, 3) 크기로 바꾸려고 하면 오류가 발생합니다.
  2. 배열의 크기를 자동으로 지정하려면 -1을 사용할 수 있습니다.
    예를 들어, (원소 수, 1) 형태로 변환하려면 array.reshape(-1, 1)을 사용합니다.

4.2 데이터셋을 2차원 배열로 변환

이제, 훈련 세트(train_input)와 테스트 세트(test_input)를 2차원 배열로 변환해봅니다.

코드 예제: 데이터 변환
1
2
3
4
5
6
7
# 훈련 세트와 테스트 세트를 2차원 배열로 변환
train_input = train_input.reshape(-1, 1)
test_input = test_input.reshape(-1, 1)

# 변환된 배열 크기 출력
print("변환된 훈련 세트 크기:", train_input.shape)
print("변환된 테스트 세트 크기:", test_input.shape)
출력 결과:
1
2
변환된 훈련 세트 크기: (42, 1)
변환된 테스트 세트 크기: (14, 1)
해설:
  • (42, 1)은 42개의 샘플이 각각 1개의 특성을 가지고 있음을 의미합니다.
  • KNN 알고리즘이 데이터를 올바르게 처리할 수 있도록 데이터 형식이 준비되었습니다.

5. k-최근접 이웃 회귀 모델 훈련

5.1 KNeighborsRegressor로 회귀 모델 생성 및 훈련

K-최근접 이웃(KNN) 회귀 모델은 scikit-learnKNeighborsRegressor 클래스를 사용해 구현할 수 있습니다.
이 클래스는 KNN 알고리즘을 사용해 회귀 문제를 해결합니다.

모델 훈련 과정:
  1. KNeighborsRegressor 객체를 생성합니다.
  2. fit() 메서드를 사용해 훈련 세트로 모델을 학습시킵니다.
코드 예제: 모델 훈련
1
2
3
4
5
6
7
8
from sklearn.neighbors import KNeighborsRegressor

# KNN 회귀 모델 생성
knr = KNeighborsRegressor(n_neighbors=3)  # 이웃의 개수를 3으로 설정
knr.fit(train_input, train_target)  # 모델 훈련

# 훈련 결과 출력
print("모델이 훈련되었습니다.")

5.2 결정계수 \(R^2\): 모델 평가 지표

훈련된 모델을 평가하기 위해 결정계수 \(R^2\)를 사용합니다.
\(R^2\)는 모델이 데이터를 얼마나 잘 설명하는지 나타내며, 값이 1에 가까울수록 성능이 좋습니다.

\(R^2\)의 정의:

\[ R^2 = 1 - \frac{(타깃 - 예측)^2의 합}{(타깃 - 평균)^2의 합} \]

  • \(R^2 = 1\): 완벽한 예측
  • \(R^2 = 0\): 타깃의 평균값으로만 예측한 경우와 동일한 성능
  • \(R^2 < 0\): 평균값보다도 나쁜 예측
코드 예제: 테스트 세트 평가
1
2
3
# 테스트 세트에서 모델 평가
score = knr.score(test_input, test_target)
print("테스트 세트 결정계수 R^2:", score)
결과 해석:
  • \(R^2) 값이 1에 가까울수록 테스트 세트에서의 모델 성능이 뛰어남을 의미합니다.
  • 값이 낮거나 음수라면 모델이 충분히 학습되지 않았거나 데이터와 잘 맞지 않음을 나타냅니다.

추가 코드: 샘플 예측

모델을 사용해 특정 샘플의 예측값을 확인할 수 있습니다.

1
2
3
4
# 샘플 데이터 예측
sample_data = [[25.0]]  # 길이가 25.0cm인 물고기의 예측
prediction = knr.predict(sample_data)
print("예측 무게:", prediction[0])
출력 예시:
1
예측 무게: 150.0

이 예제에서는 길이 25.0cm의 물고기에 대해 모델이 무게를 150.0g으로 예측합니다.

5.3 모델 평가

모델이 훈련되고 테스트 세트에서 평가된 이후, 추가로 평균 절댓값 오차(MAE)와 같은 세부적인 지표를 사용할 수 있습니다.

6. 평균 절댓값 오차 계산

6.1 mean_absolute_error()로 예측 오차 측정

테스트 세트의 예측값과 실제 타깃값 간의 차이를 구해 모델의 성능을 더 구체적으로 평가할 수 있습니다.
평균 절댓값 오차(MAE)는 타깃과 예측값의 절댓값 차이를 평균한 값으로, 모델이 얼마나 정확히 예측했는지를 나타냅니다.

MAE 계산 방법

\[ \text{MAE} = \frac{\sum_{i=1}^n |y_i - \hat{y}_i|}{n} \]

  • \(y_i\): 실제 타깃값
  • \(\hat{y}_i\): 예측값
  • \(n\): 샘플의 개수

MAE는 작을수록 모델이 더 정확하게 예측하고 있음을 의미합니다.

코드 예제: MAE 계산
1
2
3
4
5
6
7
8
from sklearn.metrics import mean_absolute_error

# 테스트 세트 예측
test_prediction = knr.predict(test_input)

# 평균 절댓값 오차 계산
mae = mean_absolute_error(test_target, test_prediction)
print("평균 절댓값 오차(MAE):", mae)
출력 예시:
1
평균 절댓값 오차(MAE): 19.157142857142862
해석:
  • 출력된 MAE 값이 19.15라는 것은, 예측값이 평균적으로 약 19g 정도 타깃값에서 벗어난다는 의미입니다.

6.2 실제 예측과 타깃값의 차이 분석

예측값과 타깃값의 차이를 직접 비교하면 모델이 어느 정도로 정확한지 더 직관적으로 이해할 수 있습니다.

코드 예제: 예측값과 타깃값 비교
1
2
3
# 예측값과 실제 타깃값 출력
for i in range(len(test_prediction)):
    print(f"예측값: {test_prediction[i]:.2f}, 실제값: {test_target[i]}")
출력 예시:
1
2
3
예측값: 115.00, 실제값: 120.00
예측값: 130.00, 실제값: 125.00
예측값: 170.00, 실제값: 160.00
해석:
  • 예측값이 타깃값과 얼마나 근접한지를 직접 확인할 수 있습니다.
  • 일부 샘플에서 예측값과 실제값 간의 큰 차이가 있다면, 모델을 개선하거나 데이터를 추가로 분석할 필요가 있습니다.

7. 모델 평가 및 과대적합과 과소적합

7.1 훈련 세트와 테스트 세트의 평가

모델의 성능을 제대로 이해하려면 훈련 세트테스트 세트의 평가를 모두 확인해야 합니다.
훈련 세트에서의 성능은 모델이 학습 데이터에 얼마나 잘 맞는지를 나타내며, 테스트 세트의 성능은 모델의 일반화 성능(새로운 데이터에 대한 성능)을 평가합니다.

코드 예제: 훈련 세트와 테스트 세트 평가
1
2
3
4
5
6
# 훈련 세트와 테스트 세트 평가
train_score = knr.score(train_input, train_target)
test_score = knr.score(test_input, test_target)

print("훈련 세트 결정계수 R^2:", train_score)
print("테스트 세트 결정계수 R^2:", test_score)
출력 예시:
1
2
훈련 세트 결정계수 R^2: 0.9804899950518966
테스트 세트 결정계수 R^2: 0.9746459963987609
해석:
  • 훈련 세트 \(R^2\): 0.98
    모델이 훈련 데이터의 패턴을 매우 잘 학습했다는 의미입니다.
  • 테스트 세트 \(R^2\): 0.97
    모델이 새로운 데이터에서도 높은 성능을 보임을 나타냅니다.

7.2 과대적합과 과소적합의 정의와 해결 방법

과대적합 (Overfitting)

정의:

  • 모델이 훈련 데이터에 지나치게 적합해져, 새로운 데이터(테스트 세트)에는 성능이 떨어지는 상태입니다.

특징:

  • 훈련 세트 \(R^2\): 매우 높음.
  • 테스트 세트 \(R^2\): 낮음.

해결 방법:

  1. 모델 복잡도 줄이기 (\(k\) 값 늘리기).
  2. 데이터 증강 또는 더 많은 데이터 확보.
  3. 정규화(Regularization) 기법 사용.
과소적합 (Underfitting)

정의:

  • 모델이 훈련 데이터의 패턴조차 제대로 학습하지 못한 상태입니다.

특징:

  • 훈련 세트와 테스트 세트 모두에서 \(R^2\) 값이 낮음.

해결 방법:

  1. 모델 복잡도 늘리기 (\(k\) 값 줄이기).
  2. 충분한 학습 시간 제공.
  3. 데이터 전처리 개선 또는 더 많은 특성 추가.
코드 예제: 과대적합 및 과소적합 확인
1
2
3
4
5
6
7
8
9
10
11
12
13
# 이웃의 개수를 줄여 모델 복잡도 증가
knr.n_neighbors = 1  # 이웃의 개수를 1로 설정 (복잡한 모델)
knr.fit(train_input, train_target)

print("훈련 세트 결정계수 R^2 (과대적합):", knr.score(train_input, train_target))
print("테스트 세트 결정계수 R^2 (과대적합):", knr.score(test_input, test_target))

# 이웃의 개수를 늘려 모델 단순화
knr.n_neighbors = 10  # 이웃의 개수를 10으로 설정 (단순한 모델)
knr.fit(train_input, train_target)

print("훈련 세트 결정계수 R^2 (과소적합):", knr.score(train_input, train_target))
print("테스트 세트 결정계수 R^2 (과소적합):", knr.score(test_input, test_target))
출력 예시:
1
2
3
4
훈련 세트 결정계수 R^2 (과대적합): 1.0
테스트 세트 결정계수 R^2 (과대적합): 0.84
훈련 세트 결정계수 R^2 (과소적합): 0.89
테스트 세트 결정계수 R^2 (과소적합): 0.85
해석:
  • 과대적합 상태: \(k=1\)일 때, 훈련 세트에서 완벽한 성능(\(R^2 = 1.0\))을 보이지만, 테스트 세트 성능이 낮음.
  • 과소적합 상태: \(k=10\)일 때, 모델이 훈련 세트에서도 낮은 성능을 보임.
팁: 적절한 모델 복잡도 찾기
  • \(k\) 값을 조정하며 훈련 세트와 테스트 세트의 성능이 균형을 이루는 지점을 찾아야 합니다.
  • \(k\) 값이 너무 작으면 과대적합, 너무 크면 과소적합 가능성이 높습니다.

8. k-최근접 이웃 모델의 복잡도 조정

8.1 이웃의 개수 (\(k\)) 조정으로 모델 성능 개선

K-최근접 이웃(KNN) 알고리즘에서 이웃의 개수 (\(k\))는 모델의 복잡도에 직접적으로 영향을 미칩니다.

  • 작은 \(k\): 모델이 훈련 데이터의 국지적 패턴을 더 잘 학습하지만, 과대적합(Overfitting) 위험이 있습니다.
  • 큰 \(k\): 모델이 데이터 전반적인 패턴을 따르며, 과소적합(Underfitting) 위험이 있습니다.
코드 예제: \(k\) 값 조정
1
2
3
4
5
6
7
8
9
10
# 이웃의 개수를 줄여 모델 복잡도 증가
knr.n_neighbors = 3  # 기본값: 5에서 3으로 줄임
knr.fit(train_input, train_target)

# 모델 평가
train_score = knr.score(train_input, train_target)
test_score = knr.score(test_input, test_target)

print("훈련 세트 결정계수 R^2 (k=3):", train_score)
print("테스트 세트 결정계수 R^2 (k=3):", test_score)
출력 예시:
1
2
훈련 세트 결정계수 R^2 (k=3): 0.9804899950518966
테스트 세트 결정계수 R^2 (k=3): 0.9746459963987609
해석:
  • 훈련 세트와 테스트 세트 모두에서 성능이 균형을 이루고 있어, \(k=3\)이 적절한 값임을 알 수 있습니다.

8.2 \(k\) 값 변화에 따른 과대적합과 과소적합 해결 사례

\(k\) 값을 작게 하거나 크게 변경해 모델 성능의 변화를 관찰해봅니다.

코드 예제: 다양한 \(k\) 값 실험
1
2
3
4
5
6
7
# 여러 k 값에 대해 모델 성능 평가
for k in range(1, 11):  # k=1부터 10까지 테스트
    knr.n_neighbors = k
    knr.fit(train_input, train_target)
    train_score = knr.score(train_input, train_target)
    test_score = knr.score(test_input, test_target)
    print(f"k={k}, 훈련 세트 R^2: {train_score:.4f}, 테스트 세트 R^2: {test_score:.4f}")
출력 예시:
1
2
3
4
5
6
k=1, 훈련 세트 R^2: 1.0000, 테스트 세트 R^2: 0.8489
k=2, 훈련 세트 R^2: 0.9912, 테스트 세트 R^2: 0.9254
k=3, 훈련 세트 R^2: 0.9805, 테스트 세트 R^2: 0.9746
k=4, 훈련 세트 R^2: 0.9723, 테스트 세트 R^2: 0.9623
k=5, 훈련 세트 R^2: 0.9598, 테스트 세트 R^2: 0.9345
...
해석:
  • \(k=1\): 훈련 세트에서는 \(R^2=1.0\)로 완벽한 예측을 보이지만, 테스트 세트에서 성능이 떨어지는 과대적합 상태.
  • \(k=3\): 훈련 세트와 테스트 세트 성능이 균형을 이루며 적절한 복잡도를 보여줌.
  • \(k=10\): 훈련 세트와 테스트 세트 모두에서 성능이 낮아지는 과소적합 상태.
팁: 적절한 \(k\) 찾기
  • 훈련 세트와 테스트 세트의 \(R^2\) 차이가 최소화되는 \(k\) 값을 선택해야 합니다.
  • \(k\) 값을 증가시키며 성능의 변화를 시각화하는 것도 좋은 방법입니다.

성능 시각화

코드 예제: \(k\) 값에 따른 성능 그래프
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import matplotlib.pyplot as plt

train_scores = []
test_scores = []

# 여러 k 값에 대해 성능 기록
for k in range(1, 11):
    knr.n_neighbors = k
    knr.fit(train_input, train_target)
    train_scores.append(knr.score(train_input, train_target))
    test_scores.append(knr.score(test_input, test_target))

# 그래프 그리기
plt.plot(range(1, 11), train_scores, label='훈련 세트 R^2')
plt.plot(range(1, 11), test_scores, label='테스트 세트 R^2')
plt.xlabel('이웃의 개수 (k)')
plt.ylabel('R^2')
plt.title('k 값에 따른 모델 성능')
plt.legend()
plt.show()
그래프 해석:
  • \(k\) 값이 작을수록 훈련 세트 성능은 높지만 테스트 세트 성능이 떨어지는 과대적합 경향이 보입니다.
  • \(k\) 값이 클수록 테스트 세트 성능은 안정적이지만 훈련 세트 성능이 낮아지는 과소적합 경향이 보입니다.

9. 핵심 요약

9.1 회귀 문제의 정의와 k-최근접 이웃 회귀의 특징

  • 회귀 문제의 정의:
    • 출력값이 연속적(숫자형)인 값을 예측하는 문제.
    • 예: 내년도 경제 성장률 예측, 특정 물체의 무게 예측.
  • k-최근접 이웃 회귀의 특징:
    • \(k\)-최근접 이웃 알고리즘은 회귀 문제를 해결할 때, 주어진 샘플의 가장 가까운 \(k\)개의 이웃 타깃값의 평균을 사용하여 예측합니다.
    • \(k\) 값은 모델의 복잡도를 결정하며, 적절한 값을 찾는 것이 중요합니다.

9.2 결정계수 (\(R^2\))의 의미와 중요성

  • 결정계수 \(R^2\):
    • 모델이 데이터를 얼마나 잘 설명하는지를 나타내는 성능 지표.
    • 값의 범위:
      • \(R^2 = 1\): 완벽한 예측.
      • \(R^2 = 0\): 타깃의 평균값으로만 예측하는 경우와 동일한 성능.
      • \(R^2 < 0\): 평균값보다도 예측 성능이 낮은 경우.
    • \(R^2\) 값이 높을수록 모델의 예측 성능이 좋음을 나타냅니다.
  • 활용:
    • \(R^2\)는 테스트 세트 성능을 평가하여 모델이 새로운 데이터에 대해 얼마나 잘 일반화되었는지 확인하는 데 유용합니다.

9.3 과대적합과 과소적합의 구분 및 해결 방안

  • 과대적합:
    • 모델이 훈련 데이터에 지나치게 맞춰져 새로운 데이터에 성능이 떨어지는 상태.
    • 해결 방법:
      1. 모델의 복잡도를 줄임 (\(k\) 값을 늘림).
      2. 데이터 증강 또는 더 많은 데이터 확보.
  • 과소적합:
    • 모델이 훈련 데이터의 패턴조차 충분히 학습하지 못한 상태.
    • 해결 방법:
      1. 모델의 복잡도를 늘림 (\(k\) 값을 줄임).
      2. 충분한 학습 시간 제공.
      3. 추가 특성을 생성하거나 데이터 전처리 개선.

요약된 학습 흐름

  1. 지도 학습 개념:
    • 분류(Classification): 이산적인 클래스 예측.
    • 회귀(Regression): 연속적인 값 예측.
  2. 데이터 준비:
    • 넘파이 배열로 데이터 생성.
    • 산점도를 통해 데이터의 분포 시각화.
    • 훈련 세트와 테스트 세트로 데이터 분리.
  3. 모델 학습 및 평가:
    • KNeighborsRegressor를 사용해 회귀 모델 학습.
    • 결정계수 \(R^2\)로 모델 성능 평가.
    • 평균 절댓값 오차(MAE)로 예측 오차 확인.
  4. 모델 조정:
    • \(k\) 값을 조정하여 과대적합과 과소적합 문제 해결.
    • 훈련 세트와 테스트 세트 성능이 균형을 이루는 적절한 \(k\) 값 선택.

10. 필수 패키지와 함수 요약

10.1 scikit-learn 주요 클래스와 함수

  • KNeighborsRegressor:
    • K-최근접 이웃(KNN) 알고리즘을 사용한 회귀 모델 생성 클래스.
    • 주요 매개변수:
      • n_neighbors: 이웃의 개수를 지정 (기본값: 5).
    • 주요 메서드:
      • fit(X, y): 훈련 세트로 모델 학습.
      • predict(X): 새로운 샘플의 타깃값 예측.
      • score(X, y): 결정계수((R^2))를 사용해 모델 성능 평가.
예제:
1
2
3
4
5
6
from sklearn.neighbors import KNeighborsRegressor

knr = KNeighborsRegressor(n_neighbors=3)  # 이웃 개수 3으로 설정
knr.fit(train_input, train_target)       # 모델 학습
predictions = knr.predict(test_input)   # 예측
score = knr.score(test_input, test_target)  # 모델 평가
  • train_test_split:
    • 데이터를 훈련 세트와 테스트 세트로 랜덤하게 분리하는 함수.
    • 주요 매개변수:
      • test_size: 테스트 세트 비율 (기본값: 0.25).
      • random_state: 랜덤 시드 설정 (재현 가능성 보장).
예제:
1
2
3
4
5
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
)
  • mean_absolute_error:
    • 평균 절댓값 오차(MAE)를 계산하는 함수로, 모델의 예측 오차를 측정.
    • 매개변수:
      • y_true: 실제 타깃값.
      • y_pred: 예측값.
예제:
1
2
3
4
from sklearn.metrics import mean_absolute_error

mae = mean_absolute_error(test_target, knr.predict(test_input))
print("평균 절댓값 오차(MAE):", mae)

10.2 numpy의 reshape() 메서드와 활용법

  • reshape():
    • 배열의 크기를 변경하는 메서드.
    • 변경된 배열의 총 원소 개수는 원본 배열과 동일해야 함.
    • -1을 사용하면 크기를 자동으로 계산.
예제:
1
2
3
4
5
import numpy as np

arr = np.array([1, 2, 3, 4])
reshaped_arr = arr.reshape(-1, 1)  # 2차원 배열로 변환
print(reshaped_arr)
출력 결과:
1
2
3
4
[[1]
 [2]
 [3]
 [4]]

10.3 matplotlib를 활용한 데이터 시각화

  • scatter(x, y):
    • 산점도를 그려 데이터의 분포를 시각화.
  • title():
    • 그래프 제목 설정.
  • xlabel() / ylabel():
    • 그래프의 축 라벨 설정.
예제:
1
2
3
4
5
6
7
import matplotlib.pyplot as plt

plt.scatter(perch_length, perch_weight)
plt.xlabel('Length')
plt.ylabel('Weight')
plt.title('Length vs Weight of Fish')
plt.show()
This post is licensed under CC BY 4.0 by the author.